Hi Leute,
Ich möchte die Binäre Quersumme einer 32 Bit-Zahl möglichst effizient berechnen.
Ich arbeite auf einem Athlon XP Model 8 Prozessor mit angeblich 128kB split L1 Cache.
Ich habe mich vorläufig für die folgende Variante entschieden:
Ich denke mal im L1-Cache ist Platz für eine Lookup-Tabelle der Quersummen aller 8-Bit-Zahlen, und da könnte man ja 4 mal pro 32-Bit zahl nachschauen und das Ergebnis zusammenaddieren.
Leider bin ich nicht ganz so sattelfest in Assembler wie ich mir das wünsche, deshalb hab ich angefangen die Funktion in C zu programmieren und dann durch hinschauen auf den generierten Assembler-Code zu optimieren.
const char quersumme_8[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3 ,3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3 ,3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
unsigned char quersumme32(register unsigned int x) {
register unsigned char a, b;
register unsigned char r1,r2;
a=quersumme_8[x&0xff];
b=quersumme_8[(x>>8)&0xff];
r1=a+b;
x=x>>16;
a=quersumme_8[x&0xff];
b=quersumme_8[(x>>8)&0xff];
r2=a+b;
return r1+r2;
}
Alles anzeigen
Mein GCC macht mit -march=athlon-xp -O3 dann folgendes draus:
Dump of assembler code for function _ZN9P211quersumme32Ej:
0x08048a90 <P2::quersumme32(unsigned int)+0>: push %ebp
0x08048a91 <P2::quersumme32(unsigned int)+1>: mov %esp,%ebp
0x08048a93 <P2::quersumme32(unsigned int)+3>: mov 0x8(%ebp),%edx
0x08048a96 <P2::quersumme32(unsigned int)+6>: movzbl %dh,%eax
0x08048a99 <P2::quersumme32(unsigned int)+9>: movzbl %dl,%ecx
0x08048a9c <P2::quersumme32(unsigned int)+12>: shr $0x10,%edx
0x08048a9f <P2::quersumme32(unsigned int)+15>: movzbl 0x8049540(%eax),%eax
0x08048aa6 <P2::quersumme32(unsigned int)+22>: add 0x8049540(%ecx),%al
0x08048aac <P2::quersumme32(unsigned int)+28>: movzbl %dl,%ecx
0x08048aaf <P2::quersumme32(unsigned int)+31>: shr $0x8,%edx
0x08048ab2 <P2::quersumme32(unsigned int)+34>: movzbl 0x8049540(%edx),%edx
0x08048ab9 <P2::quersumme32(unsigned int)+41>: add 0x8049540(%ecx),%dl
0x08048abf <P2::quersumme32(unsigned int)+47>: leave
0x08048ac0 <P2::quersumme32(unsigned int)+48>: add %edx,%eax
0x08048ac2 <P2::quersumme32(unsigned int)+50>: movzbl %al,%eax
0x08048ac5 <P2::quersumme32(unsigned int)+53>: ret
End of assembler dump.
Alles anzeigen
Jetzt frage ich mich: ist dieser Code wirklich optimal? Wenn ich da hinsehe, sehe ich 2 shr, wo eigentlich nur eins benötigt würde? Und überhaupt, was fummelt der da am Stack herum?
Kann mir vielleicht jemand von den technischen Informatikern (oder sonstigen Assembler-Programmieren) helfen das effizienter in C-inline-assembler hinzukriegen (das sollte mit dem GCC ja gehen..)?
Vielen Dank!