es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Discrepancia de tiempo de BR/RET al regresar de una subrutina ficticia a una dirección de retorno modificada.

En mis aventuras experimentando con la arquitectura ARM de 64 bits, noté una peculiar diferencia de velocidad dependiendo de si se usa “br” o “ret” para regresar de una subrutina.

; Fabricado con fines de aprendizaje/experimentación únicamente, sin ningún uso práctico
foo:
    cmp     w0, #0
    b.eq    .L0
    sub     w0, w0, #1
    sub     x30, x30, #4
    ret
.L0:
    ret ; intencionalmente duplicado 'ret'

La intención de esta subrutina es hacer que el llamador de “foo” “reingrese” a “foo” “w0” veces haciendo que “foo” regrese a la instrucción que llamó a “foo” en primer lugar (es decir, la instrucción inmediatamente antes de aquella a la que apunta “x30”). Con una temporización aproximada, con “w0” siendo un valor lo suficientemente alto, tomó alrededor de 1362 milisegundos en promedio. Curiosamente, al reemplazar el primer “ret” con “br x30”, se ejecuta más de dos veces más rápido, tomando solo unos 550 milisegundos en promedio.

La discrepancia de tiempo desaparece si la prueba se simplifica a solo llamar repetidamente a una subrutina con solo “ret” / “br x30”. ¿Qué hace que la subrutina artificial anterior sea más lenta con un “ret”?

Probé esto en algún tipo de procesador ARMv8.2 (Cortex-A76 + Cortex-A55). No estoy seguro de hasta qué punto big.LITTLE afectaría los tiempos, pero parecían bastante consistentes en múltiples ejecuciones. Esto de ninguna manera es una prueba de referencia [micro], sino más bien algo como “aproximadamente cuánto tiempo tarda esto si se ejecuta N veces”.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    La diferencia de velocidad entre utilizar br x30 y ret en la subrutina creada se puede atribuir al comportamiento de la predicción de saltos en los procesadores modernos. br x30 es una instrucción de salto directo, lo que significa que siempre va a la dirección almacenada en x30, y el procesador puede predecir fácilmente desde dónde se buscará la siguiente instrucción. En cambio, ret es una instrucción de salto indirecto que vuelve a la dirección almacenada en el registro de enlace x30. Dado que el valor exacto de x30 dependerá de la historia de ejecución del programa, la predicción de saltos se vuelve mucho más difícil para ret.

    Los procesadores modernos utilizan una variedad de técnicas para predecir el destino de los saltos indirectos, como mantener un historial de destinos de saltos anteriores y utilizar esa información para predecir futuros destinos. Sin embargo, esta predicción no es perfecta, y cuando la predicción es incorrecta, hay una penalización en el rendimiento ya que el procesador tiene que vaciar su tubería y volver a buscar las instrucciones desde el destino correcto.

    En la subrutina creada, el uso repetido de ret significa que el procesador tiene que predecir repetidamente el destino de un salto indirecto, lo que lleva a una penalización en el rendimiento. Cuando se utiliza en cambio br x30, no es necesario utilizar la predicción de saltos y se evita la penalización en el rendimiento.

    Vale la pena señalar que la disminución de velocidad observada en la subrutina creada puede no ser notable en código del mundo real, especialmente si hay otros saltos o instrucciones que permiten al procesador recuperarse de predicciones incorrectas. En general, es mejor utilizar la instrucción de salto adecuada para la situación y no depender de microbenchmarks para tomar decisiones de rendimiento.

Comments are closed.