Generaci´on de Trazas Multihilo Simultáneo

Anuncio
VI SIMPOSIO ARGENTINO DE TECNOLOGIA EN COMPUTACION: AST-PAR002
1
Generación de Trazas Multihilo Simultáneo
Augusto J. Vega, José L. Hamkalo y Bruno Cernuschi Frı́as
Facultad de Ingenierı́a, Universidad de Buenos Aires, {ajvega,jhamkal}@fi.uba.ar
Resumen— En este trabajo se presenta una herramienta
para la generación y captura de trazas de aplicaciones multihilo, para su uso en simulación de memorias cache en ambientes
multihilo simultáneo. El ambiente desarrollado es construido a partir de modificaciones aplicadas a una herramienta
de debugging y profiling llamada Valgrind. La herramienta
utiliza como entrada el código ejecutable de la aplicación
a ser procesada, la cual es traducida e instrumentada en
forma dinámica. El ambiente propuesto permite la captura de
trazas de aplicaciones paralelas reales, incluyendo aplicaciones
comerciales de las cuales no se dispone del código fuente. Se
obtuvo una granularidad muy fina, en la práctica de hasta una
referencia por cada conmutación de hilo de ejecución activo.
Se implementó un mecanismo para el almacenamiento de las
trazas en forma comprimida y con un formato simple.
Palabras clave— Traza, Multihilo, Cache.
I. I NTRODUCCI ÓN
En los últimos años, una nueva arquitectura de procesadores conocida como multihilo simultáneo o SMT (Simultaneous Multithreading) [2], ha crecido en relevancia y
ha sido incorporada en los microprocesadores actuales, tales
como los procesadores Intel Pentium IV HT [3]. Dado que es
una tecnologı́a muy reciente, son muy escasas o inexistentes
en la práctica, herramientas que simulen o aporten los datos
necesarios para realizar simulaciones de estos ambientes
multihilo simultáneo. En este trabajo presentamos una forma para la obtención de los mencionados datos y la forma
de utilizarlos para la emulación de ambientes SMT en la
simulación de jerarquı́as de memorias. El ambiente propuesto se ha construido a partir de modificaciones aplicadas
a una herramienta de debugging y profiling de dominio
público, llamada Valgrind [14].
A.
El procesamiento multihilo simultáneo (SMT)
Una arquitectura uniprocesador tradicional (por ejemplo,
superescalar) explota el paralelismo a nivel de instrucción
(ILP) [7] para lograr un mejor desempeño en la ejecución de
los procesos. Sin embargo, este grado de paralelismo suele
ser pobre en la mayorı́a de los casos, dando como resultado un desperdicio en el aprovechamiento de los recursos
del procesador. En adición al problema del bajo nivel de
ILP, un procesador superescalar también se caracteriza por
desperdiciar capacidad de cómputo, permaneciendo durante
algunos ciclos de reloj en estado ocioso. En ocasiones,
la ejecución del flujo de instrucciones suele bloquearse
debido a, entre otras cosas, malas predicciones de los
saltos, desaciertos en la memoria caché de instrucciones,
operaciones de E/S, etc., lo que se traduce indefectiblemente
en un procesador ocioso, a la espera de una instrucción a
ejecutar. Resumiendo, un procesador puede sufrir de:
Desperdicio Horizontal: debido al bajo nivel de ILP
que impide, para un ciclo de reloj, usar eficientemente
los recursos (unidades funcionales) disponibles.
Desperdicio Vertical: debido al bloqueo o latencias en
las instrucciones ejecutadas, con lo cual, el procesador
debe permanecer ocioso.
Desde hace varios años, los sistemas se software se
basan en la utilización de múltiples procesos livianos o
hilos de ejecución. En este caso, existen varios flujos de
instrucciones a ejecutar que comparten un mismo espacio
de direcciones. El uso de un procesador superescalar como
el descrito anteriormente resulta en un serio cuello de
botella, ya que la ejecución de los hilos es serializada
en el mismo. Para aprovechar el paralelismo de los sistemas multihilo, se introdujeron modificaciones a dichos
procesadores, de manera tal que sean capaces de tomar
instrucciones de los diferentes hilos, e intercalar la ejecución
de las mismas. Ası́, surgieron dos esquemas, conocidos como Coarse-Grained Multithreading (CMT) y Fine-Grained
Multithreading (FMT). En el primer caso, la ejecución de
los flujos se alterna cuando el que estaba en ejecución
incurre en alguna penalidad, tal como en el caso de un
desacierto en la memoria caché L2 [8], mientras que en el
esquema FMT, los flujos se alternan en cada instrucción
ejecutada. El objetivo de estas polı́ticas consiste en reducir
el desperdicio de ciclos de reloj: si un flujo se bloquea,
entonces el procesador realiza un cambio de contexto y
continúa con la ejecución del otro flujo. Sin embargo,
solo resuelven un problema (desperdicio vertical), pero no
mejoran el uso de las unidades funcionales por cada ciclo
de reloj.
En los últimos años se propuso un nuevo esquema, que
es en sı́ mismo una variación del FMT, y que se llamó SMT
(Simultaneous Multithreading). La solución planteada consiste en reducir los desperdicios de tiempo y uso de recursos
que señalamos anteriormente (o sea, se busca minimizar
tanto el desperdicio horizontal como también el desperdicio
vertical). Para evitar que el procesador permanezca ocioso,
las instrucciones a ejecutar se toman de varios flujos simultáneamente, de manera tal que, ante el bloqueo de uno
de ellos, se pueda continuar con la ejecución de alguno de
los otros. En realidad, la anterior ya es una caracterı́stica de
la polı́tica FMT. La diferencia está en que un esquema SMT
busca maximizar, para cada ciclo de reloj, la utilización de
los recursos del procesador. Si el flujo (o hilo) en ejecución
presenta un alto nivel de ILP, entonces ese paralelismo
permite explotar al máximo la utilización de las unidades
funcionales para un ciclo de reloj. Por otra parte, si varios
flujos presentan cada uno un bajo nivel de ILP, entonces
pueden ser ejecutados simultáneamente, para maximizar el
aprovechamiento de los recursos.
B. Los procesadores de alta performance y el sistema de
memoria
Las memorias caché han jugado un rol central en el
aumento sostenido de la performance de las computadoras
del alto rendimiento [6]. Las mejoras en la arquitectura
del procesador y la tecnologı́a han contribuido en forma
pareja durante las últimas dos décadas como las fuerzas
principales para conseguir niveles sin precedentes en el
VI SIMPOSIO ARGENTINO DE TECNOLOGIA EN COMPUTACION: AST-PAR002
funcionamiento de los procesadores de propósito general
[8]. Los procesadores de hoy en dı́a utilizan pipelines muy
profundos, implementándose con tecnologı́as de integración
submicrón decrecientes y como ya se expuso anteriormente
son capaces de manejar múltiples hilos de ejecución en
forma simultánea. Esto resulta en tasas de reloj muy altas
y en aumento, donde por cada ciclo de reloj son requeridas
múltiples instrucciones y datos del sistema de memoria. Se
ejerce ası́ una alta presión, también en aumento, sobre el
subsistema de memoria caché [15]. Esta presión demanda
un muy alto grado de eficiencia en el sistema de memoria
caché, siendo necesario continuas mejoras las cuales se
basan en nuevos esquemas, estrategias y tecnologı́as.
Los distintos parámetros de una memoria caché actúan
en forma interrelacionada y compleja sobre la performance
de la misma. Pequeños cambios en la estructura de la
memoria caché, pueden resultar en sensibles cambios en el
desempeño de los sistemas de alta performance [9]. Es por
ello que previo a la adopción de un diseño en hardware se
realicen primero simulaciones por computadora extensivas.
En dichas simulaciones se consideran no solo los aspectos
del hardware sino también el software de las aplicaciones
que resultan representativas del sistema bajo desarrollo. Un
método usual de simulación consiste en la escritura de un
programa que simule el comportamiento de la memoria
caché propuesta y luego aplicar al simulador una secuencia
de referencias a memoria que reproducen la forma en que
el procesador real podrı́a ejercitar el diseño considerado. La
secuencia de direcciones a memoria es extraı́da de un archivo o conjunto de archivos comúnmente llamados traza y el
método de simulación es llamado simulación de memoria
caché manejado por trazas [13]. Aunque es conceptualmente
simple existen un cierto número de factores que hacen el
método de simulación por trazas dificultoso en la práctica.
Algunos de estos factores son la dificultad para la colección
de la traza, el tipo de información que se colecta y los
grandes tamaños de los archivos que contienen la traza
[1], [4]. Finalmente el tiempo de simulación para consumir
una traza puede resultar muy significativo. Salvados estos
inconvenientes, el método de simulación por traza resulta
ser muy efectivo y es usado extensivamente en la mayorı́a
de los trabajos de investigación sobre memorias caché.
C.
Trazas de aplicaciones multihilo simultáneo
El estudio de una organización de memoria caché optimizada para un ambiente SMT, crea la necesidad de
contar con trazas que reflejen el paralelismo real existente
entre los hilos, i.e. contar con las múltiples referencias a
memoria por ciclo de reloj, llevadas a cabo por los hilos
activos en cada fase de ejecución de la aplicación. En la
actualidad no se encuentran disponibles públicamente trazas
de las mencionadas caracterı́sticas. Más aún, no existen herramientas capaces de generar las trazas requeridas. Tullsen,
en uno de sus artı́culos seminales sobre las arquitecturas
SMT [12], propone el uso de los benchmarks SPEC [11]
para representar los hilos de ejecución. La metodologı́a
propuesta por Tullsen puede resultar adecuada para analizar
los casos donde las referencias a memoria provienen de
hilos totalmente independientes, pero no reflejan la situación
de las aplicaciones paralelas reales, con sus distintos grados
de concurrencia, datos compartidos y exclusivos para cada
hilo, bloqueos y sincronización de hilos, etc. El presente
2
Programa
de usuario
Instrucciones
x86
UCode
Módulo
(skin)
Coregrind
VALGRIND
Instrucciones
x86
UCode
(instrumentado)
Plataforma
de base
Fig. 1. Interacción Valgrind-programa usuario.
trabajo tiene como principal objetivo realizar una contribución en este sentido, logrando la capacidad de generar
trazas de aplicaciones multihilo reales para su posterior
uso, principalmente en el estudio de nuevos esquemas de
memoria caché. Dicho objetivo es llevado a cabo mediante
un conjunto de modificaciones aplicadas a un ambiente de
debugging y profiling de dominio público llamado Valgrind
[14].
II.
EL
SISTEMA
VALGRIND
Valgrind es una herramienta que permite realizar tareas de debugging y profiling sobre programas ejecutables,
para ambientes Linux-x86. Básicamente, consiste en una
máquina virtual, que implementa un procesador sintético
x86, y sobre la cual se ejecutan programas de usuario. Este
diseño constituye a Valgrind en una capa adicional, que
se inserta entre el programa de usuario y la arquitectura de
base (ver figura 1), permitiéndole, entre otras cosas, tener un
control total de las referencias a memoria que se efectúan.
Cabe aclarar que los programas de usuario que corran sobre
Valgrind no necesitan ser recompilados ni revinculados con
bibliotecas externas.
A.
Diseño general
El sistema Valgrind está construido en base a un diseño
modular, en torno a un núcleo, que realiza el trabajo más
pesado. Este módulo central, llamado coregrind, implementa un procesador x86 sintético, e interactúa con otros
módulos (skins ó tools), para brindar diferentes funcionalidades (detectar problemas de memoria, verificar la ejecución
concurrente de hilos, simular memorias caché, etc.).
La interacción de coregrind con los demás módulos se
produce de la siguiente manera. Cuando se ejecuta un
programa de usuario sobre Valgrind, el módulo central
toma el control del mismo, lee el bloque de código a
ejecutar y lo pasa al módulo correspondiente. Este módulo,
instrumenta [5], [16] el bloque de código recibido, y se
lo retorna a coregrind. Posteriormente, el módulo central
ejecuta el bloque de código instrumentado. La manera en
que un módulo instrumenta el bloque de código provisto
por coregrind depende de qué funcionalidad provee dicho
módulo. Por ejemplo, el módulo Memcheck (que permite
verificar y detectar errores en cada referencia a memoria de
un programa de usuario), instrumenta el código agregando
sentencias para verificar cada acceso a memoria y cada
valor calculado. Los módulos que provee Valgrind son los
siguientes: MEMCHECK, ADDRCHECK, HELGRIND,
VI SIMPOSIO ARGENTINO DE TECNOLOGIA EN COMPUTACION: AST-PAR002
3
CACHEGRIND, MASSIF, CORECHECK, LACKLEY y
NULGRIND.
las referencias a memoria hechas por la aplicación, individualizando al hilo de ejecución que la produjo.
B.
A.
Detalles de la arquitectura
Valgrind es una biblioteca compartida (valgrind.so),
con algunas particularidades, como por ejemplo que tiene
prioridad en cuanto a su inicialización. Una vez que esto
sucede, toma control completo respecto a la ejecución del
programa de usuario, traduciendo las sentencias de código
del mismo en otras sentencias nuevas (en formato UCode).
Para esto utiliza la función VG (translate), mediante
la cual traduce bloques básicos de sentencias. Todo código
traducido (e instrumentado) por Valgrind es almacenado en
un caché (TC - Translation Cache).
Para ejecutar código traducido (y almacenado en el TC),
Valgrind utiliza la función VG (dispatch), que posee
la lógica necesaria para poder determinar en qué momento
ejecutar qué sentencia de código (traducida), ir a buscarla
al TC, y alimentarla al procesador real. En cada iteración
se obtiene la dirección (real) de la próxima instrucción
a ejecutar, dirección que es traducida al rango manejado
por Valgrind, para poder recuperar la instrucción desde el
TC. Mientras estas instrucciones se encuentren en el TC,
Valgrind continúa normalmente la ejecución del programa
de usuario. Si una instrucción no se hallara en el TC,
Valgrind sale de la función VG (dispatch) para hacer
una nueva llamada a VG (translate).
C.
Microcódigo
Valgrind implementa un procesador x86 sintético con
instrucciones en un formato propio, conocido como UCode
(similar al de un procesador RISC). En el formato UCode,
las micro-operaciones se conocen como UInstr.
Básicamente, los pasos en los cuales interviene el formato
de instrucciones UCode, son los siguientes: 1) Parseo de
un bloque básico del programa de usuario en un conjunto
de instrucciones UCode. Rutina invocada: VG (disBB)
2) Optimización de las instrucciones UCode obtenidas
en el paso anterior. Rutina invocada: vg improve 3)
Instrumentación de las instrucciones UCode obtenidas
en el paso anterior. Rutina invocada: vg instrument
4) Optimización de las sentencias instrumentadas, para
quitar redundancia en los chequeos. Rutina invocada:
vg cleanup 5) Asignación de registros. Rutina invocada:
vg do register allocation 6) Generación del código final x86. Rutina invocada: VG (emit code)
Una instrucción UInstr está conformada por varios campos (de la misma manera que sucede con las microoperaciones). Los más relevantes son el tipo de opcode
a ejecutar (en Valgrind, UOpcode), y los valores de los
operandos (val1, val2 y val3), entre otros. En particular,
los posibles UOpcodes a ejecutar se pueden agrupar de la
siguiente manera: GET/PUT, LOAD/STORE, MOV/CMOV
y operaciones de la Unidad Aritmético/Lógica (ALU):
LEA1/LEA2, CALLM FPU/FPU R/FPU W, JIFZ, INCEIP.
III.
M ODIFICACIONES
APLICADAS A
VALGRIND
Se presenta aquı́ un conjunto de modificaciones aplicadas
a Valgrind (tanto a su módulo central, como también a
algunos de sus skins), que permiten recopilar (en tiempo de
ejecución) información relacionada al programa de usuario
que se está ejecutando. Más precisamente, interesa obtener
El módulo a instrumentar
De los módulos (o skins) dados en la sección A, los que
tienen “contacto” con las referencias a memoria efectuadas
por el programa de usuario son: Memcheck y Addrcheck. El
primero realiza un análisis completo de la actividad que el
programa de usuario presenta respecto al uso de memoria,
mientras que el segundo, Addrcheck, es una versión más
reducida de Memcheck.
Al implementar una funcionalidad más reducida respecto
a Memcheck, el módulo Addrcheck permite la ejecución
de los programas de usuario en forma dos veces más
rápida respecto al primero [10]. Sin embargo, a pesar de
su funcionalidad acotada, Addrcheck maneja todos los tipos
de instrucciones del programa de usuario, con lo cual, es
apto para nuestros fines. Por tales razones citadas, el módulo
Addrcheck fue el instrumentado para capturar las referencias
a memoria.
Según lo detallado en la sección B, todo bloque
básico del programa de usuario es traducido a microinstrucciones UCode, mediante sucesivas llamadas a la
función VG (translate). Esta funcionalidad es competencia del módulo central (coregrind) ya que, independientemente del skin utilizado, siempre se requiere traducir el
programa de usuario a formato UCode. El proceso de traducción de instrucciones de usuario en instrucciones UCode
involucra, en uno de sus pasos, la instrumentación de los
bloques básicos siendo parseados. Dicha instrumentación
trasciende los lı́mites del módulo central, siendo competencia de cada uno de los skins. Ası́, cada skin implementa su
propio mecanismo de instrumentación, con el nombre de
SK (instrument). Por lo tanto, se modificó la función
SK (instrument) del módulo Addrcheck.
Para instrumentar un bloque básico, Valgrind inserta en
el mismo llamadas a funciones auxiliares o helpers. En el
módulo Addrcheck estos helpers, que son invocados cada
vez que el flujo de ejecución alcanza ese punto, monitorean
todos los accesos de lectura/escritura sobre memoria principal. Por esta razón, para este trabajo se modificó el código
de estas funciones auxiliares, con código que permite,
entre otras cosas, obtener la dirección de memoria siendo
referenciada.
B.
Consideraciones sobre multihilo
En esta sección analizamos el manejo que presenta Valgrind respecto al uso de hilos, y cómo se modificó esa
estructura para poder recrear una arquitectura SMT.
El sistema Valgrind posee soporte para hilos según el
estándar POSIX (pthreads ó POSIX threads). Valgrind
reemplaza la biblioteca estándar libpthread.so (que
implementa pthreads), por otra propia que encapsula a
la primera. En este trabajo analizamos y modificamos el
mecanismo de implementación y ejecución de hilos provisto
por Valgrind, para que se ajuste a nuestras necesidades.
En un ambiente multihilo convencional (uniprocesador,
no SMT), el planificador del sistema operativo es el encargado de administrar el uso de CPU por parte de los hilos.
Ası́, es quien fracciona el tiempo en rebanadas (o slices),
y decide qué hilo debe ejecutarse y por cuánto tiempo,
de acuerdo a un esquema de prioridades. En este caso,
VI SIMPOSIO ARGENTINO DE TECNOLOGIA EN COMPUTACION: AST-PAR002
si bien a nivel de las aplicaciones la ejecución está paralelizada por el uso de hilos, está claro que a nivel del
procesador la ejecución se serializa. Valgrind presenta un
esquema similar, con un planificador (vg scheduler),
encargado de ordenar la ejecución de los hilos del programa
de usuario. Para ello, en lugar de contemplar hilos con
prioridades, vg scheduler ejecuta una cantidad fija de
instrucciones por hilo, antes de efectuar el cambio y usa
una polı́tica round-robin, ejecutando 50000 bloques básicos
por hilo [10].
Como se señaló en la sección A, en una arquitectura
SMT, todos los hilos están disponibles para ser ejecutados
en cada ciclo de reloj. Eventualmente, algunos de ellos
podrı́an estar bloqueados porque, por ejemplo, realizaron
una operación de E/S o tuvieron un desacierto en la memoria
caché. Teniendo en cuenta que Valgrind realiza un cambio
de contexto cada 50000 bloques básicos ejecutados, implica un modelo de ejecución completamente alejado del
modelo SMT. Para resolver esta situación, se modificó el
planificador de Valgrind para que cambie de contexto por
cada bloque básico ejecutado. Cada bloque básico equivale
a muy pocas instrucciones x86, con lo que se logra obtener
en la práctica solo una referencia a dato en memoria por
cada conmutación de hilo. De esta manera la secuencia de
referencias a memoria generada por la herramienta, refleja
el paralelismo a nivel de hilos presente en la aplicación,
aspecto de fundamental importancia para la simulación de
memorias cache en ambientes SMT.
Se define aquı́ la forma para la salida del flujo de
referencias a memoria. Todas las referencias capturadas son
escritas en un archivo, con un formato simple, que contiene
los siguientes campos:
Id del hilo
Dirección de memoria
Los siguientes son ejemplos de referencias a memoria
capturadas a partir de las modificaciones efectuadas a
Valgrind para una aplicación con cuatro hilos de ejecución:
[
[
[
[
1]
2]
3]
4]
[
[
[
[
...
2617240328]
1963012180]
1964569408]
1967722008]
...
Finalmente dado los enormes tamaños de los archivos
de trazas tı́picos, los mismos se generan y comprimen “al
vuelo”, para lo cual se utiliza el algoritmo LZ77, mediante
la biblioteca zlib.
IV.
C ONCLUSIONES
Se presentó un ambiente para la recolección de referencias a memoria de programas multihilo. La herramienta
utiliza directamente el código ejecutable de la aplicación
analizada, con lo cual no es imprescindible contar con los
códigos fuente de la misma. Esto último es de gran importancia dado que es posible obtener las trazas de aplicaciones
comerciales, de las cuales usualmente no se dispone de los
códigos fuente mencionados. La fina granularidad lograda
en la captura de las referencias, hacen a las trazas generadas
con la herramienta propuesta ideales para la simulación de
un esquema SMT. Esto es de gran importancia, dado que no
4
existen herramientas libres y de código abierto que permitan
realizar la captura de trazas de programas multihilo que se
ejecutan en arquitecturas SMT. Asimismo el esquema SMT
ha adquirido gran importancia, y es usado en procesadores
comerciales como Intel Pentium IV HT. También Intel,
por ejemplo, lanzó su último procesador, el cual posee
un doble núcleo (Dual-Core) que incorpora la arquitectura
SMT. Por su parte, IBM y Sun Microsystems también están
implementando el esquema SMT en productos como los
servidores IBM Power5 y UltraSparc IV, respectivamente.
Esto indica que el esquema SMT promete seguir siendo
explotado en los próximos años y de ahı́ la importancia de
poder contar con los ambientes de simulación adecuados
para estas arquitecturas.
V.
AGRADECIMIENTOS
Queremos agradecer a Leandro Santi y Alejandro Gramajo de la Facultad de Ingenierı́a, U.B.A., por los valiosos
aportes realizados al presente trabajo. También agradecemos
a Julian Seward, Nick Nethercote y Jeremy Fitzhardinge, de
KDE y creadores de Valgrind, quienes aportaron sus ideas y
consejos respecto a cómo modificar dicha herramienta para
la captura de referencias a memoria. Finalmente agradecemos a Henry Levy, de la Universidad de Washington
y uno de los creadores de la arquitectura SMT, quién
aportó información sobre esta nueva tecnologı́a. El presente
trabajo cuenta con subsidios de la Universidad de Buenos
Aires y el Consejo Nacional de Investigaciones Cientı́ficas
y Técnicas (CONICET).
R EFERENCIAS
[1] A. Agarwal, L. Sites y M. Horowitz, “ATUM: a new technique for
capturing address traces using microcode”, Proceedings of the 13th
annual international symposium on Computer architecture, Tokyo,
Japón, pp. 119-127, 1986
[2] S. Eggers, J. Emer, H. Levy, J. Lo, R. Stamm, D. Tullsen; Simultaneous Multithreading: A Platform for Next-Generation Processors;
IEEE Micro; pp. 12-19; 1997.
[3] Intel Corporation, http://www.intel.com.
[4] J. Larus y T. Ball, “Optimally Profiling and Tracing Programs”,
Technical Report 1031 - Computer Sciences Department - University
of Wisconsin-Madison (Madison), 1991.
[5] J. Larus y T. Ball, “Rewriting Executable Files to Mesure Program
Behavior”, Technical Report 1083 - Computer Sciences Department
- University of Wisconsin-Madison (Madison), 1992.
[6] D. A. Patterson y J. L. Hennessy, Computer Architecture. A Quantitative Approach, 1ra edición, Morgan Kaufmann Publishers, 1990.
[7] D. A. Patterson y J. L. Hennessy, Computer Architecture, A Quantitative Approach, 2da edición, San Mateo, California: Morgan
Kaufmann Publishers, 1995.
[8] D. A. Patterson y J. L. Hennessy, Computer Architecture. A Quantitative Approach, 3ra edición, Morgan Kaufmann Publishers, 2000.
[9] S. Przybylski, “Cache and Memory Hierarchy Design. A Performance
Directed Approach”, Morgan Kaufman Publishers, 1990.
[10] J. Seward, N. Nethercote; Valgrind, version 2.1.0; Manual correspondiente a la versión 2.1.0 del sistema Valgrind; 2004.
[11] Standard Performance Evaluation Corporation, http://www.spec.org.
[12] D. Tullsen, S. Eggers, H. Levy; Simultaneous Multithreading: Maximizing On-Chip Parallelism; Proceedings of the 22nd Annual International Symposium in Computer Architecture, 1995.
[13] R. A. Uhlig y T. N. Mudge, “Trace-Driven Memory Simulation: A
Survey”, ACM Computing surveys, vol. 29, No. 2, pp. 128-170, June
1997.
[14] J. Seward, N. Nethercote, J. Fitzhardinge; Valgrind version 2.0.0;
http://valgrind.kde.org/index.html.
[15] A. Wulf y S. A. McKee, “Hitting the Memory Wall: Implications of
the Obvious”, ACM Computer Architecture News, vol. 23, nro. 1, pp.
20-24, 1995.
[16] N. Nethercote; Dynamic Binary Analysis and Instrumentation; Dissertation submitted for the degree of Doctor of Philosophy at the
University of Cambridge; 2004.
Descargar