|=--------------= Usando perfmon en arquitecturas P5, P6 y K7 =---------------| |=-----------------------------------------------------------------------------| Autor: Manuel G. Lázaro (Pakka-Pakka) Por favor, envíe comentarios, bugs, flames, etc. a esta dirección: Email: Versión del documento: 0.9 Fecha: 4/2/2002 ----| Contenidos: Contenidos. Introducción. Operando con el dispositivo perfmon. - Usando el TSC. - Usando los PMCs. Adaptando perfmon al K7. Notas sobre perfmon.c y perfmon.h. Por hacer. Referencias. Apéndice A: perfmon.c Apéndice B: perfmon.h ----| Introducción: El archivo especial /dev/perfmon permite acceder, por medio de ioctl's, a los "Performance Monitoring Counters" (PMCs) del microprocesador, así como al "Time Stamp Counter" (TSC). Estos registros están pensados para ayudar a la medición del rendimiento de un programa. El TSC permite conocer el número de ciclos del microprocesador transcurridos entre dos puntos de un programa, en tanto que los PMCs calculan el número de eventos (fallos de caché, interrupcio- nes, flops, etc.) ocurridos entre dos puntos dados. Es e

Website templates are pre-designed websites all you need to do is add your own personal content and you're ready to jump start your own website. Website templates by Vooweb

