Análisis de escape

Determinación del alcance dinámico de los punteros

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

Cuando se asigna una variable (o un objeto) en una subrutina , un puntero a la variable puede escapar a otros subprocesos de ejecución o a subrutinas de llamada. Si una implementación utiliza optimización de llamadas de cola (normalmente requerida para lenguajes funcionales ), los objetos también pueden verse como escapando a subrutinas llamadas . Si un lenguaje admite continuaciones de primera clase (como lo hacen Scheme y Standard ML of New Jersey ), partes de la pila de llamadas también pueden escapar.

Si una subrutina asigna un objeto y devuelve un puntero a él, se puede acceder al objeto desde lugares indeterminados en el programa: el puntero se ha "escapado". Los punteros también pueden escapar si están almacenados 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 demostrar que la vida útil del puntero está restringida solo al procedimiento y/o hilo actual.

Optimizaciones

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

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

Consideraciones prácticas

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 que un puntero escape. Los compiladores dinámicos pueden realizar análisis de escape utilizando la información disponible sobre sobrecarga y volver a realizar el análisis cuando los métodos relevantes son anulados por la carga de código dinámico. [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 el montón, subprocesamiento integrado, el compilador dinámico Sun HotSpot y el compilador justo a tiempo (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 está implementado en Java Standard Edition 6. Algunas JVM admiten una variante más fuerte del análisis de escape llamada análisis de escape parcial que hace posible el reemplazo escalar de un objeto asignado incluso si el objeto se escapa en algunas rutas de una función. [4]

Ejemplo (Java)

clase  Main { public static void main ( String [] args ) { ejemplo (); } public static void ejemplo () { Foo foo = new Foo (); //asignar Bar bar = new Bar (); //asignar bar . setFoo ( foo ); } }                            clase  Foo {} clase  Bar { privada Foo foo ; pública void setFoo ( Foo foo ) { this . foo = foo ; } }             

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

Ejemplos (Esquema)

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 ) ( deja (( p ( crea-vector 10000 ))) ( llena-vector-con-cosas-buenas p ) ( g ( vector-ref p 7023 ))))            

Si, sin embargo, tuviéramos

( define ( f x ) ( deja (( p ( crea-vector 10000 ))) ( llena-vector-con-cosas-buenas p ) ( g p )))          

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

Si se utilizan continuaciones para implementar estructuras de control similares a excepciones, 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 introducidos por el usuario. Si todos son números, ;;devuelve una lista que los contiene todos en orden. Si el usuario introduce uno que ;;no es un número, devuelve inmediatamente #f. ( define ( getnumlist ) ( call/cc ( lambda ( continuation ) ( define ( get-numbers ) ( let (( next-object ( read ))) ( cond (( eof-object? next-object ) ' ()) (( number? next-object ) ( cons next-object ( get-numbers ))) ( else ( continuation #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 ninguna estructura de continuación, y la invocación de la continuación llamando a continuation se puede implementar desenrollando la pila.

Véase también

Referencias

  1. ^ ab T. Kotzmann y H. Mössenböck, “Análisis de escape en el contexto de compilación dinámica y desoptimización”, 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". ACM Transactions on Programming Languages ​​and Systems . 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) . pp. 49–60. CiteSeerX 10.1.1.394.5944 . doi :10.1109/CGO.2007.34. ISBN.  978-0-7695-2764-2.S2CID16011497  .
  4. ^ Stadler, Lukas; Würthinger, Thomas; Mössenböck, Hanspeter (2014). "Análisis de escape parcial y reemplazo escalar para Java". Actas del Simposio anual internacional IEEE/ACM sobre generación y optimización de código - CGO '14 . págs. 165–174. doi :10.1145/2581122.2544157. ISBN 9781450326704.
Obtenido de "https://es.wikipedia.org/w/index.php?title=Análisis_de_escape&oldid=1227806852"