
                ķ    ķ ķ  ķ  ķ ķ
           Ķ Ĵ  \/      з            ķ
                Ľ    Ľ      Ľ  Ľ Ľ
     
                                 presents ...

                   ķ ķ ķ    ķ   ķ  ķ ķ
                           \           Ľ ķ
                   Ľ Ľ Ľ    Ľ            Ľ

                                   Prt ][

                               
                                ķ
                        }{ .\\ I) 
                                Ľ

    Here's come annother HMD coding-tips-file ...
This file is dedicated to all the demo-scene real coders.
We don't consider ourselve as the Scenes's most up-to-date coders (NOT!),
but we feel that our own experiences and conclusions may help some new coders,
such as the ex-Amiga dudes, and all kinds of beginners.


     Here we go ... and here are the subjects :

                                                     
          
            1/ The truth on flow dependency         (486)   
                                                            
           2/ Think "Pentium" - A "RISC" approach  (586)   
                                                           
          3/ The legend of Code alignement        (486+)  
                                                           
           4/ Mixing 16bit and 32bit code          (386+)  
                                                            
            5/ unordered tips ...                   (386+)  
          
       
       
       
                Good reading.
                        Black Label  of  Hemoroids Pc


        1/ THE TRUTH ON FLOW DEPENDENCY

    Two years ago, someone told me that the use of a register recently
write-accessed (i.e during the last instruction) caused the 486 to have a one-
cycle penalty. But in most cases this isn't thrue. In fact, this penalty
appears only in a few cases.

    - the use of a 16bit register after a 8bit register write-back :
        MOV BL, DH
        MOV AL, [BX]
            or
        MOV AL, 1
        INC AX
( this is not valid for 16bit/32bit access )

    - two or more write-accesses in a row :
        This code :

            mov ds:[0], al
            inc cx
            inc cx
            inc cx
            mov ds:[0], al

        is exactly as fast as :

            mov ds:[0], al
            mov ds:[0], al

So we have 3 cycles free between each write operation, because the CPU have to
write-back his tiny 32 bytes write-back buffer.

    - There's no penalty due to a flag modification operation :
            add cx, dx
            adc ax, bx
The flags are updated during the first one-cycle operation, and are ready to
use in the second one, without penalty.

We see that this problem is not really important, because most of the common
operations don't need a special approach.

But for the Pentium processor, the problem is a lot more complex, because of
the two ALUs, and the flow dependency becomes really critical.

        2/ THINK "PENTIUM" - A "RISC" APPROACH

    There's a lot of things to say about the pentium code optimization.
    But one big difference between the pentium and to 3/486 is the common
instruction set...
    For example : the well-known ADC instruction, usefull for lots of
routines, becomes really slow on pentium. I can't imagine how many routs have
been written on this concept ... !
    More generally, the Intel peoples say that only the simplest
instructions are really optimized. All others "complex" instructions are not so
well implemented. The main rule is : "MOV, ADD, MUL" ! :)
    One main reason, is that the V pipe (the second ALU) only supports
simple instrcutions, and if a complex instruction is encountered in this pipe,
a huge penalty occurs : wait for the U pipe to be ready, etc ...
    And worse : most of the "complex" instructions cannot be executed until
the other pipe is empty !

This facts really reduces the number of "fast" instructions, and some complex
instructions are executed faster when simplified in small opcodes :
    Ex.:
(1)   mov eax, [mem]
      inc eax
      mov [mem], eax
could be faster than
(2)   inc [mem]

because in (1) you can mix this instructions with some others, and both pipes
will execute only 1-cycle instructions. So you can have a better control on
the both pipes :
    Ex.:
(1*)    mov eax, [mem]  ; 1
          mov ebx, ecx  ; 1
        inc eax         ; 1
          shl ebx, 2    ; 1
        mov [mem], eax  ; 1
          inc ebx       ; 1 => 3 for U and 3 for V => 3 cycles.
