¿Qué son ciclos-estancados-frontend y ciclos-estancados-backend en el resultado 'perf stat'?

81

¿Alguien sabe cuál es el significado de los ciclos estancados frontend y los ciclos estancados backend en el resultado de la estadística de rendimiento? Busqué en Internet pero no encontré la respuesta. Gracias

$ sudo perf stat ls                     

Performance counter stats for 'ls':

      0.602144 task-clock                #    0.762 CPUs utilized          
             0 context-switches          #    0.000 K/sec                  
             0 CPU-migrations            #    0.000 K/sec                  
           236 page-faults               #    0.392 M/sec                  
        768956 cycles                    #    1.277 GHz                    
        962999 stalled-cycles-frontend   #  125.23% frontend cycles idle   
        634360 stalled-cycles-backend    #   82.50% backend  cycles idle
        890060 instructions              #    1.16  insns per cycle        
                                         #    1.08  stalled cycles per insn
        179378 branches                  #  297.899 M/sec                  
          9362 branch-misses             #    5.22% of all branches         [48.33%]

   0.000790562 seconds time elapsed
Dafan
fuente
No estoy seguro de cuál es la verdadera pregunta aquí. ¿Se preguntan qué son el frontend y el backend de una CPU? Lea esta introducción de muy alto nivel . ¿Responde esto a tu pregunta?
Ali
Busqué y busqué una respuesta similar ... Este fue el recurso más útil que encontré de Intel: software.intel.com/en-us/articles/…
Jmoney38
No, casi nadie sabe lo que realmente significan. Pero haciendo referencia al manual (como en la respuesta de Manuel Selva) combinado con esta publicación (que aún no entiendo del todo), son los más cercanos que he encontrado: sites.utexas.edu/jdm4372/2014/06/04/…
jberryman

Respuestas:

58

La teoría:

Empecemos por esto: las CPU de hoy en día son superescalares, lo que significa que pueden ejecutar más de una instrucción por ciclo (IPC). Las últimas arquitecturas de Intel pueden llegar hasta 4 IPC (4 decodificadores de instrucciones x86). No pongamos en discusión macro / micro fusión para complicar más las cosas :).

Normalmente, las cargas de trabajo no alcanzan IPC = 4 debido a diversas disputas por recursos. Esto significa que la CPU está desperdiciando ciclos (la cantidad de instrucciones las da el software y la CPU tiene que ejecutarlas en el menor número de ciclos posible).

Podemos dividir los ciclos totales que gasta la CPU en 3 categorías:

  1. Ciclos donde las instrucciones se retiran (trabajo útil)
  2. Ciclos que se gastan en el back-end (desperdiciados)
  3. Ciclos gastados en el Front-End (desperdiciados).

Para obtener un IPC de 4, el número de ciclos que se retiran tiene que estar cerca del número total de ciclos. Tenga en cuenta que en esta etapa, todas las microoperaciones (uOps) se retiran del pipeline y comprometen sus resultados en registros / cachés. En esta etapa, puede tener incluso más de 4 uOps retirándose, porque este número viene dado por el número de puertos de ejecución. Si solo tiene el 25% de los ciclos retirando 4 uOps, entonces tendrá un IPC general de 1.

Los ciclos estancados en el back-end son un desperdicio porque la CPU tiene que esperar recursos (generalmente memoria) o terminar instrucciones de latencia larga (por ejemplo, transcedentals - sqrt, recíprocos, divisiones, etc.).

Los ciclos estancados en el front-end son un desperdicio porque eso significa que el Front-End no alimenta al Back End con microoperaciones. Esto puede significar que tiene errores en la caché de instrucciones o instrucciones complejas que aún no están decodificadas en la caché de microoperaciones. El código compilado justo a tiempo generalmente expresa este comportamiento.

Otro motivo de estancamiento es el error de predicción de rama. Eso se llama mala especulación. En ese caso, se emiten uOps pero se descartan porque el BP predijo mal.

La implementación en perfiladores:

¿Cómo interpretas los ciclos estancados BE y FE?

