fbpx
Wikipedia

Código enhebrado

En ciencias de la computación, el término código enhebrado se refiere a una técnica de implementación del compilador donde el código generado tiene una forma que esencialmente consiste enteramente en llamadas a subrutinas. El código puede ser procesado por un intérprete, o simplemente puede ser una secuencia de instrucciones de llamadas a código de máquina.

Una de las principales ventajas del código enhebrado es que es muy compacto, comparado al código generado por técnicas alternativas de generación del código y de convención de llamadas. Esta ventaja usualmente viene a expensas de una velocidad de ejecución ligeramente más lenta (usualmente apenas una sola instrucción de máquina). Sin embargo, a veces hay un efecto sinergético - a veces un código más compacto es más pequeño y significativamente más rápido que el código no enhebrado.[1]​ Un programa suficientemente pequeño para caber enteramente en la memoria de acceso aleatorio puede correr más rápido que un programa menos compacto en el espacio de intercambio que requiere un constante acceso mecánico de la unidad de disco, aunque sufra de la sobrecarga en la interpretación del código enhebrado. Similarmente, un programa lo suficientemente pequeño para caber enteramente en el caché del procesador de la computadora puede correr más rápido que un programa menos compacto que sufra fallas de caché constantes.

El código enhebrado es bien conocido como la técnica de implementación comúnmente usada en el lenguaje de programación Forth. También fue usado en las versiones tempranas del lenguaje de programación B, así como en muchas implementaciones de BASIC, y algunas implementaciones de COBOL y de otros lenguajes para pequeños microcomputadores.

Historia que llevó al código enhebrado

La manera común de hacer programas de computadora es usando un compilador para "traducir" un programa escrito en una cierto lenguaje simbólico al código de máquina. El código es típicamente rápido pero no es portable puesto que el código binario ejecutable es diseñado para una plataforma de hardware específica. Un acercamiento diferente usa un conjunto de instrucciones de una máquina virtual - que no tiene un destino particular de hardware. Un intérprete lo ejecuta en cada nuevo hardware de destino.

Los computadores tempranos tenían relativamente poca memoria. Por ejemplo, la mayoría de los Data General Nova, IBM 1130, y muchas computadores Apple II tenían solamente 4 k palabras de memoria RAM instalada. Consecuentemente se pasaba mucho tiempo intentando encontrar formas de reducir el tamaño de los programas de tal manera que pudieran caber en la memoria disponible. Al mismo tiempo, los computadores eran relativamente lentos, así que la interpretación simple era perceptiblemente mucho más lenta que ejecutar el código de máquina.

En vez de escribir cada paso de una operación en cada parte del programa donde era necesaria, los programadores ahorraban memoria escribiendo cada paso de tales operaciones una sola vez y poniéndolo en una subrutina (ver "no te repitas").

Este proceso —la refactorización de código— es usado hoy, aunque por diversas razones. En estos programas, la aplicación del nivel superior puede consistir de nada más que llamadas a subrutinas. A su vez, muchas de estas subrutinas, también no consisten nada más que llamadas a subrutinas de nivel inferior.

Los mainframes y algunos microprocesadores tempranos tales como el RCA 1802 requerían varias instrucciones para llamar a una subrutina. En la aplicación de nivel superior y en muchas subrutinas, esa secuencia es constantemente repetida, sólo cambiando la dirección de la subrutina desde una llamada a la siguiente. Usando la memoria para almacenar las mismas instrucciones repetidamente era un desperdicio.

La respuesta simple fue una tabla de saltos (es decir una tabla consistiendo solo en las direcciones contiguas de las subrutinas - usualmente extraídas usando un índice, un registro de propósitos generales o un puntero). Las direcciones pueden ser directas o indirectas, contiguas o no contiguas (encadenadas por punteros), relativas o absolutas, resueltas en de tiempo de compilación o construidas dinámicamente —pero el programa se convierte en una lista de puntos de entrada al código real a ser ejecutado—. Esta técnica "se ha reinventado" con los nombres de "código enhebrado", "tabla de despacho" o "tabla de método virtual" - todas estas técnicas llenan propósitos similares.

Desarrollo del código enhebrado

Para ahorrar espacio, los programadores exprimieron las listas códigos de llamadas a subrutinas y las convirtieron en simples listas con solo las direcciones de las subrutinas, y usaron un pequeño loop para llamar a cada subrutina una por una. Por ejemplo:

