2010-06-06 9 views
7

Así que he estado jugando con pthreads, específicamente tratando de calcular el producto de dos matrices. Mi código es extremadamente complicado, ya que se acaba supone que es un proyecto divertido poco rápido para mí, pero la teoría hilo que utilicé fue muy similar a:Multiplicación de matriz con subprocesos: ¿Por qué no es más rápido?

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 

#define M 3 
#define K 2 
#define N 3 
#define NUM_THREADS 10 

int A [M][K] = { {1,4}, {2,5}, {3,6} }; 
int B [K][N] = { {8,7,6}, {5,4,3} }; 
int C [M][N]; 

struct v { 
    int i; /* row */ 
    int j; /* column */ 
}; 

void *runner(void *param); /* the thread */ 

int main(int argc, char *argv[]) { 

    int i,j, count = 0; 
    for(i = 0; i < M; i++) { 
     for(j = 0; j < N; j++) { 
     //Assign a row and column for each thread 
     struct v *data = (struct v *) malloc(sizeof(struct v)); 
     data->i = i; 
     data->j = j; 
     /* Now create the thread passing it data as a parameter */ 
     pthread_t tid;  //Thread ID 
     pthread_attr_t attr; //Set of thread attributes 
     //Get the default attributes 
     pthread_attr_init(&attr); 
     //Create the thread 
     pthread_create(&tid,&attr,runner,data); 
     //Make sure the parent waits for all thread to complete 
     pthread_join(tid, NULL); 
     count++; 
     } 
    } 

    //Print out the resulting matrix 
    for(i = 0; i < M; i++) { 
     for(j = 0; j < N; j++) { 
     printf("%d ", C[i][j]); 
    } 
     printf("\n"); 
    } 
} 

//The thread will begin control in this function 
void *runner(void *param) { 
    struct v *data = param; // the structure that holds our data 
    int n, sum = 0; //the counter and sum 

    //Row multiplied by column 
    for(n = 0; n< K; n++){ 
     sum += A[data->i][n] * B[n][data->j]; 
    } 
    //assign the sum to its coordinate 
    C[data->i][data->j] = sum; 

    //Exit the thread 
    pthread_exit(0); 
} 

fuente: http://macboypro.com/blog/2009/06/29/matrix-multiplication-in-c-using-pthreads-on-linux/

Para los no roscada versión, utilicé la misma configuración (3 matrices de 2 d, estructuras dinámicamente asignadas para mantener r/c) y agregué un temporizador. Los primeros ensayos indicaron que la versión sin hilos era más rápida. Lo primero que pensé fue que las dimensiones eran demasiado pequeñas para notar una diferencia, y se tardó más tiempo en crear los hilos. Así que aumenté las dimensiones a aproximadamente 50x50, llené aleatoriamente y lo ejecuté, y todavía no veo ninguna actualización de rendimiento con la versión enhebrada.

¿Qué me falta aquí?

+3

¿En qué tipo de procesador lo está ejecutando? Si no es multiproceso o de doble núcleo, no verá ninguna ventaja de usar múltiples hilos. De hecho, la conmutación de contexto que debe tener lugar para ejecutar ambos subprocesos simultáneamente puede dañar el rendimiento. –

+0

Una matriz de 50x50 es demasiado pequeña para las computadoras modernas. Si está buscando problemas de rendimiento, tendrá que pasar a tamaños mucho más grandes: decenas de miles de filas y columnas. Entonces, es importante buscar optimizaciones (con multihilo, por ejemplo). – PeterK

Respuesta

11

A menos que trabaje con muy matrices grandes (muchos miles de filas/columnas), entonces es poco probable que vea una gran mejora con este enfoque. Configurar un hilo en una CPU/SO moderna es realmente bastante caro en términos relativos de tiempo de CPU, mucho más tiempo que unas pocas operaciones de multiplicación.

Además, generalmente no vale la pena configurar más de un hilo por núcleo de CPU que tenga disponible. Si tiene, por ejemplo, solo dos núcleos y configura 2500 hilos (para matrices de 50x50), entonces el sistema operativo va a dedicar todo su tiempo a administrar y cambiar entre esos 2500 hilos en lugar de hacer sus cálculos.

Si tuviera que configurar dos hilos de antemano (suponiendo una CPU de dos núcleos), mantenga esos hilos disponibles todo el tiempo esperando que ocurra el trabajo, y proporcione los productos de 2500 puntos que necesita calcular en algunos tipo de cola de trabajo sincronizada, entonces podría empezar a ver una mejora. Sin embargo, todavía no será más del 50% mejor que usar solo un núcleo.

+0

La única advertencia es la situación en la que tienes un hilo de interfaz de usuario y un hilo de trabajo. –

+1

@Chris Thompson: es improbable que el hilo de UI esté utilizando mucha potencia de la CPU. La ventaja de tener un subproceso de interfaz de usuario separado es no * bloquear * el subproceso de la interfaz de usuario mientras realiza el cálculo, lo que mantiene su UI receptiva. –

+0

a la derecha. Eso es lo que quise decir :-) –

1

No permite mucha ejecución en paralelo: espera la secuencia inmediatamente después de crearla, por lo que casi no hay forma de que su programa use CPU adicionales (es decir, nunca puede usar una tercera CPU/núcleo). Intente permitir que se ejecuten más subprocesos (probablemente hasta el recuento de núcleos que tenga).

7

No estoy del todo seguro de entender el código fuente, pero esto es lo que parece: Tiene un bucle que se ejecuta M * N veces. Cada vez que pasa el ciclo, crea un hilo que completa un número en la matriz de resultados. Pero justo después de iniciar el hilo, esperas a que se complete. No creo que realmente estés ejecutando más de un hilo.

Incluso si estaba ejecutando más de un hilo, el hilo está haciendo una cantidad trivial de trabajo. Incluso si K era grande (mencionas 50), 50 multiplicaciones no es mucho comparado con el costo de comenzar el hilo en primer lugar. El programa debería crear menos hilos, ciertamente no más que la cantidad de procesadores, y asignar más trabajo a cada uno.

+1

Respuesta perfecta. Este debería ser el aceptado. – Guido

1

Si tiene un procesador con dos núcleos, entonces debe dividir el trabajo en dos partes y dar a cada hilo la mitad. El mismo principio si tienes 3, 4, 5 núcleos. El diseño de rendimiento óptimo siempre coincidirá con el número de subprocesos con el número de núcleos disponibles (por disponible quiero decir núcleos que otros procesos ya no están usando).

Otra cosa que debes tener en cuenta es que cada hilo debe tener sus datos contiguos e independientes de los datos de los otros hilos.De lo contrario, las fallas de Memcache ralentizarán significativamente el procesamiento.

Para comprender mejor estas cuestiones, me gustaría recomendar el libro Patrones de Programación Paralela http://astore.amazon.com/amazon-books-20/detail/0321228111

A pesar de sus ejemplos de código están más dirigidas a OpenMP & MPI, y está usando PThreads, siendo la primera la mitad del libro es muy rica en conceptos fundamentales & funcionamiento interno de entornos de subprocesamiento múltiple, muy útil para evitar la mayoría de los cuellos de botella de rendimiento que encontrará.

0

Siempre que el código se paralelice correctamente (no lo verificaré), el rendimiento probable solo aumenta cuando el código se paraleliza en el hardware, es decir, los hilos son realmente paralelos (multinúcleos, múltiples cpus, ... otras tecnologías ...) y no aparentemente (modo "multitarea") paralelo. Solo una idea, no estoy seguro de que este sea el caso.

Cuestiones relacionadas