name keywords google etiquetas ejemplos description gcc x86 x86-64 branch-prediction

gcc - keywords - meta tags google



¿Se utiliza la predicción de rama de prefijo Intel x86 0x2E/0x3E en realidad? (4)

Estos prefijos de instrucción no tienen efecto en los procesadores modernos (nada más nuevo que Pentium 4). Solo cuestan un byte de espacio de código, y por lo tanto, no generarlos es lo correcto.

Para obtener más información, consulte los manuales de optimización de Agner Fog, en particular 3. Microarquitectura: http://www.agner.org/optimize/

El "Manual de referencia de optimización de arquitecturas Intel® 64 y IA-32" ya no los menciona en la sección sobre optimización de sucursales (sección 3.4.1): http://www.intel.de/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf

Estos prefijos son una reliquia (inofensiva) de la arquitectura Netburst. En la optimización total, puede usarlos para alinear el código, pero eso es todo lo que es bueno para hoy en día.

En el último manual de desarrollo de software de Intel se describen dos prefijos de código de operación:

Group 2 > Branch Hints 0x2E: Branch Not Taken 0x3E: Branch Taken

Estos permiten una predicción explícita de las instrucciones de salto ( Jxx como Jxx )

Recuerdo haber leído hace un par de años que, en x86, la predicción explícita de rama era esencialmente una no operación en el contexto de los intrínsecos de predicción de rama de gccs.

Ahora no tengo claro si estas sugerencias de rama x86 son una característica nueva o si son esencialmente no-ops en la práctica.

¿Alguien puede aclarar esto?

(Es decir: ¿las funciones de predicción de rama gccs generan estas sugerencias de rama x86? ¿Y las CPU actuales de Intel no las ignoran? ¿Y cuándo sucedió esto?)

Actualizar:

He creado un programa de prueba rápida:

int main(int argc, char** argv) { if (__builtin_expect(argc,0)) return 1; if (__builtin_expect(argc == 2, 1)) return 2; return 3; }

Desmonta lo siguiente:

00000000004004cc <main>: 4004cc: 55 push %rbp 4004cd: 48 89 e5 mov %rsp,%rbp 4004d0: 89 7d fc mov %edi,-0x4(%rbp) 4004d3: 48 89 75 f0 mov %rsi,-0x10(%rbp) 4004d7: 8b 45 fc mov -0x4(%rbp),%eax 4004da: 48 98 cltq 4004dc: 48 85 c0 test %rax,%rax 4004df: 74 07 je 4004e8 <main+0x1c> 4004e1: b8 01 00 00 00 mov $0x1,%eax 4004e6: eb 1b jmp 400503 <main+0x37> 4004e8: 83 7d fc 02 cmpl $0x2,-0x4(%rbp) 4004ec: 0f 94 c0 sete %al 4004ef: 0f b6 c0 movzbl %al,%eax 4004f2: 48 85 c0 test %rax,%rax 4004f5: 74 07 je 4004fe <main+0x32> 4004f7: b8 02 00 00 00 mov $0x2,%eax 4004fc: eb 05 jmp 400503 <main+0x37> 4004fe: b8 03 00 00 00 mov $0x3,%eax 400503: 5d pop %rbp 400504: c3 retq 400505: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 40050c: 00 00 00 40050f: 90 nop

No veo 2E o 3E? Tal vez gcc los ha eluido por alguna razón?


Manual del desarrollador de software para arquitecturas Intel® 64 y IA-32 -> Volumen 2: Referencia del conjunto de instrucciones, AZ -> Capítulo 2: Formato de instrucciones -> 2.1 Formato de instrucciones para el modo protegido, modo de dirección real y modo virtual-8086 -> 2.1 .1 prefijos de instrucciones

Algunas microarquitecturas anteriores las utilizaron como sugerencias de bifurcaciones, pero las generaciones recientes no las tienen, y están reservadas para su uso en el futuro.


Si bien Pentium 4 es la única generación que realmente respeta las instrucciones de sugerencia de bifurcación, la mayoría de las CPU tienen algún tipo de predicción de bifurcación estática , que puede usarse para lograr el mismo efecto. Esta respuesta es un poco tangencial a la pregunta original, pero creo que sería una información valiosa para cualquiera que venga a esta página.

La guía de optimización de Intel y la guía de Agner Fog (que ya se han mencionado aquí) tienen excelentes descripciones de esta característica.

Intel tiene esto que decir acerca de las generaciones más nuevas que Core 2 :

Haga que el código de acceso directo después de una rama condicional sea el objetivo probable para una rama con un objetivo hacia adelante

Por lo tanto, se predice que las ramas condicionales que saltan hacia adelante en el código no serán tomadas por el algoritmo de predicción estática.

Esto es coherente con lo que GCC parece haber generado utilizando __builtin_expect : el __builtin_expect ''esperado'' return 1 / return 2 se coloca en las rutas no tomadas de las ramas condicionales, que se predecirán estáticamente como no tomadas.

Adicionalmente:

Las ramas que no tienen un historial en el búfer de destino de rama se predicen utilizando un algoritmo de predicción estática:

  • Predecir ramas incondicionales a ser tomadas.

  • Predecir ramas indirectas a NO ser tomadas.

Por lo tanto, en las rutas no esperadas ''esperadas'' donde GCC ha colocado jmp s incondicionales al final de la función, esos saltos se predecirán estáticamente como tomados (es decir, no se omitirán).

Intel también dice:

hacer que el código de caída a continuación de una rama condicional sea el objetivo improbable para una rama con un objetivo hacia atrás

Por lo tanto, se predice que las ramas condicionales que saltan hacia atrás en el código serán tomadas por el algoritmo de predicción estática.

Según Agner Fog, la mayoría de los Pentiums también siguen este algoritmo:

En PPro, P2, P3, P4 y P4E, se predice que una instrucción de transferencia de control que no se ha visto antes, o que no se encuentra en el búfer de objetivo de rama, se caerá si avanza, y se tomará si retrocede (por ejemplo, un bucle). La predicción estática lleva más tiempo que la predicción dinámica en estos procesadores.

Sin embargo, la familia Core 2 (y Pentium M) tiene una política completamente diferente:

Estos procesadores no utilizan la predicción estática. El predictor simplemente hace una predicción aleatoria la primera vez que se ve una rama, dependiendo de lo que suceda en la entrada BTB que se asigna a la nueva rama. Simplemente hay un 50% de probabilidad de hacer la predicción correcta de salto o no salto, pero el objetivo predicho es correcto.

Al igual que los procesadores AMD al parecer:

Se predice que una rama no se toma la primera vez que se ve. Se predice una rama siempre tomada después de la primera vez que se ha tomado. La predicción dinámica se usa solo después de que se haya tomado una rama y luego no se haya tomado. Los prefijos de sugerencia de rama no tienen efecto.

Hay un factor adicional a considerar: a las CPU, por lo general, les gusta ejecutarse de manera lineal, por lo que incluso las ramas tomadas predichas correctamente a menudo son más caras que las ramas no tomadas predichas correctamente.


gcc tiene razón en no generar el prefijo, ya que no tienen efecto para todos los procesadores desde el Pentium 4.

Pero __builtin_expect tiene otros efectos, como mover una ruta de código no esperada fuera de las ubicaciones de caché en el código o las decisiones en línea, por lo que sigue siendo útil.