¿Cuánto uso de macros "probables" e "improbables" es demasiado?

12

Con frecuencia, las macros conocidas como likelyy unlikelyayudan al compilador a saber si ifgeneralmente se va a ingresar o omitir un Usarlo da como resultado algunas mejoras de rendimiento (más bien menores).

Empecé a usarlos recientemente, y no estoy seguro de con qué frecuencia se deben usar esas sugerencias. Actualmente lo uso con comprobación de errores ifs, que generalmente están marcados como unlikely. Por ejemplo:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

Parece correcto, pero la comprobación de errores ifocurre con bastante frecuencia y, en consecuencia, el uso de dichas macros.

Mi pregunta es, ¿es demasiado tener likelyy unlikelymacros en cada comprobación de errores if?

Mientras estamos en eso, ¿qué otros lugares se usan a menudo?


En mi uso actual, está en una biblioteca que hace una abstracción del subsistema en tiempo real, por lo que los programas serían portátiles entre RTAI, QNX y otros. Dicho esto, la mayoría de las funciones son bastante pequeñas y llaman directamente a una o dos funciones más. Muchos son incluso static inlinefunciones.

Entonces, antes que nada, no es una aplicación que pueda perfilar. No tiene sentido "identificar cuellos de botella" ya que es una biblioteca, no una aplicación independiente.

En segundo lugar, es algo así como "Sé que esto es poco probable, también podría decírselo al compilador". No trato activamente de optimizar el if.

Shahbaz
fuente
77
Apesta a micro optimización para mí ...
Ratchet Freak
2
Para el código de la aplicación, los agregaría solo si la creación de perfiles mostró que este código se usa en una ruta activa.
CodesInChaos
@james, eso solo dice likelyy unlikelyexiste y lo que hacen. No encontré nada que sugiriera cuándo y dónde es mejor usarlos.
Shahbaz
@Shahbaz "Si la condición es frecuentemente falsa, la ejecución no es lineal. Hay una gran cantidad de código no utilizado en el medio que no solo contamina el L1i debido a la captación previa, sino que también puede causar problemas con la predicción de la rama. Si el la predicción de rama es incorrecta, la expresión condicional puede ser muy ineficiente ". Entonces, bucles ajustados donde desea asegurarse de que las instrucciones que necesita estén en el caché L1i
James

Respuestas:

12

¿Necesita tanto rendimiento que está dispuesto a contaminar su código con eso? Es una optimización menor.

  • ¿El código se ejecuta en un circuito cerrado?
  • ¿Su aplicación tiene problemas de rendimiento?
  • ¿Ha perfilado su aplicación y determinado que este ciclo particular cuesta mucho tiempo de CPU?

A menos que pueda responder yesa todo lo anterior, no se moleste con cosas como esta.

Editar: en respuesta a la edición. Incluso cuando no puede crear un perfil, generalmente puede estimar puntos de acceso. Una función de asignación de memoria llamada por todos es un buen candidato, especialmente porque requiere un solo uso de la macro para funcionar en toda la biblioteca.

Chico Java
fuente
1
Para que quede claro, no te rechacé. Sin embargo, su respuesta realmente no responde a mi pregunta. ¿Está tratando de decir que la (un)likelymacro rara vez se usa y solo en código extremadamente crítico para el rendimiento? ¿Es una "mala práctica" usarlo con frecuencia, o simplemente "innecesario"?
Shahbaz
@Shahbaz Hace que el código sea menos legible y la optimización del rendimiento puede variar de ganancia trivial a pérdida trivial. Esto último cuando la suposición acerca de la probabilidad era incorrecta o ha cambiado para ser incorrecta debido a un cambio posterior a otras partes del código. Si nunca debe usarse a menos que sea necesario.
Peter
3
@Peter: Si bien es una lástima que la sintaxis no sea más agradable, las anotaciones sobre lo que es probable o improbable pueden proporcionar información útil a los humanos que están leyendo el código. Por ejemplo, alguien que vio if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }, podría juzgar que el uso del programador de un ifpara los valores 2 y 3 no fue simplemente una consecuencia de que el programador no supiera que C puede asociar dos caseetiquetas con un solo controlador.
supercat
Nadie ha mencionado lo que siento es un punto crítico. No es solo que el camino "improbable" ocurra con menos frecuencia, sino que está condicionado a que ese camino ocurra y no te preocupe la velocidad en absoluto. Por ejemplo, un periférico deja de responder, por lo que debe reiniciarlo y dormir de todos modos.
Benjamin Lindqvist
2

Si está escribiendo para x86 / x64 (y no está utilizando CPU de 20 años), la ganancia de rendimiento al usar __builtin_expect () será insignificante, si es que la hay. La razón es que las CPU modernas x86 / x64 (aunque no estoy 100% seguro sobre Atom), tienen una predicción dinámica de rama, por lo que esencialmente la CPU "aprende" sobre la rama que se toma con más frecuencia. Claro, esta información puede almacenarse solo para un número limitado de sucursales, sin embargo, solo hay dos casos posibles. Si (a) es una rama "de uso frecuente", entonces su programa se beneficiará de esa predicción de rama dinámica, y si (b) es una rama "rara", realmente no verá ningún impacto realista en el rendimiento debido a predicciones erróneas en tales ramas raras (20 ciclos de CPU de predicción errónea de ramas no son MUY malo si sucede una vez en una luna azul).

NB: esto NO implica que en la x86 / x64 moderna la importancia de la predicción errónea de la rama sea menor: cualquier rama con 50-50 de probabilidad de saltar-nojump aún incurrirá en una penalización (IIRC 10-20 ciclos de CPU), por lo que en las ramas del bucle interno puede Todavía hay que evitarlo. Solo es importante que __builtin_expect () en x86 / x64 haya disminuido (IIRC, hace unos 10-15 años más o menos), principalmente debido a la predicción dinámica de las ramas.

NB2: para otras plataformas más allá de x86 / x64, YMMV.

Liebre sin bichos
fuente
Bueno, el compilador sabe en qué rama será menos probable la CPU. Y puede hacer que sea realmente improbable. Pero el compilador probablemente ya conoce ese patrón sin la unlikelynotación.
Deduplicador
@Deduplicator: con la predicción de ramificación dinámica, el compilador no sabe qué ramificación es más probable ya que la CPU la calcula en tiempo de ejecución en función de ejecuciones anteriores en este mismo punto del código.
No-Bugs Hare