Float in Double Conversion:Warum so viele Anweisungen?

Float in Double Conversion:Warum so viele Anweisungen?


Ich bin gespannt, ob mir jemand etwas Licht ins Dunkel bringen kann. Ich arbeite an einigen Sachen zur numerischen Datenkonvertierung, und ich habe mehrere Funktionen, die Datenkonvertierungen durchführen, die ich mit zwei Makros definiere:


#define CONV_VIA_CAST(name, dtype, vtype)                               \
static inline void name(void *data, void *view, size_t len) { \
vtype *vptr = (vtype*)view; \
dtype *dptr = (dtype*)data; \
for (size_t ii=0; ii < len/sizeof(vtype); ii++) { \
*vptr++ = (vtype)*dptr++; \
} \
}
#define CONV_VIA_FUNC(name, dtype, vtype, via) \
static inline void name(void *data, void *view, size_t len) { \
vtype *vptr = (vtype*)view; \
dtype *dptr = (dtype*)data; \
for (size_t ii=0; ii < len/sizeof(vtype); ii++) { \
*vptr++ = (vtype)via(*dptr++); \
} \
}

Wenn ich eine Float-zu-Int-Konvertierung definiere:


 CONV_VIA_FUNC(f_to_i, float, int16_t, lrintf); 

Ich bekomme ein nettes kurzes Stück Assemble mit -O3 auf:


   0x0000000000401fb0 <+0>:     shr    %rdx
0x0000000000401fb3 <+3>: je 0x401fd3 <f_to_i+35>
0x0000000000401fb5 <+5>: xor %eax,%eax
0x0000000000401fb7 <+7>: nopw 0x0(%rax,%rax,1)
0x0000000000401fc0 <+16>: cvtss2si (%rdi,%rax,4),%rcx
0x0000000000401fc6 <+22>: mov %cx,(%rsi,%rax,2)
0x0000000000401fca <+26>: add $0x1,%rax
0x0000000000401fce <+30>: cmp %rdx,%rax
0x0000000000401fd1 <+33>: jne 0x401fc0 <f_to_i+16>
0x0000000000401fd3 <+35>: repz retq

Wenn ich jedoch eine float->double (oder double->float) Funktion definiere:


CONV_VIA_CAST(f_to_d, float,   double); 

Ich bekomme diese Monstrosität:


   0x0000000000402040 <+0>:     mov    %rdx,%r8
