| < Free Open Study > |
26.5. Routines
One of the most powerful tools in code tuning is a good routine decomposition. Small,
Cross-Reference For details on working with routines, see Chapter 7, "High-Quality Routines." Rewrite Routines Inline
In the early days of computer programming, some machines imposed prohibitive performance penalties for calling a routine. A call to a routine
Modern computers collect a far smaller toll for calling a routine. Here are the results of
In some cases, you might be able to save a few nanoseconds by putting the code from a routine into the program directly where it's needed via a language feature like C++'s inline keyword. If you're working in a language that doesn't support inline directly but that does have a macro preprocessor, you can use a macro to put the code in, switching it in and out as needed. But modern machines—and "modern" means any machine you're ever likely to work on—impose virtually no penalty for calling a routine. As the example shows, you're as likely to degrade performance by keeping code inline as to optimize it. |
| < Free Open Study > |
| < Free Open Study > |
26.6. Recoding in a Low-Level Language
One long-standing piece of conventional wisdom that shouldn't be left unmentioned is the advice that when you run into a performance bottleneck, you should recode in a low-level language. If you're coding in C++, the low-level language might be assembler. If you're coding in Python, the low-level language might be C. Recoding in a lowlevel language tends to improve both speed and code
Whether you follow this well-beaten
Suppose you have a routine that converts binary data to uppercase ASCII
Delphi Example of Code That's Better Suited to Assembler
procedure HexExpand(var source: ByteArray;
var target: WordArray;
byteCount: word);
var
index: integer;
lowerByte: byte;
upperByte: byte;
targetIndex: integer;
begin
targetIndex := 1;
for index := 1 to byteCount do begin
target[ targetIndex ] := ((source[ index ] and $F0) shr 4) + ;
target[ targetIndex+1 ] := (source[ index ] and
Although it's hard to see where the fat is in this code, it contains a lot of bit manipulation, which isn't exactly Delphi's forte. Bit manipulation is assembler's forte, however, so this code is a good candidate for recoding. Here's the assembler code: Example of a Routine Recoded in Assembler
procedure HexExpand(var source;
var target;
byteCount : Integer);
label
EXPAND;
asm
MOV ECX,byteCount // load number of bytes to expand
MOV ESI,source // source offset
MOV EDI,target // target offset
XOR EAX,EAX // zero out array offset
EXPAND:
MOV EBX,EAX // array offset
MOV DL,[ESI+EBX] // get source byte
MOV DH,DL // copy source byte
AND DH,$F // get msbs
ADD DH, // add 65 to make upper case
SHR DL,4 // move lsbs into position
AND DL,$F // get lsbs
ADD DL, // add 65 to make upper case
SHL BX,1 // double offset for target array offset
MOV [EDI+EBX],DX // put target word
INC EAX // increment array offset
LOOP EXPAND // repeat until finished
end;
Rewriting in assembler in this case was profitable, resulting in a time savings of 41 percent. It's logical to assume that code in a language that's more suited to bit manipulation C++, for instancewould have less to gain than Delphi code would. Here are the results:
The "before" picture in these measurements reflects the two languages' strengths at bit manipulation. The "after" picture looks virtually identical, and it appears that the assembler code has minimized the initial performance differences between Delphi and C++. The assembler routine shows that rewriting in assembler doesn't have to produce a huge, ugly routine. Such routines are often quite modest, as this one is. Sometimes assembler code is almost as compact as its high-level-language equivalent. A relatively easy and effective strategy for recoding in assembler is to start with a compiler that generates assembler listings as a byproduct of compilation. Extract the assembler code for the routine you need to tune, and save it in a separate source file. Using the compiler's assembler code as a base, hand-optimize the code, checking for correctness and measuring improvements at each step. Some compilers intersperse the high-level-language statements as comments in the assembler code. If yours does, you might keep them in the assembler code as documentation. cc2e.com/2672
|
| < Free Open Study > |