2011-08-08 10 views
19

Supongamos que cada hilo está haciendo un cálculo de FP, estoy interesado en¿cómo se puede medir el tiempo pasado en un cambio de contexto bajo la plataforma java

  • cuánto tiempo se utiliza la CPU en el cambio de hilos en lugar de correr ellos
  • la cantidad de tráfico de sincronización se crea en el bus de memoria compartida - cuando los datos hilos comparten, deben utilizar mecanismo de sincronización

Mi pregunta: ¿cómo diseñar un programa de prueba para obtener estos datos?

Respuesta

1

la cantidad de tiempo de CPU se utiliza en el cambio de hilos en lugar de correr ellos

  • Digamos que tiene 100 millones de dólares para llevar a cabo la FPU.
  • cargarlos en una cola sincronizado (es decir, las roscas deben bloquear la cola cuando sondeo)
  • Sea n el número de procesadores disponibles en el dispositivo (dúo = 2, etc ...)

Luego crea n subprocesos chupando en la cola para realizar todos los FPU. Puede calcular el tiempo total con System.currentTimeMillis() antes y después. Luego intente con n + 1 hilos, luego n + 2, n + 3, etc ...

En teoría, cuantos más hilos tenga, mientras más cambios haya, más tiempo le tomará procesar todo FPU. Le dará una idea muy aproximada de la sobrecarga de conmutación, pero esto es difícil de medir.

la cantidad de tráfico de sincronización se crea en el bus de memoria compartida - cuando los datos hilos comparten, deben utilizar mecanismo de sincronización

crearía 10 hilos de envío de cada 10 000 mensajes a otro hilo al azar mediante el uso de una cola de bloqueo sincronizada de 100 mensajes. Cada hilo echa un vistazo a la cola de bloqueo para verificar si el mensaje es para ellos o no y sacarlo si es verdadero. Luego, tratarían de enviar un mensaje sin bloquear, luego repetir la operación de inspección, etc. hasta que la cola esté vacía y todos los hilos regresen.

En su camino, cada hilo podría tener la cantidad de éxito de empuje y vistazo/extracción versus sin éxito. Entonces, tendrías una idea aproximada de trabajo útil versus trabajo inútil en el tráfico de sincronización. De nuevo, esto es difícil de medir.

Por supuesto, podría jugar con el número de hilos o el tamaño de la cola de bloqueo también.

9

No se puede diferenciar fácilmente el desperdicio debido a la conmutación de subprocesos y debido a la contención de la memoria caché. Usted PUEDE medir la contención del hilo. A saber, en Linux, puede cat/proc/PID/XXX y obtener toneladas de estadísticas detalladas por cada hilo. SIN EMBARGO, dado que el programador preventivo no se disparará en el pie, no obtendrá más que 30 conmutadores de ctx por segundo, sin importar cuántos hilos use. Y ese tiempo será relativamente pequeño vs la cantidad de trabajo que está haciendo ... El costo real del cambio de contexto es la contaminación del caché. p.ej. hay una alta probabilidad de que tenga la mayoría de las fallas de caché una vez que vuelva a conectar el contexto. Por lo tanto, el tiempo de SO y los conteos de cambio de contexto tienen un valor mínimo.

Lo que REALMENTE es valioso es la proporción de suciedad de la línea de caché entre hilos. Dependiendo de la CPU, una línea de caché sucia seguida de una lectura de CPU par es más LENTA que una falta de caché, porque tienes que forzar a la CPU par a escribir su valor en la memoria principal antes de que puedas comenzar a leer. Algunos Las CPU le permiten extraer de las líneas de caché del mismo nivel sin tocar la memoria principal.

Así que la clave es la absolutamente minimizar cualquier estructuras modificadas de memoria compartida .. Hacer todo como de sólo lectura como sea posible .. Esto incluye la parte memorias intermedias FIFO (incluyendo piscinas ejecutor) .. A saber si se ha utilizado una cola sincronizada - luego cada sync-op es una región de memoria sucia compartida. Y más aún, si la tasa es lo suficientemente alta, probablemente activará una trampa del sistema operativo para detenerse, a la espera de mutex del hilo de igual.

Lo ideal es segmentar RAM, distribuir a un número fijo de trabajadores una sola unidad grande de trabajo, luego utilizar un bloqueo de cuenta regresiva u otra barrera de memoria (de modo que cada hilo solo lo toque una vez). Idealmente, cualquier búfer temporal se preasignan en lugar de entrar y salir de un grupo de memoria compartida (que a su vez causa la contención de la memoria caché). Los bloques "sincronizados" de Java aprovechan (detrás de las escenas) un espacio de memoria de tabla hash compartido y desencadenan así las lecturas sucias no deseadas, no he determinado si los objetos java 5 Lock lo evitan, pero todavía está aprovechando los puestos de OS que ganó No ayuda en su rendimiento. Obviamente, la mayoría de las operaciones de OutputStream desencadenan dichas llamadas sincronizadas (y, por supuesto, suelen llenar un búfer de transmisión común).

En general, mi experiencia es que el subproceso único es más rápido que el mulithreading para una matriz de bytes común/matriz de objetos, etc. Al menos con algoritmos de clasificación/filtrado simplistas con los que he experimentado. Esto es cierto tanto en Java como en C en mi experiencia. No he probado operaciones intuitivas de FPU (como divisiones, sqrt), donde las líneas de caché pueden ser menos importantes.

Básicamente, si usted es una sola CPU, no tiene problemas con la línea de caché (a menos que el sistema operativo siempre esté descargando la memoria caché incluso en subprocesos compartidos), pero la multitoma le compra menos que nada. En hyperthreading, es el mismo trato. En configuraciones de caché L2/L3 compartidas de una sola CPU (por ejemplo, AMD), es posible que encuentre algún beneficio. En el bus Intel de múltiples CPU, olvídalo: la memoria de escritura compartida es peor que la del subproceso único.

+0

Si no está buscando diseñar una aplicación, sino simplemente medir la diferencia en el rendimiento (volviendo a leer su pregunta). Luego, con un poco de suerte, el algoritmo se puede dividir linealmente y luego pasar a una cantidad configurable de hilos, con 1 posiblemente una ruta de código alternativa especial. A continuación, ejecute cada uno (posiblemente teniendo pre-exit zip up/proc/self/*). También use registrar/informar el nano-tiempo del inicio/finalización de cada hilo (más bien el delta del mismo). –

2

para medir cuánto tiempo un cambio de contexto se correría algo como lo siguiente:

public static void main(String[] args) {  
    Object theLock = new Object(); 
    long startTime; 
    long endtime; 
    synchronized(theLock){ 
     Thread task = new TheTask(theLock); 
     task.start(); 
     try { 
      theLock.wait(); 
      endTime = System.currentTimeMillis(); 
     } 
     catch(InterruptedException e){ 
      // do something if interrupted 
     } 
    } 
    System.out.println("Context Switch Time elapsed: " + endTime - startTime); 
} 

class TheTask extends Thread { 
    private Object theLock; 
    public TheTask(Object theLock){ 
     this.theLock = theLock; 
    } 
    public void run(){ 
     synchronized(theLock){ 
      startTime = System.currentTimeMillis(); 
      theLock.notify(); 
     } 
    } 
} 

Es posible que desee ejecutar este código varias veces para obtener un promedio y asegurarse de que estos dos hilos son la única los que se ejecutan en su máquina (el cambio de contexto solo ocurre dentro de estos dos hilos).

Cuestiones relacionadas