start:  thread: pushA: *sp++ = A tp = &thread &pushA  jump top top:  &pushB pushB: *sp++ = B jump *tp++ &add  jump top  ...  add: *sp++ = *--sp + *--sp    jump top 

En este caso, la decodificación de los bytecodes se realiza una sola vez, durante la compilación o la carga de programa, así que no es repetida cada vez que una instrucción es ejecutada. Esto puede ahorrar mucho tiempo y espacio cuando la sobrecarga por la decodificación (decode) y el enviar (dispath) es grande comparado al costo de la ejecución.

Observe, sin embargo, que las direcciones en thread para &pushA, &pushB, etc., tienen dos o más bytes, comparados a típicamente un byte, para el intérprete de decodificar (decode) y enviar (dispath) descrito arriba. En general las instrucciones para un intérprete de decodificar y enviar pueden ser de cualquier tamaño. Como ejemplo, un intérprete de decodificar y enviar para simular un Intel Pentium decodifica las instrucciones con un rango de 1 a 16 bytes. Sin embargo, los sistemas de bytecode eligen típicamente códigos de 1 byte para las operaciones más comunes. Así, el enhebrado a menudo tiene un costo de espacio más alto que los bytecodes. En la mayoría de los usos, la reducción en costo de decodificar compensa el aumento en costo de espacio.

Observe también que mientras que los bytecodes son nominalmente independientes de la máquina, el formato y el valor de los punteros en el enhebrado generalmente dependen de la máquina destino que está ejecutando al intérprete. Así, un intérprete puede cargar un programa bytecode portable, decodificar los bytecodes para generar código enhebrado independiente de la plataforma, luego ejecutar el código enhebrado sin referencia adicional a los bytecodes.

El loop es simple, así que está duplicado en cada handler, removiendo jump top de la lista de instrucciones de máquina necesarias para ejecutar cada instrucción del intérprete. Como ejemplo:

start:  thread: pushA: *sp++ = A tp = thread &pushA  jump *tp++ jump *tp++ &pushB pushB: *sp++ = B  &add  jump *tp++  ...  add: *sp++ = *--sp + *--sp    jump *tp++ 

Esto se llama código enhebrado directo (DTC). Aunque la técnica sea más vieja, el primer uso extensamente circulado del término "código enhebrado" es probablemente el artículo "código enhebrado" de Bell de 1973.[2]

En 1970, Charles H. Moore inventó una notación más compacta para su máquina virtual Forth: el código enhebrado indirecto (ITC). Originalmente, Moore inventó esto porque era fácil y rápido en los minicomputadores de NOVA, que tenían un bit de indirección en cada dirección. Moore dijo (en comentarios publicados en la edición de la revista Byte sobre Forth) que él encontró esto tan conveniente que lo propagó en todos los diseños Forth posteriores.

Algunos compiladores Forth compilan los programas Forth en código enhebrado directo, mientras que otros hacen código enhebrado indirecto. Los programas actúan igual de cualquier manera.

Modelos de enhebrado

Prácticamente todo el código enhebrado ejecutable usa uno u otro de estos métodos para invocar subrutinas (cada método es llamado un "modelo de enhebrado").

Enhebrado directo

Las direcciones en el enhebrado son las direcciones del lenguaje de máquina. Esta forma es simple, pero puede tener sobrecargas porque el enhebrado consiste solo de direcciones de máquina, así que todos los otros parámetros deben ser cargados indirectamente desde la memoria. Algunos sistemas Forth producen código enhebrado directo. En muchas máquinas el enhebrado directo es más rápido que el enhebrado de subrutina (ver la referencia abajo).

Como ejemplo, una máquina de pila puede ejecutar la secuencia "push A, push B, add". Eso puede ser traducido al enhebrado y rutinas siguientes, donde el tp es inicializado apuntando hacia la dirección de &thread.

thread: pushA: *sp++ = A pushB: *sp++ = B add: *sp++ = *--sp + *--sp &pushA jump *tp++  jump *tp++  jump *tp++ &pushB &add ... 

Alternativamente, los operandos pueden estar incluidos en el enhebrado. Esto puede remover alguna indirección necesaria arriba, pero hace el enhebrado más grande:

thread: push: *sp++ = *tp++ add: *sp++ = *--sp + *--sp &push jump *tp++  jump *tp++ &A &push &B &add 

Enhebrado indirecto