Diferentes perfiladores tienen diferentes enfoques sobre estas métricas. En vTune, las categorías 1 a 3 se suman para dar el 100% de los ciclos. Eso parece razonable porque o tiene su CPU estancada (no se están retirando uOps) o realiza un trabajo útil (uOps) retirándose. Vea más aquí: https://software.intel.com/sites/products/documentation/doclib/stdxe/2013SP1/amplifierxe/snb/index.htm

En perf, esto no suele suceder. Eso es un problema porque cuando ves 125% de ciclos estancados en la parte delantera , no sabes cómo interpretar esto realmente. Puede vincular la métrica> 1 con el hecho de que hay 4 decodificadores, pero si continúa con el razonamiento, el IPC no coincidirá.

Aún mejor, no sabes qué tan grande es el problema. 125% de qué? Entonces, ¿qué significan los #ciclos?

Personalmente, parezco un poco sospechoso sobre los ciclos estancados BE y FE de perf y espero que esto se solucione.

Probablemente obtendremos la respuesta final depurando el código desde aquí: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/perf/builtin-stat.c

VAndrei
fuente
¿Qué eventos se utilizan en VTune como FE y BE? Manuel publicó eventos de perf en Sandy Bridge. A veces, el decodificador no puede decodificar 4 instrucciones ( realworldtech.com/sandy-bridge/4 ; hay 3 decodificadores simples que no pueden decodificar comandos complejos).
osgx
Es cierto que también hay un decodificador complejo, pero también puede decodificar instrucciones simples. Actualicé mi publicación con un enlace a los contadores de vTune. Utiliza los mismos contadores que perf, pero creo que vTune se combina de manera diferente.
VAndrei
4
Vtune usa software.intel.com/en-us/articles/… "IDQ_UOPS_NOT_DELIVERED.CORE / SLOTS" como "Frontend bound" y "1 - (Front-End Bound + Retiring + Bad Speculation)" como "Backend obligado" donde " Retirando = UOPS_RETIRED.RETIRE_SLOTS / SLOTS "," Mala especulación = (UOPS_ISSUED.ANY - UOPS_RETIRED.RETIRE_SLOTS + 4 * INT_MISC.RECOVERY_CYCLES) / SLOTS "y" SLOTS = 4 * "CPU_CLEDK de ancho igual a la línea de canalización". ".
osgx
1
Y para Sandy Bridge, el manual de optimización de Intel intel.com/content/dam/www/public/us/en/documents/manuals/… proporciona lo mismo en "B.3.2 Metodología jerárquica de caracterización del rendimiento de arriba hacia abajo" "% FE_Bound = 100 * (IDQ_UOPS_NOT_DELIVERED.CORE / N);% Mala_especulación = 100 * ((UOPS_ISSUED.ANY - UOPS_RETIRED.RETIRE_SLOTS + 4 * INT_MISC.RECOVERY_CYCLES) / N);% Retirada = 100 * (UOPS_RETIR_RETIR); * (1 - (FE_Bound + Retiring + Bad_Speculation)); N = 4 * CPU_CLK_UNHALTED.THREAD "
osgx
@osgx Gracias. Ahora sabemos qué significan las métricas en vTune y que suman el 100%. La siguiente pregunta es ¿por qué perf los calcula de manera diferente? ¿Es un error o hay un significado detrás de él?
VAndrei
42

Para convertir eventos genéricos exportados por perf en los eventos sin procesar de la documentación de su CPU, puede ejecutar:

more /sys/bus/event_source/devices/cpu/events/stalled-cycles-frontend 

Te mostrará algo como

event=0x0e,umask=0x01,inv,cmask=0x01

De acuerdo con la documentación de Intel SDM volumen 3B (tengo un núcleo i5-2520):

UOPS_ISSUED.ANY:

  • Incrementa en cada ciclo el # de Uops emitidos por la RAT a RS.
  • Establezca Cmask = 1, Inv = 1, Any = 1 para contar los ciclos detenidos de este núcleo.

Para el evento de backend de ciclos estancados que se traduce en event = 0xb1, umask = 0x01 en mi sistema, la misma documentación dice:

UOPS_DISPATCHED.THREAD:

  • Cuenta el número total de uops que se enviarán por subproceso en cada ciclo
  • Configure Cmask = 1, INV = 1 para contar los ciclos de bloqueo.