is faster than
(2*)    inc [mem]       ; 3
        mov ebx, ecx    ; 1
        shl ebx, 2      ; 2 (flow dependency penalty)
        inc ebx         ; 2 (flow dependency penalty) => more than 7 cycles

BTW : On pentium, small loops are often better than big portions of un-rolled
code, because of the branch prediction chip... So don't waste memory anymore,
and have a better cache ratio ! :)

        3/ THE LEGEND OF CODE ALIGNEMENT

    On 486, Pentium and Hexium, the Code alignement has strictly NO effect
on the speed of a routine.
    More exactly, it can change a little bit the speed, but you never know
in wich way : sometimes an ALIGN 4 or ALIGN 16 can speed it up a bit, but
sometimes it simply *decreases* the speed performance...
    So, here's my rule : "Never align your code".
    By doing this, you can have a more "compact" code, and then a better
cache ratio.

        4/ MIXING 16bit AND 32bit CODE

    In protected mode, you can choose the kind of code segment you want :
16bit or 32bit. To do this, just modify the 32bit-type bit in your code
selector.
    In a 16 bit code segment, all 32bit opcodes have the 66h prefix (like
in classical real mode), so the "MOV EAX, 1" instruction will be slower than
the "MOV AX, 1" instruction, because of the decoding time. Using 32bit
instructions in a 16bit code segment brings also another problem : the size
of the code will be increased, and since, the cache hit/miss ratio decreased.
    In a 32 bit code segment, all 16bit opcodes have the 66h prefix, and it
makes them run slower too.
    The main idea is : in a routine where the number of 16bit instructions
is smaller than the number of 32bit ones, you'll have better results in a 32bit
code segment. But sometimes not ... then we could have 2 code segments, and we
could choose in wich segment is located each routine, to avoid 66h prefixes.
    This seems a bit complex to do... but the result could be great.

Note : It's also important not to switch between modes to often, so this idea
should apply only to important routines.


        5/ UNORDERED TIPS

- "mov eax, cs" will copy CS in AX, and clear the upper-part of EAX.
  ( db 66h, 8Ch, C8h )
- When accessing the video, the segment used to adress video area doesn't
matter... but it still important to stay in DS when reading datas :

    mov al, ds:[bx]     ; 1
    mov es:[di], al     ; X (depends on the video card)
is better than
    mov al, es:[bx]     ; 2
    mov ds:[di], al     ; X (depends on the video card)

... and always try to add CPU internal instructions between video accesses.

- The [reg+offset] adressing mode takes longer if the offset specified is
larger than 127 bytes forward or backward, because the generated opcode will
have 1 byte more :
    mov al, ds:[bx+40h]
is faster than
    mov al, ds:[bx+140h]

- Recently i've seen a very curious phenomenon, due to the internal cache
structure : once you adressed a byte in memory, the location of the next byte
you will read is REALLY important :

    mov al, [bx]
    mov al, [bx+512]

is *A LOT* faster than

    mov al, [bx]
    mov al, [bx+128]

It's also thrue if adresses are very far one from another :

    mov al, ds:[bx]         ; with 1 Megabyte between both locations... !
    mov al, gs:[ebx+512]    ;

All diferrences seem to be important modulo 1024 bytes, that means :
    mov al, [bx]
    mov al, [bx+512]
is as fast as
    mov al, [bx]
    mov al, [bx+1024+512]

But sometimes, the best "distance" between two bytes is 64 or 128 instead of
512 ... !
One rule then : just check ! Try to move your different datas in memory, and
try to find the best "phase" between two data arrays.
I found it myself when coding a voxel, and for me the best "phase" between the
height map and the color map was 512 bytes. I still dunno why ???... :)
If you find new intersting facts or explanations concerning this, please let me
know.

                                ķ
                        }{ .\\ I) 
                                Ľ
    You can contact me on :

        A.C.E BBS :  +31+45-88-75-48  28.8k  Hemoroids WHQ  (France)
        Simon Caby  (Black Label  of  Hemoroids Pc)
   
EoF