El enhebrado indirecto usa punteros que apuntan hacia localizaciones que a su vez apuntan al código de máquina. El puntero indirecto puede ser seguido por los operandos que son almacenados en el "bloque" indirecto en vez de estar almacenados repetidamente en el enhebrado. Así, el código indirecto es a menudo más compacto que el código enhebrado directo, pero la indirección típicamente también lo hace más lenta, aunque usualmente todavía más rápida que los intérpretes de bytecode. Donde los operandos del handler incluyen tanto valores como tipos, los ahorros de espacio sobre el código enhebrado directo pueden ser significativos. Los sistemas Forth más antiguos producían típicamente código enhebrado indirecto.

Como ejemplo, si la meta es ejecutar el "push A, push B, add", lo siguiente puede ser usado. Aquí, el tp es inicializado apuntando a la dirección &thread, cada fragmento del código (push, add) es encontrado por doble indirección por medio del tp; y los operandos para cada fragmento de código son encontrados en el primer nivel de indirección siguiendo la dirección del fragmento.

thread: i_pushA: push:  add: &i_pushA &push *sp++ = *(*tp + 1) *sp++ = *--sp + *--sp &i_pushB &A jump *(*tp++)  jump *(*tp++) &i_add i_pushB:  &push  &B  i_add:  &add 

Enhebrado de subrutina

El "código enhebrado de subrutina" (también llamado "código enhebrado de llamada") consiste en una serie de instrucciones "call" en lenguaje de máquina (o solo las direcciones de las funciones "call", en oposición el enhebrado directo el cual usa "jump"). Los compiladores tempranos para el ALGOL, FORTRAN, COBOL y algunos sistemas Forth produjeron a menudo código enhebrado de subrutina. El código en muchos de estos sistemas operaba una pila de operandos LIFO (last-in-firs-out), que tenía una bien desarrollada teoría del compilador. La mayoría de los procesadores modernos tienen soporte especial del hardware para las subrutinas con las instrucciones "call" y "return", así que la sobrecarga de una instrucción de máquina adicional por envío es disminuida; pero según mediciones de Antón Ertl, "en contraste con los mitos populares, el enhebrado de subrutina es usualmente más lento que el enhebrado directo". [3] Pruebas más recientes de Ertl demuestran que el enhebrado directo es el modelo de enhebrado más rápido en los procesadores Xeon, Opteron, y Athlon, mientras que el enhebrado indirecto es el modelo de enhebrado más rápido en procesadores Pentium M, y el enhebrado de subrutina es el modelo de enhebrado más rápido en el Pentium 4, el Pentium III, y procesadores PPC.

Como un ejemplo de enhebrado de llamada, el "push A, push B, add":

thread:  pushA: pushB: add: call pushA *sp++ = A *sp++ = B *sp++ = *--sp + *--sp call pushB ret  ret ret call add 

Enhebrado de token

El código enhebrado de token usa listas de índices de 8 o 12 bits a una tabla de punteros. El código enhebrado de token es notablemente compacto, sin mucho esfuerzo especial por un programador. Tiene usualmente entre la mitad y tres cuartas partes el tamaño de otros códigos enhebrados, los cuales a su vez tienen entre la cuarta y la octava parte del tamaño del código compilado. Los punteros de la tabla pueden ser tanto indirectos como directos. Algunos compiladores Forth producen código enhebrado de token. Algunos programadores consideran al "p-code" generado por algunos compiladores de Pascal, al igual que los bytecodes usados por .NET, Java, BASIC, y algunos compiladores C, como enhebrado de token.

Históricamente, un acercamiento común es el bytecode, que utiliza opcodes de 8 bits y, a menudo, una máquina virtual basada en pila. Un interpretador típico es conocido como "decode and dispatch interpreter" y sigue la forma:

bytecode: top:  pushA: pushB: add: 0 /*pushA*/ i = decode(vpc++) *sp++ = A *sp++ = B *sp++ = *--sp + *--sp 1 /*pushB*/ addr = table[i] jump top jump top jump top 2 /*add*/ jump *addr 

Si la máquina virtual usa solamente instrucciones del tamaño de un byte, el decode() es simplemente un ferch desde el bytecode, pero a menudo hay instrucciones comunes de 1 byte más instrucciones menos comunes de múltiples bytes (ver CISC), en este caso, decode() es más complejo. La decodificación de opcodes de un solo byte puede ser muy simple y eficientemente manejado por una tabla de saltos usando el opcode directamente como un índice.