Por lo general, los ciclos detenidos son ciclos en los que el procesador está esperando algo (la memoria se alimenta después de ejecutar una operación de carga, por ejemplo) y no tiene otras cosas que hacer. Además, la parte frontal de la CPU es la pieza de hardware responsable de obtener y decodificar las instrucciones (convertirlas en UOP), mientras que la parte de backend es responsable de ejecutar eficazmente las UOP.

Manuel Selva
fuente
gracias por su respuesta. Entonces, ¿cuál es la diferencia entre estancado e inactivo?
Dafan
2
Atascado e inactivo son lo mismo. La CPU está inactiva porque está detenida porque la canalización de instrucciones no se mueve.
Milind Dumbare
@Milind, ¿no debería haber una diferencia, estancado debería ser "no progresamos porque la siguiente etapa no lo permite", y inactivo debería ser "no hay nada que procesar"?
Surt
13

Un ciclo de CPU se "detiene" cuando la canalización no avanza durante él.

La tubería del procesador se compone de muchas etapas: el front-end es un grupo de estas etapas que es responsable de las fases de recuperación y decodificación, mientras que el back-end ejecuta las instrucciones. Hay un búfer entre el front-end y el back-end, por lo que cuando el primero se detiene, el segundo aún puede tener algo de trabajo por hacer.

Tomado de http://paolobernardi.wordpress.com/2012/08/07/playing-around-with-perf/

Milind Dumbare
fuente
2
¿Cómo podemos tener más puestos que ciclos?
osgx
11

Según el autor de estos eventos, se definieron libremente y se aproximan por los contadores de rendimiento de CPU disponibles. Como sé, perf no admite fórmulas para calcular algún evento sintético basado en varios eventos de hardware, por lo que no puede usar el método de bloqueo de front-end / back-end del manual de optimización de Intel (implementado en VTune) http: // www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf "B.3.2 Metodología jerárquica de caracterización del rendimiento de arriba hacia abajo"

%FE_Bound = 100 * (IDQ_UOPS_NOT_DELIVERED.CORE / N ); 
%Bad_Speculation = 100 * ( (UOPS_ISSUED.ANY – UOPS_RETIRED.RETIRE_SLOTS + 4 * INT_MISC.RECOVERY_CYCLES ) / N) ; 
%Retiring = 100 * ( UOPS_RETIRED.RETIRE_SLOTS/ N) ; 
%BE_Bound = 100 * (1 – (FE_Bound + Retiring + Bad_Speculation) ) ; 
N = 4*CPU_CLK_UNHALTED.THREAD" (for SandyBridge)

Las fórmulas correctas se pueden usar con algunas secuencias de comandos externas, como se hizo en pmu-tools ( toplev.py) de Andi Kleen : https://github.com/andikleen/pmu-tools (fuente), http://halobates.de/blog/ p / 262 (descripción):

% toplev.py -d -l2 numademo  100M stream
...
perf stat --log-fd 4 -x, -e
{r3079,r19c,r10401c3,r100030d,rc5,r10e,cycles,r400019c,r2c2,instructions}
{r15e,r60006a3,r30001b1,r40004a3,r8a2,r10001b1,cycles}
numademo 100M stream
...
BE      Backend Bound:                      72.03%
    This category reflects slots where no uops are being delivered due to a lack
    of required resources for accepting more uops in the    Backend of the pipeline.
 .....
FE      Frontend Bound:                     54.07%
This category reflects slots where the Frontend of the processor undersupplies
its Backend.

Compromiso que introdujo eventos de parada de ciclos de interfaz y de ciclos de parada de fondo en lugar del universal original stalled-cycles:

http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/commit/?id=8f62242246351b5a4bc0c1f00c0c7003edea128a

author  Ingo Molnar <mingo@el...>   2011-04-29 11:19:47 (GMT)
committer   Ingo Molnar <mingo@el...>   2011-04-29 12:23:58 (GMT)
commit  8f62242246351b5a4bc0c1f00c0c7003edea128a (patch)
tree    9021c99956e0f9dc64655aaa4309c0f0fdb055c9
parent  ede70290046043b2638204cab55e26ea1d0c6cd9 (diff)

