From: spc@pineal.math.fau.edu (Sean 'Captain Napalm' Conner)
Subject: Re: Fast 386 code (and min() and max())
Organization: Florida Atlantic University, but who cares?
Date: Sat, 9 Jan 1993 21:01:59 GMT

In article <1993Jan5.151702.20426@netcom.com> tm@netcom.com (Toshiyasu Morita) writes:
>
>Alright, next question:
>
>What general guidelines are there to follow to write fast 386/486 code?
>What's fast, what's not?
>
  Some quick hacks:

  Avoid most of the specialized instructions (like xlat, enter and loop), as
the more general ones have been optimized to Hell and back and now run
faster than the specialized instructions that replaced them.

  ADD r,r is faster than SHL r,1 and posibly two ADDs are faster than 
SHL r,2.  And in this same vein, never MUL or DIV by a power of two, use
shifts (or ADDs 8-)

  Have the desinations for JMPs, Jccs, or CALLs on long word boundaries (if
you can spare the memory).  Even word boundaries are better than having a
destination end on an odd address (at the very least, the beginning of each
subroutine should start on an even address).

  Since the 486 is pipelined, you may have to play around with instruction
ordering to get best performance.  A fragement like:

		mov	ax,[si]
		add	si,2
		mov	[di],ax
		add	di,2

  is slow because SI is being used to generate an address, and then modified
in the following instruction, causing the pipeline to stall (to wait for the
first instruction to finish using SI).  A better way is to:

		mov	ax,[si]
		add	di,2			;allow ref to AX and SI
						;to finish 
		mov	[di],ax			;ADD DI is quick, won't
						;stall pipeline
		add	si,2			;now we can update SI

  Basically, memory references have the potiential to stall the pipeline. 
Be careful in the way you order the instructions (only for a 486+)

  FP operations are (for the basic operations + - * /) as fast as (or
faster) than the same integer based instructions on the 386/486, and may
(not sure on the 486) execute concurrently as the 386 instructions, so the
best bet would be to interspace FP operations with 386/486 operations (that
way, the main CPU won't have to wait for the FPU to finish).

  Avoid conditional jumps.  They stall the pipeline.  While it may be
tempting to write:

	foo:	mov	ax,[esi]		;get 16 bit quantity
		cwd				;sign extend into EAX
		mov	[edi],eax
		add	esi,2
		add	edi,4
		dec	cx
		jnz	foo

  A faster loop is to unroll it a few times:

	foo1:	mov	ax,[esi]
		cwd
		mov	[edi],eax
		dec	cx
		jz	endfoo
		mov	ax,2[esi]
		cwd
		mov	4[edi],eax
		add	esi,4
		add	edi,8
		dec	cx
		jnz	foo1
	endfoo:

  and adjust the count properly (I could have unrolled the loop two more
times if I wasn't lazy 8-).

  In 16 bit mode, 32 bit instructions take a bit longer to execute (more to
decode).  In 32 bit mode, 16 bit instructions take a bit longer to execute
(again, more to decode).

  Register to register operations are the fastest.  Memory to memory
operations are the slowest (or course).

  Well, that's about all I can remember off the top of my head (I don't have
my 386 reference manual handy with me right now).  Hope this helps some.

  -spc (Remember all of the optimizations for the 8088 you learned?  Well,
	throw them out the window, here comes the 386 8-)

Newsgroups: alt.lang.asm,rec.games.programmer