De Wikipedia, la enciclopedia libre
Saltar a navegación Saltar a búsqueda

En la optimización del compilador , el análisis de escape es un método para determinar el alcance dinámico de los punteros  , donde en el programa se puede acceder a un puntero. Está relacionado con el análisis de punteros y el análisis de formas .

Cuando se asigna una variable (o un objeto) en una subrutina , un puntero a la variable puede escapar a otros hilos de ejecución oa subrutinas de llamada. Si una implementación usa optimización de llamadas de cola (generalmente requerida para lenguajes funcionales ), los objetos también pueden verse como escapando a subrutinas llamadas . Si un idioma admite continuaciones de primera clase (al igual que Scheme y Standard ML de Nueva Jersey ), es posible que también se escapen partes de la pila de llamadas .

Si una subrutina asigna un objeto y le devuelve un puntero, se puede acceder al objeto desde lugares indeterminados en el programa; el puntero se ha "escapado". Los punteros también pueden escapar si se almacenan en variables globales u otras estructuras de datos que, a su vez, escapan del procedimiento actual.

El análisis de escape determina todos los lugares donde se puede almacenar un puntero y si se puede probar que la vida útil del puntero está restringida solo al procedimiento y / o hilo actual.

Optimizaciones [ editar ]

Un compilador puede utilizar los resultados del análisis de escape como base para las optimizaciones: [1]

  • Conversión de asignaciones de pila en asignaciones de pila . [2] Si un objeto se asigna en una subrutina y un puntero al objeto nunca se escapa, el objeto puede ser un candidato para la asignación de pila en lugar de la asignación de montón. En los lenguajes de recolección de basura, esto puede reducir la frecuencia con la que debe ejecutarse el recolector.
  • Elisión de sincronización . Si se encuentra que un objeto es accesible desde un solo hilo, las operaciones en el objeto se pueden realizar sin sincronización.
  • Romper objetos o reemplazo escalar . [3] Se puede encontrar que se puede acceder a un objeto de maneras que no requieren que el objeto exista como una estructura de memoria secuencial. Esto puede permitir que partes (o todo) del objeto se almacenen en los registros de la CPU en lugar de en la memoria.

Consideraciones prácticas [ editar ]

En los lenguajes de programación orientados a objetos, los compiladores dinámicos son candidatos particularmente buenos para realizar análisis de escape. En la compilación estática tradicional, la anulación de métodos puede hacer que el análisis de escape sea imposible, ya que cualquier método llamado puede ser anulado por una versión que permita escapar a un puntero. Los compiladores dinámicos pueden realizar análisis de escape utilizando la información disponible sobre la sobrecarga y volver a realizar el análisis cuando la carga de código dinámico anula los métodos relevantes. [1]

La popularidad del lenguaje de programación Java ha hecho que el análisis de escape sea un objetivo de interés. La combinación de Java de asignación de objetos solo en montón, subprocesos integrados, el compilador dinámico Sun HotSpot y el compilador Just - In -Time (JIT) de OpenJ9 crea una plataforma candidata para optimizaciones relacionadas con el análisis de escape (consulte Análisis de escape en Java ). El análisis de escape se implementa en Java Standard Edition 6. Algunas JVM admiten una variante más fuerte de análisis de escape llamada análisis de escape parcial que hace posible el reemplazo escalar de un objeto asignado incluso si el objeto escapa en algunas rutas de una función. [4]

Ejemplo (Java) [ editar ]

class  Main  {  public  static  void  main ( String []  args )  {  example ();  }  ejemplo vacío estático público  () { Foo foo = new Foo (); // alloc Bar bar = new Bar (); // barra de asignación . setFoo ( foo ); } }                 clase  Foo  {}class  Bar  {  privado  Foo  foo ;  public  void  setFoo ( Foo  foo )  {  esto . foo  =  foo ;  } }

En este ejemplo, se crean dos objetos (comentados con alloc), y uno de ellos se da como argumento a un método de otro. El método setFoo()almacena una referencia a un objeto Foo recibido. Si el objeto Bar estaba en el montón, la referencia a Foo se escaparía. Pero en este caso, un compilador puede determinar, con análisis de escape, que el objeto Bar en sí mismo no escapa a la invocación de example(). Lo que implica que una referencia a Foo tampoco puede escapar. Entonces, el compilador puede asignar ambos objetos en la pila de manera segura.

Ejemplos (esquema) [ editar ]

En el siguiente ejemplo, el vector p no escapa a g , por lo que puede asignarse en la pila y luego eliminarse de la pila antes de llamar a g .

( define ( f  x )  ( let (( p  ( make-vector 10000 )))  ( fill-vector-with-good-stuff  p )  ( g  ( vector-ref p  7023 ))))

Sin embargo, si tuviéramos

( definir ( f  x )  ( dejar (( p  ( hacer-vector 10000 )))  ( llenar-vector-con-cosas buenas  p )  ( g  p )))

entonces p tendría que asignarse en el montón o (si el compilador conoce g cuando se compila f y se comporta bien) asignarse en la pila de tal manera que pueda permanecer en su lugar cuando se llame a g .

Si se utilizan continuaciones para implementar estructuras de control de tipo excepción, el análisis de escape a menudo puede detectar esto para evitar tener que asignar una continuación y copiar la pila de llamadas en ella. Por ejemplo, en

;; Lee los objetos de esquema ingresados ​​por el usuario. Si todos son números, ;; devuelve una lista que los contiene todos en orden. Si el usuario ingresa uno que ;; no es un número, inmediatamente devuelve #f. ( define ( getnumlist )  ( call / cc ( lambda ( continuación )  ( define ( get-numbers )  ( let (( next-object  ( read )))  ( cond  (( eof-object? next-object )  ' ())  ( ( número? siguiente-objeto ) ( contras next-object  ( get-numbers )))  ( else ( continuación  #f )))))  ( get-numbers ))))

El análisis de escape determinará que la continuación capturada por call / cc no escapa, por lo que no es necesario asignar una estructura de continuación, y la invocación de la continuación llamando a la continuación puede implementarse truncando la pila.

Ver también [ editar ]

  • Análisis de alias
  • Análisis de puntero
  • Análisis de formas

Referencias [ editar ]

  1. ^ a b T. Kotzmann y H. Mössenböck, “Escape analysis in the context of dynamic compilation and desoptimization”, en Actas de la 1ª conferencia internacional ACM / USENIX sobre entornos de ejecución virtual, Nueva York, NY, EE. UU., 2005, págs. 111–120.
  2. ^ Blanchet, Bruno (noviembre de 2003). "Análisis de escape para JavaTM: teoría y práctica". Transacciones ACM sobre lenguajes y sistemas de programación . 25 (6): 713–775. doi : 10.1145 / 945885.945886 . ISSN  0164-0925 .
  3. ^ Kotzmann, Thomas; Mössenböck, Hanspeter (marzo de 2007). Soporte en tiempo de ejecución para optimizaciones basadas en análisis de escape . Simposio Internacional sobre Generación y Optimización de Código (CGO'07) . págs. 49–60. CiteSeerX 10.1.1.394.5944 . doi : 10.1109 / CGO.2007.34 . ISBN  978-0-7695-2764-2.
  4. ^ Stadler, Lukas; Würthinger, Thomas; Mössenböck, Hanspeter (2014). "Análisis de escape parcial y reemplazo escalar para Java". Actas del Simposio Internacional Anual IEEE / ACM sobre Generación y Optimización de Código - CGO '14 . págs. 165-174. doi : 10.1145 / 2581122.2544157 . ISBN 9781450326704.