0x0000000000402043 <+3>: shr $0x3,%r8
0x0000000000402047 <+7>: test %r8,%r8
0x000000000040204a <+10>: je 0x402106 <f_to_d+198>
0x0000000000402050 <+16>: shr $0x5,%rdx
0x0000000000402054 <+20>: lea 0x0(,%rdx,4),%r9
0x000000000040205c <+28>: test %r9,%r9
0x000000000040205f <+31>: je 0x402108 <f_to_d+200>
0x0000000000402065 <+37>: lea (%rdi,%r8,4),%rax
0x0000000000402069 <+41>: cmp $0xb,%r8
0x000000000040206d <+45>: lea (%rsi,%r8,8),%r10
0x0000000000402071 <+49>: seta %cl
0x0000000000402074 <+52>: cmp %rax,%rsi
0x0000000000402077 <+55>: seta %al
0x000000000040207a <+58>: cmp %r10,%rdi
0x000000000040207d <+61>: seta %r10b
0x0000000000402081 <+65>: or %r10d,%eax
0x0000000000402084 <+68>: test %al,%cl
0x0000000000402086 <+70>: je 0x402108 <f_to_d+200>
0x000000000040208c <+76>: xorps %xmm3,%xmm3
0x000000000040208f <+79>: xor %eax,%eax
0x0000000000402091 <+81>: xor %ecx,%ecx
0x0000000000402093 <+83>: nopl 0x0(%rax,%rax,1)
0x0000000000402098 <+88>: movaps %xmm3,%xmm0
0x000000000040209b <+91>: add $0x1,%rcx
0x000000000040209f <+95>: movlps (%rdi,%rax,1),%xmm0
0x00000000004020a3 <+99>: movhps 0x8(%rdi,%rax,1),%xmm0
0x00000000004020a8 <+104>: movhlps %xmm0,%xmm1
0x00000000004020ab <+107>: cvtps2pd %xmm0,%xmm2
0x00000000004020ae <+110>: cvtps2pd %xmm1,%xmm0
0x00000000004020b1 <+113>: movlpd %xmm2,(%rsi,%rax,2)
0x00000000004020b6 <+118>: movhpd %xmm2,0x8(%rsi,%rax,2)
0x00000000004020bc <+124>: movlpd %xmm0,0x10(%rsi,%rax,2)
0x00000000004020c2 <+130>: movhpd %xmm0,0x18(%rsi,%rax,2)
0x00000000004020c8 <+136>: add $0x10,%rax
0x00000000004020cc <+140>: cmp %rcx,%rdx
0x00000000004020cf <+143>: ja 0x402098 <f_to_d+88>
0x00000000004020d1 <+145>: cmp %r9,%r8
0x00000000004020d4 <+148>: lea (%rsi,%r9,8),%rsi
0x00000000004020d8 <+152>: lea (%rdi,%r9,4),%rdi
0x00000000004020dc <+156>: je 0x40210d <f_to_d+205>
0x00000000004020de <+158>: mov %r9,%rdx
0x00000000004020e1 <+161>: mov %r9,%rax
0x00000000004020e4 <+164>: neg %rdx
0x00000000004020e7 <+167>: lea (%rsi,%rdx,8),%rcx
0x00000000004020eb <+171>: lea (%rdi,%rdx,4),%rdx
0x00000000004020ef <+175>: nop
0x00000000004020f0 <+176>: movss (%rdx,%rax,4),%xmm0
0x00000000004020f5 <+181>: cvtps2pd %xmm0,%xmm0
0x00000000004020f8 <+184>: movsd %xmm0,(%rcx,%rax,8)
0x00000000004020fd <+189>: add $0x1,%rax
0x0000000000402101 <+193>: cmp %rax,%r8
0x0000000000402104 <+196>: ja 0x4020f0 <f_to_d+176>
0x0000000000402106 <+198>: repz retq
0x0000000000402108 <+200>: xor %r9d,%r9d
0x000000000040210b <+203>: jmp 0x4020de <f_to_d+158>
0x000000000040210d <+205>: nopl (%rax)
0x0000000000402110 <+208>: retq

Kann jemand etwas Licht ins Dunkel bringen, was hier unter der Motorhaube für die Schwimmer-> Doppelumwandlung vor sich geht? Und vielleicht, wie es geschrieben werden könnte, um eine effizientere Montage zu erreichen? Ich verwende gcc 4.6.3, falls das wichtig ist.


Antworten:


Hier passieren mehrere Dinge, die ich schnell erkennen kann (der Code ist ein bisschen lang, die Zeit ist ein bisschen spät und ich bin kein Fan der AT&T-Syntax).


Erstens war die zweite Schleife vektorisiert (aber schlecht, siehe unten). Das verursacht von Natur aus ein gewisses Aufblähen des Codes - es muss jetzt mit einem "Ende" fertig werden, das kürzer als ein Vektor und so ist.


Zweitens ist Float to Double eine Erweiterungskonvertierung. Das spielt bei Skalaren keine Rolle, aber bei Vektoren bedeutet dies, dass Sie nicht einfach einige Daten lesen, konvertieren und zurückschreiben können - irgendwo auf der Strecke werden Sie doppelt so viele Bytes haben und sie müssen behandelt werden mit. (daher der movhlps %xmm0,%xmm1 )


Die eigentliche Schleife erstreckt sich nur von 402098h bis 4020cfh, darunter ist das "Tail-Handling", und darüber ist eine Monstrosität, die testet, ob sie die Hauptschleife vollständig überspringt und einige Dinge, die ich nicht ganz herausgefunden habe - es würde machen Sinn, wenn es für die Ausrichtung war, aber ich sehe keine test rdi, 15 's drin, noch irgendetwas Offensichtliches, das einen nicht ausgerichteten Anfang beseitigen würde.


Und drittens ist GCC lahm. Dies ist nicht ungewöhnlich. Es scheint zu glauben, dass xmm3 irgendwie involviert ist, was nicht der Fall ist, und es scheint vergessen zu haben, dass Vektoren in einem Stück aus dem Speicher geladen werden können - das könnte wiederum daran liegen, dass die Monstrosität am Anfang wirklich ist nicht Test auf Ausrichtung und dies ist seine Verteidigung gegen nicht ausgerichtete Zeiger. GCC hat hier jedenfalls einen schlechten Job gemacht.