significado procesador mexico assembly x86 sse simd

assembly - procesador - ¿El uso de mix de pxor y xorps afecta el rendimiento?



sse wikipedia (1)

TL: DR: parece que puede haber alguna afinación específica de microarch para esta secuencia de código específica. No hay nada "generalmente recomendado" al respecto que pueda ayudar en otros casos.

En una mayor consideración, creo que la teoría de @Iwillnotexist Idonotexist es la más probable: esto fue escrito por un no experto que pensó que esto podría ayudar. La asignación de registros es una gran pista: muchos prefijos REX podrían haberse evitado eligiendo todos los registros utilizados repetidamente en los 8 bajos.

XORPS se ejecuta en el dominio "flotante", en algunas CPU Intel (Nehalem y posterior), mientras que PXOR siempre se ejecuta en el dominio "ivec".

Como el cableado de cada salida de ALU a cada entrada de ALU para enviar los resultados directamente sería costoso, los diseñadores de CPU los dividen en dominios. (El reenvío guarda la latencia de escritura de nuevo en el archivo de registro y la relectura). Un cruce de dominio puede tomar un extra de 1 ciclo de latencia (familia Intel SnB) o 2 ciclos (Nehalem).

Lectura adicional: mi respuesta sobre ¿Cuál es la diferencia entre los intrínsecos lógicos de SSE?

Dos teorías se me ocurren:

  • Quien escribió esta idea pensó que PXOR y XORPS daría más paralelismo, porque no compiten entre sí. (Esto es incorrecto: PXOR puede ejecutarse en todos los puertos vectoriales ALU, pero XORPS no puede).

  • Este es un código muy ingeniosamente ajustado que crea un retraso de desvío a propósito, para evitar conflictos de recursos que pueden retrasar la ejecución del próximo PCLMULQDQ. (o, como sugiere EOF, el tamaño del código / alineación podría tener algo que ver con eso).

El aviso de derechos de autor en el código dice "2011-2015 Intel", por lo que vale la pena considerar la posibilidad de que sea de algún modo útil para alguna CPU reciente de Intel, y no se basa únicamente en un malentendido sobre cómo funcionan las CPU de Intel. Nehalem fue la primera CPU en incluir PCLMULQDQ en absoluto, y esta es Intel, así que, en todo caso, se ajustará para funcionar mal en las CPUs de AMD. El historial del código no está en el repositorio de git, solo la confirmación del 6 de mayo que agregó la versión actual.

El documento técnico de Intel (de diciembre de 2009) que se basa únicamente en PXOR usado, no en XORPS, en su versión del bloque 2x pclmul / 2x xor.

La tabla de Agner Fog ni siquiera muestra una cantidad de uops para PCLMULQDQ en Nehalem, o qué puertos requieren. Tiene una latencia de 12c y una por cada 8c de rendimiento, por lo que podría ser similar a la implementación de 18 uop de Sandy / Ivybridge. Haswell lo hace impresionante 3 uops (2p0 p5), mientras que se ejecuta en solo 1 uop en Broadwell (p0) y Skylake (p5).

XORPS solo puede ejecutarse en port5 (hasta Skylake, donde también se ejecuta en los tres puertos ALU vectoriales). En Nehalem tiene 2c de retardo de bypass cuando una de sus entradas proviene de PXOR. En las CPU de la familia SnB, Agner Fog dice:

En algunos casos, no hay retardo de derivación cuando se usa el tipo incorrecto de orden aleatorio o instrucción booleana.

Por lo tanto, creo que en realidad no hay un retraso de desvío adicional para reenviar desde PXOR -> XORPS en SnB, por lo que el único efecto sería que solo se puede ejecutar en el puerto 5. En Nehalem, podría retrasar el XORPS hasta que se hayan completado los PSHUFB.

En el ciclo principal desenrollado, hay un PSHUFB después de los XOR, para configurar las entradas para el próximo PCLMUL. SnB / IvB puede ejecutar mezclas de enteros en p1 / p5 (a diferencia de Haswell y más adelante donde solo hay una unidad aleatoria en p5. Pero tiene 256 b de ancho, para AVX2).

Como competir por los puertos necesarios para configurar la entrada para el próximo PCLMUL no parece útil, mi mejor estimación es el tamaño / alineación del código si este cambio se realizó al ajustar SnB.

En las CPU donde PCLMULQDQ es más de 4 uops, está microcodificado . Esto significa que cada PCLMULQDQ requiere una línea completa de caché uop para sí misma. Como solo 3 líneas de caché de UO pueden asignarse al mismo bloque 32B de instrucciones x86, esto significa que gran parte del código no cabe en la caché uop en absoluto en SnB / IvB. Cada línea de la memoria caché uop solo puede almacenar en caché las instrucciones contiguas. Del manual de optimización de Intel:

Todas las microoperaciones de una forma (línea de caché uop) representan instrucciones que son estáticamente contiguas en el código y tienen sus EIP dentro de la misma región alineada de 32 bytes.

Esto suena como un problema muy similar a tener un DIV entero en un bucle: alineación de bifurcación para bucles que implican instrucciones microcodificadas en CPU de la familia Intel SnB . Con la alineación correcta, puede lograr que se quede sin la caché uop (el DSB en la terminología del contador de rendimiento de Intel) . @Iwillnotexist Idonotexist hizo algunas pruebas útiles en una CPU Haswell de instrucciones microcodificadas, lo que demuestra que evitan que se ejecute desde el búfer de bucle invertido. (LSD en la terminología de Intel).

En Haswell y más adelante, PCLMULQDQ no está microcodificado, por lo que puede ir en la misma línea de caché uop con otras instrucciones antes o después.

Para las CPUs anteriores, podría valer la pena intentar modificar el código para quebrar la memoria caché uop en menos lugares. OTOH, el cambio entre la memoria caché uop y los decodificadores heredados puede ser peor que simplemente ejecutar desde los decodificadores.

También IDK si un despliegue tan grande es realmente útil. Probablemente varíe mucho entre SnB y Skylake, ya que las instrucciones microcodificadas son muy diferentes para la tubería, y es posible que SKL no bloquee el rendimiento de PCLMUL.

Me encontré con un rápido cálculo de CRC utilizando la implementación de PCLMULQDQ . Veo, chicos mezclan las instrucciones pxor y xorps como en el siguiente fragmento:

movdqa xmm10, [rk9] movdqa xmm8, xmm0 pclmulqdq xmm0, xmm10, 0x11 pclmulqdq xmm8, xmm10, 0x0 pxor xmm7, xmm8 xorps xmm7, xmm0 movdqa xmm10, [rk11] movdqa xmm8, xmm1 pclmulqdq xmm1, xmm10, 0x11 pclmulqdq xmm8, xmm10, 0x0 pxor xmm7, xmm8 xorps xmm7, xmm1

¿Hay alguna razón práctica para esto? Aumento de rendimiento? Si es así, ¿qué hay debajo de esto? ¿O tal vez es solo una especie de estilo de codificación, por diversión?