Para, en las instrucciones donde las operaciones individuales son simples, por ejemplo "push" y "add", la sobrecarga implicada en decidir lo que se debe ejecutar es más grande que el costo real de la ejecución, tales intérpretes son a menudo mucho más lentos que el código de máquina. Sin embargo para instrucciones ("compuestas") más complejas, el porcentaje de sobrecarga es proporcionalmente menos significativo. [3]

Enhebrado de Huffman

El código enhebrado de Huffman consiste en listas de códigos Huffman. Un código de Huffman es una cadena de bits de longitud variable usada para identificar un elemento único. Un intérprete de enhebrado de Huffman localiza las subrutinas usando una tabla de índice o un árbol de punteros que pueden ser navagados por el código Huffman. El código enhebrado de Huffman es una de las más compactas representaciones conocidas para un programa de computadora. Básicamente el índice y los códigos están organizados midiendo la frecuencia en que cada subrutina ocurre en el código. A las llamadas frecuentes se le dan los códigos más cortos. Las operaciones con frecuencias aproximadamente iguales se le dan códigos con longitudes de bits casi iguales. La mayoría de los sistemas enhebrados de Huffman han sido implementados como sistemas Forth de enhebrado directo, y son usados para grandes cantidades de paquetes de código corriendo lentamente dentro de pequeños y baratos microcontroladores. La mayoría de las aplicaciones publicadas han estado en juguetes, calculadoras o relojes.

Enhebrados menos usados

  • Enhebrado de cadena, donde las operaciones son identificadas por cadenas, usualmente buscados en una tabla hash. Esto fue usado por Charles H. Moore en las implementaciones más tempranas de Forth y en el lenguaje de computadora experimental interpretado por hardware de la Universidad Illinois. También se utiliza en Bashforth.

Bifurcaciones

Los ejemplos de arriba no muestran bifurcaciones. Para todos los intérpretes, una bifurcación cambia el puntero del enhebrado (arriba indicado como tp). Como ejemplo, una bifurcación condicional cuando el valor tope de la pila es cero puede ser codificada como sigue. Observe que el &thread[123] es la localización a saltar (jump), no la dirección de un handler, y así que debe ser saltada (tp++) independientemente de si la bifurcación es tomada.

thread:  brz: ...  tmp = *tp++ &brz  if (*sp++ == 0) &thread[123]  tp = tmp ...  jump *tp++ 

Amenidades comunes

En una máquina, la separación de las pilas de datos y de retorno elimina mucho del código para el manejo de la pila, reduciendo substancialmente el tamaño del código enhebrado. El principio de la doble pila fue originado tres veces independientemente: para los grandes sistemas de Burroughs, el Forth y el PostScript, y es usado en algunas máquinas virtuales de Java.

Tres registros están a menudo presentes en una máquina virtual enhebrada. Otro existe para pasar datos entre las subrutinas ("palabras"). Estos son:

  • IP o i (puntero de instrucción); llamado tp en los ejemplos de arriba
  • w (puntero de trabajo)
  • rp o r (puntero de pila de retorno)
  • SP o s (puntero de pila de parámetros, usado para pasar parámetros entre las palabras)

A menudo, las máquinas virtuales enhebradas tales como las implementaciones de Forth tienen una máquina virtual simple en su corazón, consistiendo en tres primitivas. Esas son:

  • nest, también llamado docol
  • unnest, o semi_s (; s)
  • next

En una máquina virtual de enhebrado indirecto, la que está dada aquí, las operaciones es:

next: (ip)+ -> w  ; jmp (w)+ nest: ip -> -(rp)  ; w -> ip  ; next unnest: (rp)+ -> ip  ; next 

Éste es quizás el intérprete más simple y más rápido o máquina virtual.

Referencias

  1. Speed of various interpreter dispatch techniques V2
  2. James R. Bell, "Threaded Code", CACM, 1973, 16, 6, pp 370–372
  3. Enciclopedia detallada de todos los tipos de códigos DTC

Lectura adicional

  • Anton Ertl's explanatory page What is Threaded Code? describes different threading techniques and provides further references.
  • The Development of the C Language by Dennis M. Ritchie describes B (a precursor of C) as implemented using "threaded code".
  • includes the seminal (but out of print) book Thinking Forth by published in 1984.
  • Starting FORTH online version of the book Starting FORTH by published in 1981.
  • Brad Rodriguez's covers threading techniques in depth.
  • Historia de los CPU de propósito general
  • GCC extensions. Labels as Values