Sendmail se configura a partir de unos archivos con una sintáxis algo compleja: los que tienen extensión .cf en esos directorios. Con las últimas versiones de este MTA, el proceso se simplifica en gran medida ya que nosotros no deberemos tocar estos archivos. En su lugar, editaremos los ficheros con extensión .mc que, una vez procesados, nos darán los anteriores.ncluyendo código fuente, cómo modificar la fuente del núcleo para soportar también los microprocesadores AMD K7 Athlon. También daremos ejemplos de la aplicación de /dev/perfmon para optimar aplicaciones y para obtener información sobre el microprocesador. Para obtener información más específica sobre los PMCs y el TSC, el lector puede consultar las referencias [3] (para procesadores Intel) y [4] (para AMD). FreeBSD ofrece en /usr/share/examples/perfmon un programa en C que permite acceder a /dev/perfmon. También el manual da en perfmon(3) información básica sobre este dispositivo. Es interesante observar que si el núcleo de FreeBSD (al menos hasta la ver- sión 4.3) ha sido compilado con la opción i686 y es ejecutado en un K7, el acceso a /dev/perfmon puede echar abajo el sistema. De todos modos, el soporte del dispositivo perfmon no es activado por defecto en el núcleo. Este tutorial no habría sido posible sin la ayuda material de las tiendas de 24 horas. ¡Gracias tíos! Tampoco habría sido posible sin apoyo moral: A sil-ellincenya anvanima hildoro! Tye-meláne. ----| Advertencia: Las instrucciones ofrecidas en este artículo incluyen manipulaciones de partes críticas del sistema operativo y accesos a registros específicos de ciertos modelos de microprocesadores. El autor no se hace responsable de ningún perjuicio derivado del seguimiento de las instrucciones en este artí- culo, sea ese seguimiento correcto o incorrecto. Todos los programas presentados en este artículo, incluyendo la implementa- ción de perfmon para el microprocesador AMD K7 Athlon, han sido probados en el ordenador del autor y han funcionado correctamente. Sin embargo eso puede no aplicarse al suyo, ya sea porque se trata de un modelo diferente o por otra causa. Use esos programas bajo su propia responsabilidad. Etcétera. ----| Operando con el dispositivo perfmon. En esta sección describiremos, desde el punto de vista del programador, cómo acceder a /dev/perfmon. Es importante observar que necesita un micro- procesador compatible con el dispositivo perfmon. Este dispositivo ha sido probado en Pentium y Pentium Pro. En [3], Sección 15.6, no se indican varia- ciones entre el Pentium Pro y los Pentium II y III relativas a los PMCs, de forma que perfmon _debería_ funcionar con Pentium II y III. Si dispone de un AMD K7, FreeBSD 4.3 no tiene soporte para su micro, e intentar acceder al dispositivo puede causar resultados impredecibles (en mi caso, una caída completa del sistema y un reboot forzado). Sin embargo, puede hacer una compilación del núcleo con soporte para K7. Si le interesa, siga las instruc- ciones en la sección "Adaptando perfmon al K7" más abajo. Luego puede probar a ejecutar alguno de los programas de esta sección. Si se decide a probar, por favor escríbame un mail indicando los resultados a . En vez de dar una exposición completa de las funciones de perfmon, analiza- remos algunos programas de ejemplo, comentando en detalle las partes más significativas. El primer programa que consideraremos mide el número de ciclos de reloj entre dos puntos de un programa, es decir, accede al TSC. --------> Usando el TSC: Recorte el siguiente programa (prueba.c) y compílelo con las opciones: #gcc -o prueba prueba.c -lm ---------------------------------| prueba.c |----------------------------------- #include /* Usaremos ioctl */ #include /* Cabecera de perfmon. */ #include /* Para) */ #include /* Para close() */ #include /* Codigos de error. */ #include __inline void test(void); /* Funcion a cronometrar. */ __inline void test2(void); /* Funcion diminuta. */ int main(int argc, char **argv) { struct pmc_tstamp ini, fin; int fd; /* Descriptor de fichero */ register int i,num; /* Contadores. */ num = 100; /* Por defecto 100 vueltas. */ if (argc > 1) num = atoi(argv[1]); if (num<1) num = 100; /* Abrimos /dev/perfmon */ if ((fd _PATH_PERFMON, O_RDWR, 0)) < 0) { perror("); exit(1); } /* Leemos el TSC */ if (ioctl(fd, PMIOTSTAMP, &ini) < 0) { perror("ioctl"); exit(1); } for (i=0 ; i /* Esta funcion de prueba hace un calculo simple. */ __inline void test(void) { double x=1; x = exp(x); } |------------------------------------------------------------------------------| El programa "prueba" mide el número de ciclos de reloj transcurridos mien- tras se ejecuta un bucle que llama cierto número de veces a la subrutina test. Este número de veces es por defecto 100, pero puede definirse al ejecutar el programa (por ejemplo, "prueba 1000" hace un bucle de 1000 vueltas). El progra- ma devuelve la frecuencia en MHz del microprocesador y el promedio de ciclos de reloj que ha llevado ejecutar el bucle. Más adelante discutiremos en detalle cómo entender los resultados que ofrece este programa. Ahora veamos cómo funciona el programa: Los cuatro ficheros de cabecera que encontramos al principio del programa son necesarios para acceder a cualquiera de los servicios de perfmon. El único fichero de cabecera exclusivo de perfmon es /usr/include/machine/perfmon.h . Este fichero contiene las definciones de los tipos de datos, máscaras y servi- cios esenciales del dispositivo perfmon. Es interesante leerlo antes de seguir con este programa. Para manipular el TSC nos interesará de perfmon.h la estruc- tura struct pmc_tstmp { int pmct_rate; /* Frec. del micro en MHz (aprox.) */ quad_t pmct_value; /* Contador. Es de 64 bits con signo. */ }; Esta es la estructura que nos permite leer el TSC a través de /dev/perfmon. El primer campo nos indica la frecuencia de nuestro micro en Megahertz, mientras que el segundo campo indica el número de ciclos transcurridos desde el encendido del ordenador. Hay que tener en cuenta que ambas cantidades son aproximadas (consulte [3], [4]), y que concretamente la cantidad pmct_rate es calculada por el driver, con un error añadido menor que 1MHz (ver /sys/i386/i386/perfmon.c). De perfmon.h también nos interesa la definición _PATH_PERFMON. Esta será en la mayor parte de los casos igual a /dev/perfmon. Otra definición que hemos usado es PMIOTSTAMP, dentro de cada ioctl. Esta es la función específica de perfmon que nos permite meter el contenido del TSC dentro de la estructura pmc_tstmp. En perfmon.h podrá ver el resto de las funciones ofrecidas (PMIOSETUP, PMIOGET, PMIOSTART, PMIOSTOP, PMIOREAD, PMIORESET). Todas estas se refieren a los PMCs, que no hemos usado en este programa. Teniendo todo esto en cuenta, es fácil entender ya el programa. La estruc- tura del mismo se resume en: 1) Determinar número num de vueltas (por defecto num=100) 2) Abrir el archivo especial /dev/perfmon (en _PATH_PERFMON) 3) Primera lectura del TSC, a la estructura ini 4) Se ejecuta el bucle de test() 5) Seguna lectura del TSC, a la estructura fin 6) Presentación de los resultados En el programa se incluye una segunda función test (test2), que contiene una única instrucción del microprocesador (nop, es decir No OPeration, no hace nada). Esta puede ser útil para calcular el tiempo que se tarda en realizar un bucle de un sólo elemento, así como la perturbación en la cuenta del número de ciclos del reloj que introduce el driver. Tenga de todos modos en cuenta que a la hora de calcular el tiempo empleado también se incluyen los ciclos de reloj usados en gestionar el bucle (incremento de i, comparación con num, salto). Una ejecución de este programa en mi K7-700 resulta: #./prueba 1000 Frecuencia (MHz) : 701 Ciclos en test (media): 248 es decir, que la función test lleva unos 248 ciclos de reloj al ejecutarse, con- tando con la gestión del bucle. Sin embargo, ejecutemos ahora la variante: #./prueba 1 Frecuencia (MHz) : 701 Ciclos en test (media): 28232 ¿Qué ha sucedido para que el tiempo necesario suba tanto?!! Peor aún, ejecutemos: #./prueba 2 Frecuencia (MHz) : 701 Ciclos en test (media): 13774 Ahora resulta que lleva la mitad que con una sola vuelta. Si vamos haciendo pruebas con diferentes valores veremos que desde el máximo num=1 el número de ciclos decrece rápidamente según crece num hasta estabilizarse cuando num>500 en un valor casi fijo (sobre 250 en mi K7). ¿Qué está sucediendo? La respuesta está (en buena parte) en la caché de nuestro microprocesador: La primera vez que entramos en el bucle nuestro microprocesador necesita almacenar en la caché tanto los datos que va a emplear en el test, como el código que va a usar. Así que la primera vuelta del bucle es varios órdenes de magnitud más lenta que las demás (al menos para las funciones test que he empleado en el programa prueba. Si la función fuera muy grande podría no caber en la caché, haciendo el resulta- do más homogéneo). Así que la primera vuelta es muy lenta, y las demás son muy rápidas. Podemos encontar una expresión aproximada para la salida del programa prueba: Si en la primera vuelta el programa necesita A ciclos (A = 28000[aprox.] en mi K7) y en las siguientes -cuando test() ya está en la caché- necesita B ciclos (B = 250 [aprox.] en mi K7), entonces el promedio para n vueltas es: Ciclos = (A + (n-1)B)/n que es del orden de A/n + B y que claramente tiende hacia B cuando n es grande. Si n=2, el número es del orden de A/2, dado que B es mucho más pequeño que A. Esto concuerda con lo que hemos comprobado. Finalmente observemos que el TSC cuenta los ciclos del microprocesador sin importarle qué proceso los ejecuta (ya sea el núcleo, nuestro programa, u otro proceso). Esto quiere decir que si num es demasiado grande, el resultado del programa prueba puede verse alterado por la interferencia de otros procesos, dado que el bucle se ejecuta el suficiente tiempo como para que el núcleo le conceda el microprocesador a otros procesos durante un rato. Así que no hay que pasarse con el valor de num. --------> Usando los PMCs: Mientras que el acceso al TSC es equivalente, y muy simple, en todos los micros que soporten perfmon, el caso de la manipulación de los PMCs es comple- tamente diferente. Existen incompatibilidades entre los micros P5 y P6, y entre estos dos y el K7. A nivel del dispositivo perfmon esta incompatibilidad sólo se manifiesta en que las máscaras que hay que enviar a la ioctl PMIOSETUP son diferentes para cada microprocesador. Si observa /sys/i386/include/perfmon.h verá muchas definiciones empezando por PMC6 (p. ej. PMC6_FLOPS) y por PMC5 (como PMC5_FLOPS). Si oberva la versión modificada de perfmon.h en el apéndice B, también encontrará definciones empezando por K7 (como K7_PC_IFETCH). Todas estas definiciones corresponden a diferentes eventos que pueden ser calculados por diferentes microprocesadores: así, PMC6_FLOPS es la máscara que dice a un PMC en un P6 que cuente todas las instrucciones en coma flotante que ejecute el micro. PMC5_FLOPS es la máscara equivalente a PMC6, pero para un micro P5. Una máscara equivalente no existe para el K7, de acuerdo con [4]. La máscara K7_PC_IFETCH indica a un contador de un K7 que cuente el número de capturas de la caché de instrucciones. Pasemos a estudiar un ejemplo concreto de uso de los PMCs: --------------------------------| prueba2.c |---------------------------------- #include /* Usaremos ioctl */ #include #include /* Para) */ #include /* Para close() */ #include #include __inline void test(void); /* Funcion a comprobar. */ __inline void test2(void); /* Funcion diminuta. */ int main(int argc, char **argv) { struct pmc contador; /* Nuestro contador. */ struct pmc_data lectura; /* Aqui va la lectura del contador. */ int fd; /* Descriptor de fichero */ register int i,num; num = 100; /* Por defecto 100 vueltas. */ if (argc > 1) num = atoi(argv[1]); if (num<1) num = 100; /* Abrimos /dev/perfmon */ if ((fd _PATH_PERFMON, O_RDWR, 0)) < 0) { perror("); exit(1); } /* Determinamos el PMC a usar, el evento y las flags. */ contador.pmc_num = 0; /* PMC numero cero. */ contador.pmc_flags = PMCF_USR | PMCF_E; /* Flags edge y usuario. */ contador.pmc_event = K7_PC_IFETCH_MISS; /* Fallos cache de instrucciones. */ contador.pmc_unit = 0; /* Este es casi siempre cero. */ contador.pmc_mask = 0; /* Mascara a cero. */ /* Configuramos el lector. */ lectura.pmcd_num = 0; /* Leer contador cero. */ /* Configura PMC y lo resetea a cero. */ if (ioctl(fd, PMIOSETUP, &contador) < 0) { perror("ioctl(PMIOSETUP)"); exit(1); } if (ioctl(fd, PMIOSTART, &(contador.pmc_num)) < 0) { perror("ioctl(PMIOSTART)"); exit(1); } for (i=0 ; i void test(void) { double x=1; x = exp(x); } |------------------------------------------------------------------------------| Antes de compilar este programa, tenga en cuenta que ha sido escrito para funcionar en un AMD K7. Si su microprocesador es un P6, deberá cambiar la línea contador.pmc_event = K7_PC_IFETCH_MISS; /* Fallos cache de instrucciones. */ por la contador.pmc_event = PMC6_IFU_IFETCH_MISS; y si tiene un P5 deberá cambiarla por contador.pmc_event = PMC5_INS_CACHE_MISS; Esto se debe a la incompatibilidad de las máscaras de eventos que indicamos anteriormente. Una vez hecho ese cambio, no hay más problemas desde el punto de vista del usuario: perfmon se encarga de gestionar todo correctamente. La estructura del programa es del todo similar a la del programa de prueba anterior. En este caso queremos medir el número de fallos de caché (para el caché de instrucciones) que se producen al ejecutar varias veces la función test. Lo primero que debemos hacer es determinar qué contador vamos a usar; qué evento vamos a contar; si vamos a contar el número de ciclos en los que ese evento tiene lugar o bien el número de veces que ese evento ocurre, indiferente- mente de su duración; si vamos a tener en cuenta si varios eventos de ese tipo ocurren simultaneamente; qué modificadores vamos a imponer a ese contador de eventos; y finalmente si vamos a tener en cuenta aquellos eventos que ocurren en modo núcleo, en modo usuario, o en ambos. De determinar todas estas variantes se encarga la estructura struct pmc contador que se define al principio del programa. La estructura de define en perfmon.h, donde se definen también algunos atajos hacia sus elementos (dado que esta estructura contiene una unión, debido a exigencias del hardware). Los componentes de esta estrucutura son: contador.pmc_num : Esta cantidad entera indica qué contador, de los disponibles, vamos a usar. Los micros Intel tienen dos contadores (el 0 y el 1), en tanto que el K7 tiene cuatro (desde 0 hasta 3). Observe que, para mantener la compatibilidad, el driver para K7 sólo permite usar los contadores 0 y 1. contador.pmc_flags : Esta cantidad es un byte que indica al contador cómo tiene que contar los eventos. Básicamente nos interesan tres flags: PMCF_USR: se consideran eventos en modo usuario. PMCF_SO : se consideran eventos en modo núcleo. PMCF_E : se considera el número de eventos, no su duración. pueden combinarse cualesquiera de estas flags de modo simultáneo. Es interesante observar que si no se activa PMCF_E se mide el número de ciclos en que el micro permanece mientras cierto evento está ocurriendo, no el número de eventos de ese tipo que tienen lugar. Luego veremos ejemplos con PMCF_E activada y no activada. También puede probar el programa de ejemplo en /usr/share/examples que permite activar esa flag desde la línea de comandos. contador.pmc_event : Aquí se especifica el evento a estudiar. Recuerde que esta es la parte que depende del tipo de microprocesador para el que se compila el programa. La lista de eventos, un tanto críptica, se encuentra en perfmon.h . Si quiere entender eso mejor, consulte [3], Apéndice A, o [4], Apén- dice D. contador.pmc_unit : Esto es un campo de 8 bits que permite modificar aún más la selección de algunos (muy pocos) eventos, en general relacionados con la caché de nivel 2. Lo mejor es dejarlo a cero, salvo que quiera monitorizar esa caché. Refiérase a [3], [4] en ese caso. contador.pmc_mask : Este campo de 8 bits permite afinar aspectos relativos a la simultaneidad de eventos. Si este campo tiene un valor no nulo, se consideran sólo eventos que ocurren simultaneamente tantas veces como el valor de este campo (si en el programa de prueba fuese contador.pmc_mask == 2, entonces sólo se contarían aquellas ocasiones en las que ocurrieran simultánea- mente dos o más fallos de caché de instrucciones, algo muy poco probable [¿imposible?]). Otro tipo de eventos sí pueden ser simultáneos, claro. También es posible contar cuándo ocurren _menos_ eventos que la cantidad especificada en contador.pmc_mask. Para ello, hay que activar la flag PMCF_INV en contador. pmc_flags. Es poco probable que desee hacer alguna de estas variaciones, así que en general es conveniente dejar este campo a cero, de modo que se cuentan todos los eventos que ocurren, con independencia del grado de simultaneidad. Con esto acabamos nuestro recorrido por la estructura pmc. Como podemos ver es bastante configurable. Para nuestro programa de ejemplo hemos tomado una situación fácil: usar contador 0, considerar eventos sólo en modo usuario, contar los eventos (y no el tiempo que están activos), definir el evento como los fallos en la caché de instrucciones, e ignorar cuestiones de simultaneidad (lo de la caché de nivel 2 no se plantea para este evento en particular). Además de configurar el contador que vamos a usar, tendremos que definir una estructura para almacenar los valores de ese contador, tal y como hicimos con el TSC en el ejemplo anterior. Para eso tenemos la estructura pmc_data, que en el programa hemos llamado lectura. Los campos que contiene son dos: lectura.pmcd_num : Cantidad entera. Se refiere al contador que queremos leer con esta estructura. En nuestro ejemplo es 0, dado que éste es el contador que vamos a activar. lectura.pmcd_value : Cantidad de 64 bits con signo. Este es valor del contador. Aunque el contador tiene espacio para contar hasta 64 bits, en general sólo podrán usarse 40 (48 en un K7) bits del mismo. Una vez que el programa ha decidido qué contadores usar, cómo usarlos, y ha preparado la estructura de lectura, empieza lo interesante. Lo primero es indicar al driver que queremos configurar el contador 0. Para ello hacemos la ioctl con el servicio PMIOSETUP, enviando la estructura struct pmc contador al driver. Esto prepara el contador 0 para ser usado, y lo resetea. A continuación ponemos a correr el contador con la llamada PMIOSTART. A esta llamada sólo hay que darle el número del contador, es decir 0. Eso se hace enviando &(contador.pmc_num). A partir de este momento el contador 0 está registrando eventos. Ahora dejamos correr el bucle tantas veces como indique num. El contador 0 se encargará de registrar todos los eventos. En cuanto el bucle termina, hacemos una llamada al servicio PMIOREAD, para leer el contador y almacenarlo en la estrucutura struct pmcd lectura . Por eso a esta llamada le enviamos el puntero &lectura . A partir de este momento, en lectura.pmcd_value está almacenado el número de eventos que han ocurrido. Obser- vemos que no se ha detenido el contador para leerlo. El contador sigue corrien- do, aún mientras es leído. Esto puede producir una perturbación en la lectura (a fin de cuentas queremos saber qué pasa en el bucle, no en el driver). Luego veremos hasta qué punto es importante esa perturbación. A continuación detenemos el contador con la llamada PMIOSTOP, indicándole el número de contador a detener. Como hemos dicho, esto podría haberse hecho antes de leer el contador. Finalmente se pone el contador a cero con PMIORESET. Esto no es necesario (esta parte puede omitirse del programa sin que haya ningún efecto nocivo). Simplemente la he incluido para que vea como se resetea a cero el contador. Hecho esto, el programa escribe el número de eventos ocurridos y termina. Es posible combinar las llamadas de activación, desactivación, reseteo, lectura y configuración libremente a lo largo del programa. Eso es lo que se hace a lo largo del programa de ejemplo /usr/share/examples/perfmon/perfmon.c , que da un ejemplo bastante más intrincado de operación con el dispositivo perfmon. De todos modos, este ejemplo permite hacerse una idea de por dónde van los tiros. Es posible jugar con varios contadores a la vez, sin que el driver ponga objecciones. Sin embargo, para arquitecturas P6 hay que seguir un orden a la hora de activar y desactivar los contadores (vea perfmon(3) en el manual). Esta restricción no se aplica a los PMCs del K7: pueden ser activados y desactivados en cualquier orden. Tenga eso en cuenta si va a jugar con varios contadores a la vez y tiene un micro de Intel. Pasemos ahora a analizar la salida del programa. La compilación del programa es como antes: #gcc -o prueba2 prueba2.c -lm Al ejecutarlo resulta: #./prueba2 1000 Numero de eventos: 22 Es decir, ocurren 22 fallos en la caché de instrucciones. Probemos ahora con: #./prueba2 1 Numero de eventos: 20 ¡Ajá! Esto aclara bastante por qué se daba el retraso cuando contábamos los ciclos de reloj en el ejemplo anterior. Veamos qué pasa con dos vueltas: #./prueba2 2 Numero de eventos: 21 La situación está bastante clara: en la primera ejecución del bucle, la función test no está en la caché de instrucciones, y produce en torno a 20 fallos de caché hasta que entra en él. Esto justifica buena parte del retraso al dar la primera vuelta. Ocupémonos ahora de la injerencia de los otros procesos en nuestras medi- ciones. Al usar el TSC dijimos que si el bucle era muy largo, entonces otros procesos podían perturbar nuestras mediciones. Veámoslo: #./prueba2 100000 Numero de eventos: 10730 Aquí está el resultado que cabía esperar. El contador registra fallos en la caché de instrucciones que pertenecen a otros procesos. El número de fallos se dispara de 20 (aproximadamente) hasta más de 10.000. Unos párrafos atrás, al hablar de las flags del contador, indicamos que además de medir el número de veces que ocurre un evento, podemos medir también su duración. Veamos un ejemplo de esta situación. Para ello deberemos modificar un poco el código de prueba2.c . Lo que sigue es la modificación correcta para un K7; si ha llegado hasta aquí y posee un P5 o un P6, le dejo como ejercicio para el lector el adaptar el código para su micro (es decir, soy demasiado pere- zoso para escribir todas las variantes ;) Contemos el número de saltos (instrucciones jmp en ensamblador) que se pro- ducen a lo largo del bucle. Esta cantidad deberá crecer linealmente con el número de vueltas del bucle. Además, para no contar otros saltos que no sean los del bucle, usaremos la función test2 en vez de la función test. Los cambios son: 1) la línea contador.pmc_event = K7_PC_IFETCH_MISS; pasa a ser contador.pmc_event = K7_FR_RET_BRANCH; 2) la línea test(); que está en el bucle, pasa a ser test2(); Compilemos de nuevo, con las mismas opciones, y veamos los resultados: #gcc -o prueba2 prueba2.c -lm #./prueba2 1000 Numero de eventos: 1014 Es decir, en torno a 1000. Veamos otras pruebas: #./prueba2 100 Numero de eventos: 114 #./prueba2 10 Numero de eventos: 24 Estos son los resultados de esperar. Dado que la función test no hace nada, lo que se cuenta son los saltos que causa el bucle al comparar la variable i con la variable num. Además hay unos 15 (¿por qué 15 y no 14? ;) saltos de regalo causados por las ioctl's. Pero, ¿cuánto tiempo dura cada salto? Esto podemos calcularlo modificando otra línea y recompilando el programa. Hagamos otro cambio: 3) la línea contador.pmc_flags = PMCF_USR | PMCF_E; pasa a ser contador.pmc_flags = PMCF_USR; Es decir, medimos los ciclos de reloj en los que se gestionan los saltos, no el número de veces que el micro topa con ellos. Veamos qué pasa: #gcc -o prueba2 prueba2.c -lm #./prueba2 1000 Numero de eventos: 4008 #./prueba2 100 Numero de eventos: 408 #./prueba2 10 Numero de eventos: 48 Vamos, que el procesador se duerme en torno a 4 ciclos mientras decide qué hacer con cada salto que se encuentra. Observe que decir "Numero de eventos" no es en este caso correcto. Habría que decir "Numero de ciclos en ese evento" en su lugar. Finalmente veamos cómo optimar un código fuente en C mediante la célebre técnica de "desenrollar bucles" (de "loop unrolling"). En nuestro ya muy modificado programa, cambiemos el bucle de prueba por el siguiente: for (i=0 ; i<(num/2) ; i++) { test2(); test2(); } Si recompilamos y ejecutamos el programa, obtenemos: #gcc -o prueba2 prueba2.c -lm #./prueba2 1000 Numero de eventos: 3008 ¡Ahora el micro se pasa durmiendo en los saltos un 25% menos de tiempo! Claro que por otro lado ahora sólo resulta correcto el código cuando el número de vueltas es par. Pero supongo que coge la idea. Esto es todo respecto a nuestro tutorial acerca del dispositivo /dev/perfmon en FreeBSD. Espero que le haya resultado útil e interesante. Share and enjoy! ;) ----| Adaptando perfmon al K7. En esta sección indicamos cómo modificar el núcleo de FreeBSD de manera que el dispositivo perfmon funcione para microprocesadores AMD K7. No debería inten- tar esta modificación si su microprocesador no es un K7. Esto incluye micropro- cesadores de AMD anteriores, tales como el AMD K6-II. El autor ha probado el funcionamiento de perfmon en un K7-700 sin apreciar ningún error. Sin embargo, el código fuente debe considerarse en un estado muy temprano de desarrollo. Como podrá ver comparando los códigos fuente, las modificaciones realizadas al driver original de FreeBSD han sido pequeñas, así que no debería contener grandes problemas de seguridad ni funcionamiento. La aproximación que seguiremos para adaptar la fuente del núcleo será la de "mínimo impacto": modificaremos la menor cantidad de archivos en la fuente del núcleo (en total dos), y éstos serán modificados lo mínimo posible. Esto hará que la modificación no sea del todo elegante, pero sí fácil de realizar. Los archivos de la fuente del núcleo que es necesario modificar son: /sys/i386/i386/perfmon.c /sys/i386/include/perfmon.h Como primer paso de la modificación, es conveniente sacar copias de seguri- dad de esos dos archivos: #cp /sys/i386/i386/perfmon.c /sys/i386/i386/perfmon.c.old #cp /sys/i386/include/perfmon.h /sys/i386/include/perfmon.h.old A continuación, recorte los archivos perfmon.c y perfmon.h de los apéndices A y B respectivamente, y sustitúyalos por los originales: #cp perfmon.c /sys/i386/i386/perfmon.c #cp perfmon.h /sys/i386/include/perfmon.h Con estas modificaciones, ya puede intentar compilar el núcleo con soporte para Athlon. Para ello genere un archivo de configuración del núcleo que conten- ga (aparte de las usuales) las líneas: cpu CPU_I686 options PERFMON Supongamos que a ese archivo de configuración lo ha llamado /sys/i386/conf/NUEVO. Basta hacer entonces una compilación de este núcleo. Desde el directorio /sys/i386/conf hay que seguir el proceso normal: #/usr/sbin/config NUEVO #cd ../../compile/NUEVO #make depend #make #make install Al reiniciar con este nuevo núcleo, el soporte experimental para K7 de perfmon estará activado. Finalmente, puede copiar perfmon.h en /usr/include/machine/ si quiere compilar para K7 alguno de los programas de prueba de la sección anterior. ----| Notas sobre perfmon.c y perfmon.h: En esta sección resumo las modificaciones realizadas a ambos ficheros para lograr la compatibilidad con el K7. Los datos acerca del K7 fueron extraidos de [4], Apéndice D. Si desea comprender a fondo el código, necesitará consultar el archivo /sys/i386/include/cpufunc.h, que contiene las macros en código máquina que permiten leer y escribir los PMCs (macros rdpmc(), wrpmc(), rdmsr() y wrmsr()), leer el TSC (macro rdtsc()), y enmascarar interrupciones (macros enable_intr(), disable_intr()). Una referencia completa sobre estas instruccio- nes en ensamblador se encuentra en [2]. El fichero perfmon.h es una ampliación del original que incluye las máscaras para los PMCs del K7. Es totalmente compatible con el perfmon.h original. Para mantener esa compatibilidad sólo se permite usar dos de los cuatro PMCs del K7 (dado que el P5 y el P6 tienen tan sólo dos contadores). Los nombres de las máscaras para el K7 los he tomado como mejor me parecía, dado que en [4] no indican nombres para ellas, al contrario que en las especificaciones de Intel. Aunque algunas de las máscaras del K7 se corresponden con las de Intel, no las he llamado de la misma manera, dado que las correspondencias están muy lejos de ser completas. El fichero perfmon.c es incompatible con el original. Aunque ha sido escrito para ser compatible con el original, he encerrado entre comentarios (/* ... */) varias líneas de código, dado que de otra manera habría que modificar el Makefile del núcleo para instalarlo, y eso complicaría mucho el proceso descrito en la sección anterior (habría que andar modificando archivos make y demás). Todo lo que se ha añadido en el fichero es un nuevo case en el switch del principio (case CPUCLASS_K7), y una nueva función de escritura de PMCs, la writectlk7. También he incluido las definiciones MASK_INTEL y MASK_K7 en el principio del programa, dado que el K7 tiene los contadores de eventos más grandes que los modelos de Intel. El código de perfmon.c fue escrito para poder ser ampliado fácilmente, así que las modificaciones han sido mínimas. ----| Por hacer: Me he permitido dar a la versión inicial de este documento el número 0.9. Sin embargo, quedan aún bastantes cosas por hacer. Entre ellas: - Integrar perfmon.c en el kernel de manera que sea compatible hacia atrás, no un parche como hasta ahora. - Añadir a perfmon.h los nuevos eventos para Pentium II & III que se encuen- tran en [3], Apéndice A. - Pulir en perfmon.c cosas como el límite de dos contadores para el K7, y la cutre resolución del problema de la diferencia de máscaras entre los Intel y los Athlon (usando un #define). Se pueden dejar las cosas como están, pero estaríamos desperdiciando el potencial del K7 en ambos aspectos. - Encontrar cobayas^H^H^H^H^H^H^H beta-testers para el parche en micros AMD K7. Bromas aparte, sería interesante que algunos usuarios de micros K7 me enviaran resultados relativos al parche. Con los resultados de mi micro y el de alguno de mis amigos no puedo confiar del todo en que esté libre de bugs. ¡Geeks de AMD, uníos! ----| Referencias: (estas tres referencias son accesibles en www.intel.com) [1] Intel Software Developer's Manual Vol. 1: Basic Architecture, Order #243190. Intel Corporation. [2] Intel Software Developer's Manual Vol. 2: Instruction Set Reference, Order #243191. Intel Corporation. [3] Intel Software Developer's Manual Vol. 3: System Programming Guide, Order #243192. Intel Corporation. (esta referencia es accesible en www.amd.com) [4] AMD Athlon(tm) Processor x86 Code Optimization Guide, Order #22007/0, September 2000. Advanced Micro Devices. ----| Apéndices: |---| Apéndice A: perfmon.c |--------------------------------------------------| /* * Copyright 1996 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/i386/perfmon.c,v 1.21 1999/09/25 18:24:04 phk Exp $ */ /* Modified for K7 compatibility, 2/2/2002 */ #include #include #include #include #ifndef SMP #include #endif #include #include static int perfmon_inuse; static int perfmon_cpuok; #ifndef SMP static int msr_ctl[NPMC]; #endif static int msr_pmc[NPMC]; static unsigned int ctl_shadow[NPMC]; static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */ static int (*writectl)(int); #ifndef SMP static int writectl5(int); static int writectl6(int); static int writectlk7(int); #endif static d_close_t perfmon_close; static d_t perfmon_ static d_ioctl_t perfmon_ioctl; #define CDEV_MAJOR 2 /* We're really a minor of mem.c */ static struct cdevsw perfmon_cdevsw = { /**/ perfmon_ /* close */ perfmon_close, /* read */ noread, /* write */ nowrite, /* ioctl */ perfmon_ioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "perfmon", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #define MASK_INTEL 0xffffffffffULL /* P5 y P6 tienen contadores de 40 bits. */ #define MASK_K7 0xffffffffffffULL /* K7 tiene contadores de 48 bits. */ /* * Must be called after cpu_class is set up. */ void perfmon_init(void) { #ifndef SMP /* Este switch esta modificado para incluir soporte a K7 * con la opcion "cpu CPU_I686" en el nucleo. Es algo * sucio pero mejor que andar cambiando el Makefile del * kernel. */ switch(cpu_class) { case CPUCLASS_586: perfmon_cpuok = 1; msr_ctl[0] = 0x11; msr_ctl[1] = 0x11; msr_pmc[0] = 0x12; msr_pmc[1] = 0x13; writectl = writectl5; break; case CPUCLASS_686: /* perfmon_cpuok = 1; msr_ctl[0] = 0x186; msr_ctl[1] = 0x187; msr_pmc[0] = 0xc1; msr_pmc[1] = 0xc2; writectl = writectl6; break; case CPUCLASS_K7: */ perfmon_cpuok = 1; msr_ctl[0] = 0xc0010003; msr_ctl[1] = 0xc0010002; msr_ctl[2] = 0xc0010001; msr_ctl[3] = 0xc0010000; msr_pmc[0] = 0xc0010007; msr_pmc[1] = 0xc0010006; msr_pmc[2] = 0xc0010005; msr_pmc[3] = 0xc0010004; writectl = writectlk7; break; default: perfmon_cpuok = 0; break; } #endif /* SMP */ make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon"); } int perfmon_avail(void) { return perfmon_cpuok; } int perfmon_setup(int pmc, unsigned int control) { if (pmc < 0 || pmc >= NPMC) return EINVAL; perfmon_inuse |= (1 << pmc); control &= ~(PMCF_SYS_FLAGS << 16); disable_intr(); ctl_shadow[pmc] = control; writectl(pmc); wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); enable_intr(); return 0; } int perfmon_get(int pmc, unsigned int *control) { if (pmc < 0 || pmc >= NPMC) return EINVAL; if (perfmon_inuse & (1 << pmc)) { *control = ctl_shadow[pmc]; return 0; } return EBUSY; /* XXX reversed sense */ } int perfmon_fini(int pmc) { if (pmc < 0 || pmc >= NPMC) return EINVAL; if (perfmon_inuse & (1 << pmc)){ perfmon_stop(pmc); ctl_shadow[pmc] = 0; perfmon_inuse &= ~(1 << pmc); return 0; } return EBUSY; /* XXX reversed sense */ } int perfmon_start(int pmc) { if (pmc < 0 || pmc >= NPMC) return EINVAL; if (perfmon_inuse & (1 << pmc)) { disable_intr(); ctl_shadow[pmc] |= (PMCF_EN << 16); wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); writectl(pmc); enable_intr(); return 0; } return EBUSY; } int perfmon_stop(int pmc) { if (pmc < 0 || pmc >= NPMC) return EINVAL; if (perfmon_inuse & (1 << pmc)) { disable_intr(); pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & MASK_K7; ctl_shadow[pmc] &= ~(PMCF_EN << 16); writectl(pmc); enable_intr(); return 0; } return EBUSY; } int perfmon_read(int pmc, quad_t *val) { if (pmc < 0 || pmc >= NPMC) return EINVAL; if (perfmon_inuse & (1 << pmc)) { if (ctl_shadow[pmc] & (PMCF_EN << 16)) *val = rdmsr(msr_pmc[pmc]) & MASK_K7; else *val = pmc_shadow[pmc]; return 0; } return EBUSY; } int perfmon_reset(int pmc) { if (pmc < 0 || pmc >= NPMC) return EINVAL; if (perfmon_inuse & (1 << pmc)) { wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); return 0; } return EBUSY; } #ifndef SMP /* * Unfortunately, the performance-monitoring registers are laid out * differently in the P5 and P6. We keep everything in P6 format * internally (except for the event code), and convert to P5 * format as needed on those CPUs. The writectl function pointer * is set up to point to one of these functions by perfmon_init(). */ /* La funcion especifica de K7 es muy simple. */ int writectlk7(int pmc) { wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); return 0; } int writectl6(int pmc) { if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { wrmsr(msr_ctl[pmc], 0); } else { wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); } return 0; } #define P5FLAG_P 0x200 #define P5FLAG_E 0x100 #define P5FLAG_USR 0x80 #define P5FLAG_OS 0x40 int writectl5(int pmc) { quad_t newval = 0; if (ctl_shadow[1] & (PMCF_EN << 16)) { if (ctl_shadow[1] & (PMCF_USR << 16)) newval |= P5FLAG_USR << 16; if (ctl_shadow[1] & (PMCF_OS << 16)) newval |= P5FLAG_OS << 16; if (!(ctl_shadow[1] & (PMCF_E << 16))) newval |= P5FLAG_E << 16; newval |= (ctl_shadow[1] & 0x3f) << 16; } if (ctl_shadow[0] & (PMCF_EN << 16)) { if (ctl_shadow[0] & (PMCF_USR << 16)) newval |= P5FLAG_USR; if (ctl_shadow[0] & (PMCF_OS << 16)) newval |= P5FLAG_OS; if (!(ctl_shadow[0] & (PMCF_E << 16))) newval |= P5FLAG_E; newval |= ctl_shadow[0] & 0x3f; } wrmsr(msr_ctl[0], newval); return 0; /* XXX should check for unimplemented bits */ } #endif /* !SMP */ /* * Now the user-mode interface, called from a subdevice of mem.c. */ static int writer; static int writerpmc; static int perfmon_dev_t dev, int flags, int fmt, struct proc *p) { if (!perfmon_cpuok) return ENXIO; if (flags & FWRITE) { if (writer) { return EBUSY; } else { writer = 1; writerpmc = 0; } } return 0; } static int perfmon_close(dev_t dev, int flags, int fmt, struct proc *p) { if (flags & FWRITE) { int i; for (i = 0; i < NPMC; i++) { if (writerpmc & (1 << i)) perfmon_fini(i); } writer = 0; } return 0; } static int perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct proc *p) { struct pmc *pmc; struct pmc_data *pmcd; struct pmc_tstamp *pmct; int *ip; int rv; switch(cmd) { case PMIOSETUP: if (!(flags & FWRITE)) return EPERM; pmc = (struct pmc *)param; rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); if (!rv) { writerpmc |= (1 << pmc->pmc_num); } break; case PMIOGET: pmc = (struct pmc *)param; rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); break; case PMIOSTART: if (!(flags & FWRITE)) return EPERM; ip = (int *)param; rv = perfmon_start(*ip); break; case PMIOSTOP: if (!(flags & FWRITE)) return EPERM; ip = (int *)param; rv = perfmon_stop(*ip); break; case PMIORESET: if (!(flags & FWRITE)) return EPERM; ip = (int *)param; rv = perfmon_reset(*ip); break; case PMIOREAD: pmcd = (struct pmc_data *)param; rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); break; case PMIOTSTAMP: if (!tsc_freq) { rv = ENOTTY; break; } pmct = (struct pmc_tstamp *)param; /* XXX interface loses precision. */ pmct->pmct_rate = tsc_freq / 1000000; pmct->pmct_value = rdtsc(); rv = 0; break; default: rv = ENOTTY; } return rv; } |------------------------------------------------------------------------------| |---| Apéndice B: perfmon.h |--------------------------------------------------| /* * Copyright 1996 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/include/perfmon.h,v 1.7 1999/12/29 04:33:04 peter Exp $ */ /* Modified for K7 compatibility, 2/2/2002 */ /* * Interface to performance-monitoring counters for Intel Pentium and * Pentium Pro CPUs. */ #ifndef _MACHINE_PERFMON_H_ #define _MACHINE_PERFMON_H_ #ifndef _KERNEL #include #endif #include #define NPMC 2 /* FIXME : PUEDER SER HASTA 4 PARA ATHLON */ #define PMIOSETUP _IOW('5', 1, struct pmc) #define PMIOGET _IOWR('5', 7, struct pmc) #define PMIOSTART _IOW('5', 2, int) #define PMIOSTOP _IOW('5', 3, int) #define PMIOREAD _IOWR('5', 4, struct pmc_data) #define PMIORESET _IOW('5', 5, int) #define PMIOTSTAMP _IOR('5', 6, struct pmc_tstamp) struct pmc { int pmc_num; union { struct { unsigned char pmcus_event; unsigned char pmcus_unit; unsigned char pmcus_flags; unsigned char pmcus_mask; } pmcu_s; unsigned int pmcu_val; } pmc_pmcu; }; #define PMC_ALL (-1) #define pmc_event pmc_pmcu.pmcu_s.pmcus_event #define pmc_unit pmc_pmcu.pmcu_s.pmcus_unit #define pmc_flags pmc_pmcu.pmcu_s.pmcus_flags #define pmc_mask pmc_pmcu.pmcu_s.pmcus_mask #define pmc_val pmc_pmcu.pmcu_val #define PMCF_USR 0x01 /* count events in user mode */ #define PMCF_OS 0x02 /* count events in kernel mode */ #define PMCF_E 0x04 /* use edge-detection mode */ #define PMCF_PC 0x08 /* PMx output pin control */ #define PMCF_INT 0x10 /* APIC interrupt enable (do not use) */ #define PMCF_EN 0x40 /* enable counters */ #define PMCF_INV 0x80 /* invert counter mask comparison */ #define PMCF_SYS_FLAGS (PMCF_INT | PMCF_EN) /* user cannot set */ struct pmc_data { int pmcd_num; quad_t pmcd_value; }; struct pmc_tstamp { int pmct_rate; quad_t pmct_value; }; #ifndef _KERNEL #define _PATH_PERFMON "/dev/perfmon" #else /* * Intra-kernel interface to performance monitoring counters */ void perfmon_init __P((void)); int perfmon_avail __P((void)); int perfmon_setup __P((int, unsigned int)); int perfmon_get __P((int, unsigned int *)); int perfmon_fini __P((int)); int perfmon_start __P((int)); int perfmon_stop __P((int)); int perfmon_read __P((int, quad_t *)); int perfmon_reset __P((int)); #endif /* _KERNEL */ /* * AMD K7 Athlon performance counters, from * "AMD Athlon(tm) Processor x86 Code Optimization Guide" * by AMD, 22007/0 - September 2000 */ /* Data Cache Unit. */ #define K7_DC_ACCESSES 0x40 #define K7_DC_MISSES 0x41 #define K7_DC_L2REFILL 0x42 #define K7_DC_SYSREFILL 0x43 #define K7_DC_WRITEBACK 0x44 #define K7_DC_L1MISS_L2HIT 0x45 #define K7_DC_L1MISS_L2MISS 0x46 #define K7_DC_MISDAT 0x47 /* Instruction Cache Unit. */ #define K7_PC_IFETCH 0x80 #define K7_PC_IFETCH_MISS 0x81 #define K7_PC_ITLB_L1ML2H 0x84 #define K7_PC_ITLB_MISS 0x85 /* Retirement events and interruptions. */ #define K7_FR_RET_INST 0xc0 #define K7_FR_RET_OPS 0xc1 #define K7_FR_RET_BRANCH 0xc2 #define K7_FR_RET_MISBRANCH 0xc3 #define K7_FR_RET_TAKBRANCH 0xc4 #define K7_FR_RET_MTBRANCH 0xc5 #define K7_FR_RET_FCTRANS 0xc6 #define K7_FR_RET_RRBRANCH 0xc7 #define K7_FR_INT_MASK 0xcd #define K7_FR_INT_MASKWP 0xce #define K7_FR_INT_HARDMASK 0xcd /* * Pentium Pro performance counters, from Appendix B. */ /* Data Cache Unit */ #define PMC6_DATA_MEM_REFS 0x43 #define PMC6_DCU_LINES_IN 0x45 #define PMC6_DCU_M_LINES_IN 0x46 #define PMC6_DCU_M_LINES_OUT 0x47 #define PMC6_DCU_MISS_OUTSTANDING 0x48 /* Instruction Fetch Unit */ #define PMC6_IFU_IFETCH 0x80 #define PMC6_IFU_IFETCH_MISS 0x81 #define PMC6_ITLB_MISS 0x85 #define PMC6_IFU_MEM_STALL 0x86 #define PMC6_ILD_STALL 0x87 /* L2 Cache */ #define PMC6_L2_IFETCH 0x28 /* MESI */ #define PMC6_L2_LD 0x29 /* MESI */ #define PMC6_L2_ST 0x2a /* MESI */ #define PMC6_L2_LINES_IN 0x24 #define PMC6_L2_LINES_OUT 0x26 #define PMC6_L2_M_LINES_INM 0x25 #define PMC6_L2_M_LINES_OUTM 0x27 #define PMC6_L2_RQSTS 0x2e /* MESI */ #define PMC6_L2_ADS 0x21 #define PMC6_L2_DBUS_BUSY 0x22 #define PMC6_L2_DBUS_BUSY_RD 0x23 /* External Bus Logic */ #define PMC6_BUS_DRDY_CLOCKS 0x62 #define PMC6_BUS_LOCK_CLOCKS 0x63 #define PMC6_BUS_REQ_OUTSTANDING 0x60 #define PMC6_BUS_TRAN_BRD 0x65 #define PMC6_BUS_TRAN_RFO 0x66 #define PMC6_BUS_TRAN_WB 0x67 #define PMC6_BUS_TRAN_IFETCH 0x68 #define PMC6_BUS_TRAN_INVAL 0x69 #define PMC6_BUS_TRAN_PWR 0x6a #define PMC6_BUS_TRAN_P 0x6b #define PMC6_BUS_TRAN_IO 0x6c #define PMC6_BUS_TRAN_DEF 0x6d #define PMC6_BUS_TRAN_BURST 0x6e #define PMC6_BUS_TRAN_ANY 0x70 #define PMC6_BUS_TRAN_MEM 0x6f #define PMC6_BUS_DATA_RCV 0x64 #define PMC6_BUS_BNR_DRV 0x61 #define PMC6_BUS_HIT_DRV 0x7a #define PMC6_BUS_HITM_DRV 0x7b #define PMC6_BUS_SNOOP_STALL 0x7e /* Floating Point Unit */ #define PMC6_FLOPS 0xc1 /* counter 0 only */ #define PMC6_FP_COMP_OPS_EXE 0x10 /* counter 0 only */ #define PMC6_FP_ASSIST 0x11 /* counter 1 only */ #define PMC6_MUL 0x12 /* counter 1 only */ #define PMC6_DIV 0x13 /* counter 1 only */ #define PMC6_CYCLES_DIV_BUSY 0x14 /* counter 0 only */ /* Memory Ordering */ #define PMC6_LD_BLOCKS 0x03 #define PMC6_SB_DRAINS 0x04 #define PMC6_MISALIGN_MEM_REF 0x05 /* Instruction Decoding and Retirement */ #define PMC6_INST_RETIRED 0xc0 #define PMC6_UOPS_RETIRED 0xc2 #define PMC6_INST_DECODER 0xd0 /* (sic) */ /* Interrupts */ #define PMC6_HW_INT_RX 0xc8 #define PMC6_CYCLES_INT_MASKED 0xc6 #define PMC6_CYCLES_INT_PENDING_AND_MASKED 0xc7 /* Branches */ #define PMC6_BR_INST_RETIRED 0xc4 #define PMC6_BR_MISS_PRED_RETIRED 0xc5 #define PMC6_BR_TAKEN_RETIRED 0xc9 #define PMC6_BR_MISS_PRED_TAKEN_RET 0xca #define PMC6_BR_INST_DECODED 0xe0 #define PMC6_BTB_MISSES 0xe2 #define PMC6_BR_BOGUS 0xe4 #define PMC6_BACLEARS 0xe6 /* Stalls */ #define PMC6_RESOURCE_STALLS 0xa2 #define PMC6_PARTIAL_RAT_STALLS 0xd2 /* Segment Register Loads */ #define PMC6_SEGMENT_REG_LOADS 0x06 /* Clocks */ #define PMC6_CPU_CLK_UNHALTED 0x79 /* * Pentium Performance Counters * This list comes from the Harvard people, not Intel. */ #define PMC5_DATA_READ 0 #define PMC5_DATA_WRITE 1 #define PMC5_DATA_TLB_MISS 2 #define PMC5_DATA_READ_MISS 3 #define PMC5_DATA_WRITE_MISS 4 #define PMC5_WRITE_M_E 5 #define PMC5_DATA_LINES_WBACK 6 #define PMC5_DATA_CACHE_SNOOP 7 #define PMC5_DATA_CACHE_SNOOP_HIT 8 #define PMC5_MEM_ACCESS_BOTH 9 #define PMC5_BANK_CONFLICTS 10 #define PMC5_MISALIGNED_DATA 11 #define PMC5_INST_READ 12 #define PMC5_INST_TLB_MISS 13 #define PMC5_INST_CACHE_MISS 14 #define PMC5_SEGMENT_REG_LOAD 15 #define PMC5_BRANCHES 18 #define PMC5_BTB_HITS 19 #define PMC5_BRANCH_TAKEN 20 #define PMC5_PIPELINE_FLUSH 21 #define PMC5_INST_EXECUTED 22 #define PMC5_INST_EXECUTED_V 23 #define PMC5_BUS_UTILIZATION 24 #define PMC5_WRITE_BACKUP_STALL 25 #define PMC5_DATA_READ_STALL 26 #define PMC5_WRITE_E_M_STALL 27 #define PMC5_LOCKED_BUS 28 #define PMC5_IO_CYCLE 29 #define PMC5_NONCACHE_MEMORY 30 #define PMC5_ADDR_GEN_INTERLOCK 31 #define PMC5_FLOPS 34 #define PMC5_BP0_MATCH 35 #define PMC5_BP1_MATCH 36 #define PMC5_BP2_MATCH 37 #define PMC5_BP3_MATCH 38 #define PMC5_HW_INTR 39 #define PMC5_DATA_RW 40 #define PMC5_DATA_RW_MISS 41 #endif /* !_MACHINE_PERFMON_H_ */ |------------------------------------------------------------------------------|

eldemonio.org El site BSD en Castellano Articles catalogue

Website templates are pre-designed websites all you need to do is add your own personal content and you're ready to jump start your own website. Website templates by Vooweb

eldemonio.org v 4_2