tag seguimiento manager etiqueta conversiones conversion adwords c assembly compiler-optimization

seguimiento - adwords conversion tag manager



flotación para doble conversión: ¿por qué tantas instrucciones? (2)

Hay varias cosas que están sucediendo aquí que puedo ver rápidamente (el código es un poco largo, el tiempo es un poco tarde y no soy un fan de la sintaxis de AT&T).

En primer lugar, el segundo bucle fue vectorizado (pero mal, ver más abajo). Esto causa intrínsecamente una gran cantidad de código, ahora tiene que lidiar con un "final de cola" que es más corto que un vector y tal.

En segundo lugar, flotar para duplicar es una conversión de ampliación. Eso no importa para los escalares, pero con los vectores significa que no puede simplemente leer algunos datos, convertirlos y escribirlos. En algún punto de las líneas, terminará con el doble de bytes y habrá que repartirlos. con. (de ahí las movhlps %xmm0,%xmm1 )

El bucle real solo se extiende desde 402098h hasta 4020 cfh, debajo de eso está el "manejo de la cola", y encima de eso hay una monstruosidad que verifica si se omite el bucle principal por completo y algunas cosas que aún no he resuelto. Detecte si fue para la alineación, pero no veo ninguna test rdi, 15 ''s allí, ni nada obvio que pueda deshacerse de un comienzo no alineado.

Y en tercer lugar, GCC está siendo cojo. Esto no es inusual. Parece pensar que xmm3 está involucrado de alguna manera, y no lo está, y parece haber olvidado que los vectores se pueden cargar en una memoria de una sola pieza; entonces, de nuevo, esto podría ser porque la monstruosidad al principio realmente no lo hizo. prueba de alineación y esta es su defensa contra punteros no alineados. En cualquier caso, sin embargo, GCC hizo un mal trabajo aquí.

Tengo curiosidad si alguien puede arrojar algo de luz sobre esto por mí. Estoy trabajando en algunas cosas de conversión de datos numéricos, y tengo varias funciones que hacen conversiones de datos, que defino usando dos macros:

#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++); / } / }

Cuando defino una conversión flotante a int:

CONV_VIA_FUNC(f_to_i, float, int16_t, lrintf);

Obtengo un pequeño y terso trozo de ensamblar con -O3 en:

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

Sin embargo, cuando defino una función float-> double (o double-> float):

CONV_VIA_CAST(f_to_d, float, double);

Me sale esta monstruosidad

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

¿Alguien puede arrojar algo de luz sobre lo que está pasando bajo el capó aquí para la conversión flotante-> doble? ¿Y quizás cómo podría escribirse para obtener un ensamblaje más eficiente? Estoy usando gcc 4.6.3 si eso importa.


Lo que usted llama "monstruosidad" en realidad parece un código vectorizado automáticamente . Algo así como 20 años de investigación se han dedicado a este tipo de técnica antes de que comenzara a funcionar bien y fuera útil en compiladores de propósito general.

Puede que no sea bonito, pero los implementadores de GCC creen que será más rápido para arreglos largos. Si sus arreglos no son realmente largos, o si no puede soportar la idea de que el código compilado se vea así, desactive esa optimización en particular. La -O2 con -O2 debería hacerlo (sin probar).