Véase también

  •   Datos: Q2491860

código, enhebrado, ciencias, computación, término, código, enhebrado, refiere, técnica, implementación, compilador, donde, código, generado, tiene, forma, esencialmente, consiste, enteramente, llamadas, subrutinas, código, puede, procesado, intérprete, simplem. En ciencias de la computacion el termino codigo enhebrado se refiere a una tecnica de implementacion del compilador donde el codigo generado tiene una forma que esencialmente consiste enteramente en llamadas a subrutinas El codigo puede ser procesado por un interprete o simplemente puede ser una secuencia de instrucciones de llamadas a codigo de maquina Una de las principales ventajas del codigo enhebrado es que es muy compacto comparado al codigo generado por tecnicas alternativas de generacion del codigo y de convencion de llamadas Esta ventaja usualmente viene a expensas de una velocidad de ejecucion ligeramente mas lenta usualmente apenas una sola instruccion de maquina Sin embargo a veces hay un efecto sinergetico a veces un codigo mas compacto es mas pequeno y significativamente mas rapido que el codigo no enhebrado 1 Un programa suficientemente pequeno para caber enteramente en la memoria de acceso aleatorio puede correr mas rapido que un programa menos compacto en el espacio de intercambio que requiere un constante acceso mecanico de la unidad de disco aunque sufra de la sobrecarga en la interpretacion del codigo enhebrado Similarmente un programa lo suficientemente pequeno para caber enteramente en el cache del procesador de la computadora puede correr mas rapido que un programa menos compacto que sufra fallas de cache constantes El codigo enhebrado es bien conocido como la tecnica de implementacion comunmente usada en el lenguaje de programacion Forth Tambien fue usado en las versiones tempranas del lenguaje de programacion B asi como en muchas implementaciones de BASIC y algunas implementaciones de COBOL y de otros lenguajes para pequenos microcomputadores Indice 1 Historia que llevo al codigo enhebrado 2 Desarrollo del codigo enhebrado 3 Modelos de enhebrado 3 1 Enhebrado directo 3 2 Enhebrado indirecto 3 3 Enhebrado de subrutina 3 4 Enhebrado de token 3 5 Enhebrado de Huffman 3 6 Enhebrados menos usados 4 Bifurcaciones 5 Amenidades comunes 6 Referencias 7 Lectura adicional 8 Vease tambienHistoria que llevo al codigo enhebrado EditarLa manera comun de hacer programas de computadora es usando un compilador para traducir un programa escrito en una cierto lenguaje simbolico al codigo de maquina El codigo es tipicamente rapido pero no es portable puesto que el codigo binario ejecutable es disenado para una plataforma de hardware especifica Un acercamiento diferente usa un conjunto de instrucciones de una maquina virtual que no tiene un destino particular de hardware Un interprete lo ejecuta en cada nuevo hardware de destino Los computadores tempranos tenian relativamente poca memoria Por ejemplo la mayoria de los Data General Nova IBM 1130 y muchas computadores Apple II tenian solamente 4 k palabras de memoria RAM instalada Consecuentemente se pasaba mucho tiempo intentando encontrar formas de reducir el tamano de los programas de tal manera que pudieran caber en la memoria disponible Al mismo tiempo los computadores eran relativamente lentos asi que la interpretacion simple era perceptiblemente mucho mas lenta que ejecutar el codigo de maquina En vez de escribir cada paso de una operacion en cada parte del programa donde era necesaria los programadores ahorraban memoria escribiendo cada paso de tales operaciones una sola vez y poniendolo en una subrutina ver no te repitas Este proceso la refactorizacion de codigo es usado hoy aunque por diversas razones En estos programas la aplicacion del nivel superior puede consistir de nada mas que llamadas a subrutinas A su vez muchas de estas subrutinas tambien no consisten nada mas que llamadas a subrutinas de nivel inferior Los mainframes y algunos microprocesadores tempranos tales como el RCA 1802 requerian varias instrucciones para llamar a una subrutina En la aplicacion de nivel superior y en muchas subrutinas esa secuencia es constantemente repetida solo cambiando la direccion de la subrutina desde una llamada a la siguiente Usando la memoria para almacenar las mismas instrucciones repetidamente era un desperdicio La respuesta simple fue una tabla de saltos es decir una tabla consistiendo solo en las direcciones contiguas de las subrutinas usualmente extraidas usando un indice un registro de propositos generales o un puntero Las direcciones pueden ser directas o indirectas contiguas o no contiguas encadenadas por punteros relativas o absolutas resueltas en de tiempo de compilacion o construidas dinamicamente pero el programa se convierte en una lista de puntos de entrada al codigo real a ser ejecutado Esta tecnica se ha reinventado con los nombres de codigo enhebrado tabla de despacho o tabla de metodo virtual todas estas tecnicas llenan propositos similares Desarrollo del codigo enhebrado EditarPara ahorrar espacio los programadores exprimieron las listas codigos de llamadas a subrutinas y las convirtieron en simples listas con solo las direcciones de las subrutinas y usaron un pequeno loop para llamar a cada subrutina una por una Por ejemplo start thread pushA sp A tp amp thread amp pushA jump top top amp pushB pushB sp B jump tp amp add jump top add sp sp sp jump top En este caso la decodificacion de los bytecodes se realiza una sola vez durante la compilacion o la carga de programa asi que no es repetida cada vez que una instruccion es ejecutada Esto puede ahorrar mucho tiempo y espacio cuando la sobrecarga por la decodificacion decode y el enviar dispath es grande comparado al costo de la ejecucion Observe sin embargo que las direcciones en thread para amp pushA amp pushB etc tienen dos o mas bytes comparados a tipicamente un byte para el interprete de decodificar decode y enviar dispath descrito arriba En general las instrucciones para un interprete de decodificar y enviar pueden ser de cualquier tamano Como ejemplo un interprete de decodificar y enviar para simular un Intel Pentium decodifica las instrucciones con un rango de 1 a 16 bytes Sin embargo los sistemas de bytecode eligen tipicamente codigos de 1 byte para las operaciones mas comunes Asi el enhebrado a menudo tiene un costo de espacio mas alto que los bytecodes En la mayoria de los usos la reduccion en costo de decodificar compensa el aumento en costo de espacio Observe tambien que mientras que los bytecodes son nominalmente independientes de la maquina el formato y el valor de los punteros en el enhebrado generalmente dependen de la maquina destino que esta ejecutando al interprete Asi un interprete puede cargar un programa bytecode portable decodificar los bytecodes para generar codigo enhebrado independiente de la plataforma luego ejecutar el codigo enhebrado sin referencia adicional a los bytecodes El loop es simple asi que esta duplicado en cada handler removiendo jump top de la lista de instrucciones de maquina necesarias para ejecutar cada instruccion del interprete Como ejemplo start thread pushA sp A tp thread amp pushA jump tp jump tp amp pushB pushB sp B amp add jump tp add sp sp sp jump tp Esto se llama codigo enhebrado directo DTC Aunque la tecnica sea mas vieja el primer uso extensamente circulado del termino codigo enhebrado es probablemente el articulo codigo enhebrado de Bell de 1973 2 En 1970 Charles H Moore invento una notacion mas compacta para su maquina virtual Forth el codigo enhebrado indirecto ITC Originalmente Moore invento esto porque era facil y rapido en los minicomputadores de NOVA que tenian un bit de indireccion en cada direccion Moore dijo en comentarios publicados en la edicion de la revista Byte sobre Forth que el encontro esto tan conveniente que lo propago en todos los disenos Forth posteriores Algunos compiladores Forth compilan los programas Forth en codigo enhebrado directo mientras que otros hacen codigo enhebrado indirecto Los programas actuan igual de cualquier manera Modelos de enhebrado EditarPracticamente todo el codigo enhebrado ejecutable usa uno u otro de estos metodos para invocar subrutinas cada metodo es llamado un modelo de enhebrado Enhebrado directo Editar Las direcciones en el enhebrado son las direcciones del lenguaje de maquina Esta forma es simple pero puede tener sobrecargas porque el enhebrado consiste solo de direcciones de maquina asi que todos los otros parametros deben ser cargados indirectamente desde la memoria Algunos sistemas Forth producen codigo enhebrado directo En muchas maquinas el enhebrado directo es mas rapido que el enhebrado de subrutina ver la referencia abajo Como ejemplo una maquina de pila puede ejecutar la secuencia push A push B add Eso puede ser traducido al enhebrado y rutinas siguientes donde el tp es inicializado apuntando hacia la direccion de amp thread thread pushA sp A pushB sp B add sp sp sp amp pushA jump tp jump tp jump tp amp pushB amp add Alternativamente los operandos pueden estar incluidos en el enhebrado Esto puede remover alguna indireccion necesaria arriba pero hace el enhebrado mas grande thread push sp tp add sp sp sp amp push jump tp jump tp amp A amp push amp B amp add Enhebrado indirecto Editar El enhebrado indirecto usa punteros que apuntan hacia localizaciones que a su vez apuntan al codigo de maquina El puntero indirecto puede ser seguido por los operandos que son almacenados en el bloque indirecto en vez de estar almacenados repetidamente en el enhebrado Asi el codigo indirecto es a menudo mas compacto que el codigo enhebrado directo pero la indireccion tipicamente tambien lo hace mas lenta aunque usualmente todavia mas rapida que los interpretes de bytecode Donde los operandos del handler incluyen tanto valores como tipos los ahorros de espacio sobre el codigo enhebrado directo pueden ser significativos Los sistemas Forth mas antiguos producian tipicamente codigo enhebrado indirecto Como ejemplo si la meta es ejecutar el push A push B add lo siguiente puede ser usado Aqui el tp es inicializado apuntando a la direccion amp thread cada fragmento del codigo push add es encontrado por doble indireccion por medio del tp y los operandos para cada fragmento de codigo son encontrados en el primer nivel de indireccion siguiendo la direccion del fragmento thread i pushA push add amp i pushA amp push sp tp 1 sp sp sp amp i pushB amp A jump tp jump tp amp i add i pushB amp push amp B i add amp add Enhebrado de subrutina Editar El codigo enhebrado de subrutina tambien llamado codigo enhebrado de llamada consiste en una serie de instrucciones call en lenguaje de maquina o solo las direcciones de las funciones call en oposicion el enhebrado directo el cual usa jump Los compiladores tempranos para el ALGOL FORTRAN COBOL y algunos sistemas Forth produjeron a menudo codigo enhebrado de subrutina El codigo en muchos de estos sistemas operaba una pila de operandos LIFO last in firs out que tenia una bien desarrollada teoria del compilador La mayoria de los procesadores modernos tienen soporte especial del hardware para las subrutinas con las instrucciones call y return asi que la sobrecarga de una instruccion de maquina adicional por envio es disminuida pero segun mediciones de Anton Ertl en contraste con los mitos populares el enhebrado de subrutina es usualmente mas lento que el enhebrado directo 3 Pruebas mas recientes de Ertl demuestran que el enhebrado directo es el modelo de enhebrado mas rapido en los procesadores Xeon Opteron y Athlon mientras que el enhebrado indirecto es el modelo de enhebrado mas rapido en procesadores Pentium M y el enhebrado de subrutina es el modelo de enhebrado mas rapido en el Pentium 4 el Pentium III y procesadores PPC Como un ejemplo de enhebrado de llamada el push A push B add thread pushA pushB add call pushA sp A sp B sp sp sp call pushB ret ret ret call add Enhebrado de token Editar El codigo enhebrado de token usa listas de indices de 8 o 12 bits a una tabla de punteros El codigo enhebrado de token es notablemente compacto sin mucho esfuerzo especial por un programador Tiene usualmente entre la mitad y tres cuartas partes el tamano de otros codigos enhebrados los cuales a su vez tienen entre la cuarta y la octava parte del tamano del codigo compilado Los punteros de la tabla pueden ser tanto indirectos como directos Algunos compiladores Forth producen codigo enhebrado de token Algunos programadores consideran al p code generado por algunos compiladores de Pascal al igual que los bytecodes usados por NET Java BASIC y algunos compiladores C como enhebrado de token Historicamente un acercamiento comun es el bytecode que utiliza opcodes de 8 bits y a menudo una maquina virtual basada en pila Un interpretador tipico es conocido como decode and dispatch interpreter y sigue la forma bytecode top pushA pushB add 0 pushA i decode vpc sp A sp B sp sp sp 1 pushB addr table i jump top jump top jump top 2 add jump addr Si la maquina virtual usa solamente instrucciones del tamano de un byte el decode es simplemente un ferch desde el bytecode pero a menudo hay instrucciones comunes de 1 byte mas instrucciones menos comunes de multiples bytes ver CISC en este caso decode es mas complejo La decodificacion de opcodes de un solo byte puede ser muy simple y eficientemente manejado por una tabla de saltos usando el opcode directamente como un indice Para en las instrucciones donde las operaciones individuales son simples por ejemplo push y add la sobrecarga implicada en decidir lo que se debe ejecutar es mas grande que el costo real de la ejecucion tales interpretes son a menudo mucho mas lentos que el codigo de maquina Sin embargo para instrucciones compuestas mas complejas el porcentaje de sobrecarga es proporcionalmente menos significativo 3 Enhebrado de Huffman Editar El codigo enhebrado de Huffman consiste en listas de codigos Huffman Un codigo de Huffman es una cadena de bits de longitud variable usada para identificar un elemento unico Un interprete de enhebrado de Huffman localiza las subrutinas usando una tabla de indice o un arbol de punteros que pueden ser navagados por el codigo Huffman El codigo enhebrado de Huffman es una de las mas compactas representaciones conocidas para un programa de computadora Basicamente el indice y los codigos estan organizados midiendo la frecuencia en que cada subrutina ocurre en el codigo A las llamadas frecuentes se le dan los codigos mas cortos Las operaciones con frecuencias aproximadamente iguales se le dan codigos con longitudes de bits casi iguales La mayoria de los sistemas enhebrados de Huffman han sido implementados como sistemas Forth de enhebrado directo y son usados para grandes cantidades de paquetes de codigo corriendo lentamente dentro de pequenos y baratos microcontroladores La mayoria de las aplicaciones publicadas han estado en juguetes calculadoras o relojes Enhebrados menos usados Editar Enhebrado de cadena donde las operaciones son identificadas por cadenas usualmente buscados en una tabla hash Esto fue usado por Charles H Moore en las implementaciones mas tempranas de Forth y en el lenguaje de computadora experimental interpretado por hardware de la Universidad Illinois Tambien se utiliza en Bashforth Bifurcaciones EditarLos ejemplos de arriba no muestran bifurcaciones Para todos los interpretes una bifurcacion cambia el puntero del enhebrado arriba indicado como tp Como ejemplo una bifurcacion condicional cuando el valor tope de la pila es cero puede ser codificada como sigue Observe que el amp thread 123 es la localizacion a saltar jump no la direccion de un handler y asi que debe ser saltada tp independientemente de si la bifurcacion es tomada thread brz tmp tp amp brz if sp 0 amp thread 123 tp tmp jump tp Amenidades comunes EditarEn una maquina la separacion de las pilas de datos y de retorno elimina mucho del codigo para el manejo de la pila reduciendo substancialmente el tamano del codigo enhebrado El principio de la doble pila fue originado tres veces independientemente para los grandes sistemas de Burroughs el Forth y el PostScript y es usado en algunas maquinas virtuales de Java Tres registros estan a menudo presentes en una maquina virtual enhebrada Otro existe para pasar datos entre las subrutinas palabras Estos son IP o i puntero de instruccion llamado tp en los ejemplos de arriba w puntero de trabajo rp o r puntero de pila de retorno SP o s puntero de pila de parametros usado para pasar parametros entre las palabras A menudo las maquinas virtuales enhebradas tales como las implementaciones de Forth tienen una maquina virtual simple en su corazon consistiendo en tres primitivas Esas son nest tambien llamado docol unnest o semi s s nextEn una maquina virtual de enhebrado indirecto la que esta dada aqui las operaciones es next ip gt w jmp w nest ip gt rp w gt ip next unnest rp gt ip next Este es quizas el interprete mas simple y mas rapido o maquina virtual Referencias Editar Speed of various interpreter dispatch techniques V2 James R Bell Threaded Code CACM 1973 16 6 pp 370 372 Enciclopedia detallada de todos los tipos de codigos DTCLectura adicional EditarAnton Ertl s explanatory page What is Threaded Code describes different threading techniques and provides further references The Development of the C Language by Dennis M Ritchie describes B a precursor of C as implemented using threaded code Thinking Forth Project includes the seminal but out of print book Thinking Forth by Leo Brodie published in 1984 Starting FORTH online version of the book Starting FORTH by Leo Brodie published in 1981 Brad Rodriguez s Moving FORTH Part 1 Design Decisions in the Forth Kernel covers threading techniques in depth Historia de los CPU de proposito general GCC extensions Labels as ValuesVease tambien EditarCompilacion en tiempo de ejecucion Tabla de saltos Forth Datos Q2491860Obtenido de https es wikipedia org w index php title Codigo enhebrado amp oldid 134851485, wikipedia, wiki, leyendo, leer, libro, biblioteca,

español

, española, descargar, gratis, descargar gratis, mp3, video, mp4, 3gp, jpg, jpeg, gif, png, imagen, música, canción, película, libro, juego, juegos