Almost a year ago, a thread on “premature Delphi optimization” came by on G+ about this code:
procedure ExchangeInteger(var AValue1, AValue2: Integer); begin AValue1 := AValue1 xor AValue2; AValue2 := AValue1 xor AValue2; AValue1 := AValue1 xor AValue2; end;
I don’t think that was premature optimization, just some code from an old fart that had already been programming in the era where processors had reasons to use it:
- limited instruction set
- few registers
- limited amount of RAM.
Back then, the only efficient way to exchange two variables of the same data type was using the XOR swap algorithm.
Nowadays you have more options, and this is where the fun in that thread began, which I will show in a minute.
First a bit of history
The XOR swap algorithm was widely known in the 80s of last century and before, especially because the 6502 processor (oh the days of LISA Assembler) was vastly popular, as was the Z80. Together, they powered the majority of the home computers in the 70s and 80s.
Popular 6502 powered computers were Acorn Atom and BBC, Apple II series, Commodore PET and VIC-20 (the Commodore 64 ran on a 6510), Atari 400/800/XL/XE.
Popular Z80 powered computers were Amstrad CPC, MSX, Exidy Sorcerer, TRS-80, P2000, Sinclair ZX80, ZX81 and ZX Spectrum, Kaypro, Osborne 1 and the Z-80 SoftCard for Apple II.
- The 6502 (see 6502.org) had
- only 56 instructions (the 65C02 variations had a few more), and the XOR was called EOR.
- addressing memory was limited (not all instructions understood both zero-page-absolute and absolute addressing)
- only 3 registers (A, X and Y) - The Z80 (see z80.info) had:
- only 68 instructions and XOR was indeed called XOR
- many more addressing modes than the 6502
- a whopping 9 registers
So back-then you needed the XOR algorighm to do the exchange.
Now the fun part: more choice
Two more variations for the method were proposed.
The first one was to use the XCHG instruction, which has been there since the 8086.
There is even an XCHG EAX, EAX instruction with opcode $90, which is the same as NOP.
Stefan Glienke proposed it, as Delphi compiler adds a MOV with every XOR.
procedure ExchangeInteger(var AValue1, AValue2: Integer); asm xchg ecx, [eax] xchg ecx, [edx] xchg [eax], ecx end;
You’d think this is fast: assembler + tiny XCHG instruction. Well, don’t think, but measure: this one is slower, even on modern systems.
Eric Grange did notice the speed difference and came up with this one:
procedure ExchangeInteger(var AValue1, AValue2: Integer); var tmp: Integer; begin tmp := AValue1; aValue1 := AVAlue2; aValue2 := tmp; end;
Stefan Glienke concluded: The xor version is faster than the xchg thing. And the version with tmp var is faster (with optimization on ofc).
Real conclusion
The real conclusion however was made by Kenneth Cochran:
I’d leave this one up to the compiler to optimize.
–jeroen
via: Stefan Meisner – Google+ – if premature optimization is the root of all evil then….
Filed under: Borland Pascal, Delphi, Delphi 1, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi 3, Delphi 4, Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi x64, Delphi XE, Delphi XE2, Delphi XE3, Development, History, Pascal, Software Development, Turbo Pascal, UCSD Pascal