Perf events: agregue definiciones de eventos de ciclo estancado de front-end y back-end genéricos. Agregue dos eventos de hardware genéricos: ciclos estancados de front-end y back-end.

Estos eventos miden las condiciones cuando la CPU está ejecutando código pero sus capacidades no se utilizan por completo. Comprender estas situaciones y analizarlas es una subtarea importante de los flujos de trabajo de optimización de código.

Ambos eventos limitan el rendimiento: la mayoría de los bloqueos de front-end tienden a ser causados ​​por predicciones erróneas de rama o errores de búsqueda de instrucciones, los bloqueos de backend pueden ser causados ​​por diversas escaseces de recursos o programación de instrucciones ineficiente.

Las paradas de front-end son las más importantes: el código no puede ejecutarse rápidamente si no se mantiene el flujo de instrucciones.

Un back-end sobreutilizado puede causar paradas en el front-end y, por lo tanto, también debe vigilarse.

La composición exacta depende en gran medida de la lógica del programa y de la combinación de instrucciones.

Usamos los términos 'stall', 'front-end' y 'back-end' de manera flexible e intentamos usar los mejores eventos disponibles de CPU específicas que se aproximen a estos conceptos.

Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Enlace: http://lkml.kernel.org/n/[email protected] Firmado por: Ingo Molnar

    /* Install the stalled-cycles event: UOPS_EXECUTED.CORE_ACTIVE_CYCLES,c=1,i=1 */
-       intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES] = 0x1803fb1;
+       intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x1803fb1;

-   PERF_COUNT_HW_STALLED_CYCLES        = 7,
+   PERF_COUNT_HW_STALLED_CYCLES_FRONTEND   = 7,
+   PERF_COUNT_HW_STALLED_CYCLES_BACKEND    = 8,
osgx
fuente
Entonces, al final, ¿es un error de rendimiento? Porque FE + BE +? no agregue a un valor teórico conocido, es difícil evaluar qué tan grande es el problema de su código. Cuando vea que el 75% de FE se estanca, debe compararse con algo. Decir que el 75% del 100% del código está bloqueado en el FE o BE tiene un significado y valor completamente diferente. Por lo que veo, incluso toplev.py tiene el mismo problema. Si esto no es un problema, ¿cómo interpretamos las métricas? ¿Qué hace que las métricas sean altas o bajas?
VAndrei
VAndrei, ¿tiene un ejemplo breve y reproducible para SandyBridge (generación + -1)? tanto para perf statcon FE> 100% como para toplev.py? Acabo de comenzar con bucles simples y cortos y tengo ciclos 3G para instrucciones 3G (1G son ramas con una tasa de falla del 0.00%) con paradas 2G FE ( perf stat) y 1G BE paradas (IPC = 1.00). Creo que el problema es definir correctamente "parada" para el núcleo complejo de OOO y otro es interpretar correctamente los toplev.pyresultados.
osgx
El código que publiqué aquí: stackoverflow.com/questions/28961405/… debe estar vinculado al extremo frontal. Hay muchos fallos de rama en él, por lo que generaría paradas de EF. Con respecto a BE obligado, necesita una carga de trabajo que espere de los datos de RAM. Asigne la mitad del tamaño de su memoria física en un búfer y use un LCG (como en mi código) para hacer una operación de lectura / modificación / escritura en una ubicación aleatoria en el búfer. Eso genera una pequeña cantidad de instrucciones además de la transacción RMW y el núcleo se detendrá en el BE esperando los datos de la RAM.
VAndrei
Generar cargas de trabajo vinculadas a FE es todo un desafío. Pruebe si el microbenchmark de ramificación funciona, pero si no, necesita algo más complejo. La pérdida de FE se generaría por un gran número de errores de caché de instrucciones. Para hacer eso, necesita un código grande con saltos lejanos para generar múltiples errores de I $. En este punto, no tengo una idea sobre cómo hacer una carga de trabajo vinculada a FE en un microbenchmark.
VAndrei
Creo que le interesaría este enlace: stackoverflow.com/questions/1756825/… Puede usar algunas de las técnicas discutidas para vaciar el I $ y, por lo tanto, generar puestos de FE.
VAndrei