Pregunta

Para uno de los proyectos que estoy haciendo en este momento, necesito mirar el rendimiento (entre otras cosas) de diferentes lenguajes de programación concurrentes .

En este momento estoy buscando comparar python sin pila y C ++ PThreads , por lo que el foco está en estos dos idiomas, pero probablemente se probarán otros idiomas más adelante. Por supuesto, la comparación debe ser lo más representativa y precisa posible, por lo que mi primer pensamiento fue comenzar a buscar algunos problemas de referencia concurrentes / multiproceso estándar, por desgracia no pude encuentre pruebas, problemas / puntos de referencia decentes o estándar.

Entonces mi pregunta es la siguiente: ¿Tiene alguna sugerencia para un problema bueno, fácil o rápido para probar el rendimiento del lenguaje de programación (y exponer sus puntos fuertes y débiles en el proceso )?

¿Fue útil?

Solución

Desde que el juego de benchmarks se trasladó a una máquina quad-core en septiembre de 2008, muchos programas en diferentes lenguajes de programación se han reescrito para explotar quad-core - por ejemplo, los primeros 10 programas de mandelbrot .

Otros consejos

¿Seguramente debería probar hardware y compiladores en lugar de un lenguaje para el rendimiento de concurrencia?

Me gustaría ver un lenguaje desde el punto de vista de cuán fácil y productivo es en términos de concurrencia y cuánto 'aísla' al programador de cometer errores de bloqueo.

EDIT: a partir de la experiencia pasada como investigador que diseña algoritmos paralelos, creo que en la mayoría de los casos el rendimiento concurrente dependerá en gran medida de cómo se paraleliza un algoritmo y cómo se dirige al hardware subyacente.

Además, los puntos de referencia son notoriamente desiguales; Esto es aún más en un entorno paralelo. Por ejemplo, un punto de referencia que 'procese' matrices muy grandes sería adecuado para un procesador de canalización de vectores, mientras que un ordenamiento paralelo podría ser más adecuado para CPU multi-núcleo de propósito más general.

Estos pueden ser útiles:

Puntos de referencia paralelos

Puntos de referencia paralelos NAS

Bueno, hay algunos clásicos, pero diferentes pruebas enfatizan diferentes características. Algunos sistemas distribuidos pueden ser más robustos, tener un paso de mensajes más eficiente, etc. Una mayor sobrecarga de mensajes puede afectar la escalabilidad, ya que la forma normal de escalar a más máquinas es enviar una mayor cantidad de mensajes pequeños. Algunos problemas clásicos que puede probar son un Tamiz de Eratóstenes distribuido o una calculadora de secuencia de Fibonacci mal implementada (es decir, para calcular el octavo número de la serie, girar una máquina para el séptimo y otro para el sexto). Casi cualquier algoritmo de divide y vencerás se puede hacer simultáneamente. También podría hacer una implementación concurrente del juego de la vida o transferencia de calor de Conway. Tenga en cuenta que todos estos algoritmos tienen enfoques diferentes y, por lo tanto, es probable que no obtenga un sistema distribuido con el mejor rendimiento en todos ellos.

Diría que la más fácil de implementar rápidamente es la calculadora de Fibonacci mal implementada, aunque pone demasiado énfasis en crear hilos y muy poca comunicación entre esos hilos.

  

Seguramente debería probar el hardware   y compiladores en lugar de un lenguaje   para el rendimiento de concurrencia?

No, el hardware y los compiladores son irrelevantes para mis propósitos de prueba. Solo estoy buscando algunos buenos problemas que puedan probar qué tan bien el código, escrito en un idioma, puede competir contra el código de otro idioma. Realmente estoy probando las construcciones disponibles en los lenguajes específicos para hacer programación concurrente. Y uno de los criterios es el rendimiento (medido en el tiempo).

Algunos de los otros criterios de prueba que estoy buscando son:

  • cuán fácil es escribir el código correcto; porque, como todos sabemos, la programación concurrente es más difícil que escribir programas de un solo hilo
  • cuál es la técnica utilizada para la programación concurrente: basada en eventos, basada en actores, análisis de mensajes, ...
  • cuánto código debe escribir el propio programador y cuánto se hace automáticamente para él: esto también se puede probar con los problemas de referencia dados
  • cuál es el nivel de abstracción y la cantidad de sobrecarga involucrada cuando se traduce de nuevo al código de máquina

Entonces, en realidad, no busco rendimiento como el único y el mejor parámetro (que de hecho me enviaría al hardware y los compiladores en lugar del lenguaje en sí), en realidad estoy buscando desde el punto de vista de los programadores para verificar qué lenguaje es el más adecuado para qué tipo de problemas, cuáles son sus debilidades y fortalezas, etc.

Tenga en cuenta que este es solo un proyecto pequeño y, por lo tanto, las pruebas también deben mantenerse pequeñas. (por lo tanto, no es posible realizar pruebas rigurosas de todo)

He decidido utilizar el Mandelbrot set (el algoritmo de tiempo de escape para ser más precisos) para comparar los diferentes idiomas.
Me queda bastante bien, ya que el algoritmo original se puede implementar fácilmente y crear la variante multiproceso a partir de él no es mucho trabajo.

a continuación está el código que tengo actualmente. Todavía es una variante de subproceso único, pero la actualizaré tan pronto como esté satisfecho con el resultado.

#include <cstdlib> //for atoi
#include <iostream>
#include <iomanip> //for setw and setfill
#include <vector>


int DoThread(const double x, const double y, int maxiter) {
    double curX,curY,xSquare,ySquare;
    int i;

    curX = x + x*x - y*y;
    curY = y + x*y + x*y;
    ySquare = curY*curY;
    xSquare = curX*curX;

    for (i=0; i<maxiter && ySquare + xSquare < 4;i++) {
      ySquare = curY*curY;
      xSquare = curX*curX;
      curY = y + curX*curY + curX*curY;
      curX = x - ySquare + xSquare;
    }
    return i;
}

void SingleThreaded(int horizPixels, int vertPixels, int maxiter, std::vector<std::vector<int> >&  result) {
    for(int x = horizPixels; x > 0; x--) {
        for(int y = vertPixels; y > 0; y--) {
            //3.0 -> so we always have -1.5 -> 1.5 as the window; (x - (horizPixels / 2) will go from -horizPixels/2 to +horizPixels/2
            result[x-1][y-1] = DoThread((3.0 / horizPixels) * (x - (horizPixels / 2)),(3.0 / vertPixels) * (y - (vertPixels / 2)),maxiter);
        }
    }
}

int main(int argc, char* argv[]) {
    //first arg = length along horizontal axis
    int horizPixels = atoi(argv[1]);

    //second arg = length along vertical axis
    int vertPixels = atoi(argv[2]);

    //third arg = iterations
    int maxiter = atoi(argv[3]);

    //fourth arg = threads
    int threadCount = atoi(argv[4]);

    std::vector<std::vector<int> > result(horizPixels, std::vector<int>(vertPixels,0)); //create and init 2-dimensional vector
    SingleThreaded(horizPixels, vertPixels, maxiter, result);

    //TODO: remove these lines
    for(int y = 0; y < vertPixels; y++) {
      for(int x = 0; x < horizPixels; x++) {
            std::cout << std::setw(2) << std::setfill('0') << std::hex << result[x][y] << " ";
        }
        std::cout << std::endl;
    }
}

Lo he probado con gcc en Linux, pero estoy seguro de que también funciona en otros compiladores / sistemas operativos. Para que funcione, debe ingresar algunos argumentos de la línea de comandos de esta manera:

  

mandelbrot 106 500 255 1

el primer argumento es el ancho (eje x)
el segundo argumento es la altura (eje y)
el tercer argumento es el número de iteraciones máximas (el número de colores)
los últimos complementos son la cantidad de subprocesos (pero ese no se usa actualmente)

en mi resolución, el ejemplo anterior me da una buena representación de arte ASCII de un conjunto de Mandelbrot. Pero pruébelo usted mismo con diferentes argumentos (el primero será el más importante, ya que será el ancho)

A continuación puede encontrar el código que pirateé juntos para probar el rendimiento de subprocesos múltiples de pthreads. No lo he limpiado y no se han realizado optimizaciones; entonces el código es un poco crudo .

el código para guardar el conjunto mandelbrot calculado como un mapa de bits no es mío, puede encontrarlo aquí

#include <cstdlib> //for atoi
#include <iostream>
#include <iomanip> //for setw and setfill
#include <vector>

#include "bitmap_Image.h" //for saving the mandelbrot as a bmp

#include <pthread.h>

pthread_mutex_t mutexCounter;
int sharedCounter(0);
int percent(0);

int horizPixels(0);
int vertPixels(0);
int maxiter(0);

//doesn't need to be locked
std::vector<std::vector<int> > result; //create 2 dimensional vector

void *DoThread(void *null) {
    double curX,curY,xSquare,ySquare,x,y;
    int i, intx, inty, counter;
    counter = 0;

    do {
        counter++;
        pthread_mutex_lock (&mutexCounter); //lock
            intx = int((sharedCounter / vertPixels) + 0.5);
            inty = sharedCounter % vertPixels;
            sharedCounter++;
        pthread_mutex_unlock (&mutexCounter); //unlock

        //exit thread when finished
        if (intx >= horizPixels) {
            std::cout << "exited thread - I did " << counter << " calculations" << std::endl;
            pthread_exit((void*) 0);
        }

        //set x and y to the correct value now -> in the range like singlethread
        x = (3.0 / horizPixels) * (intx - (horizPixels / 1.5));
        y = (3.0 / vertPixels) * (inty - (vertPixels / 2));

        curX = x + x*x - y*y;
        curY = y + x*y + x*y;
        ySquare = curY*curY;
        xSquare = curX*curX;

        for (i=0; i<maxiter && ySquare + xSquare < 4;i++){
          ySquare = curY*curY;
          xSquare = curX*curX;
          curY = y + curX*curY + curX*curY;
          curX = x - ySquare + xSquare;
        }
        result[intx][inty] = i;
     } while (true);
}

int DoSingleThread(const double x, const double y) {
    double curX,curY,xSquare,ySquare;
    int i;

    curX = x + x*x - y*y;
    curY = y + x*y + x*y;
    ySquare = curY*curY;
    xSquare = curX*curX;

    for (i=0; i<maxiter && ySquare + xSquare < 4;i++){
      ySquare = curY*curY;
      xSquare = curX*curX;
      curY = y + curX*curY + curX*curY;
      curX = x - ySquare + xSquare;
    }
    return i;

}

void SingleThreaded(std::vector<std::vector<int> >&  result) {
    for(int x = horizPixels - 1; x != -1; x--) {
        for(int y = vertPixels - 1; y != -1; y--) {
            //3.0 -> so we always have -1.5 -> 1.5 as the window; (x - (horizPixels / 2) will go from -horizPixels/2 to +horizPixels/2
            result[x][y] = DoSingleThread((3.0 / horizPixels) * (x - (horizPixels / 1.5)),(3.0 / vertPixels) * (y - (vertPixels / 2)));
        }
    }
}

void MultiThreaded(int threadCount, std::vector<std::vector<int> >&  result) {
    /* Initialize and set thread detached attribute */
    pthread_t thread[threadCount];
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);


    for (int i = 0; i < threadCount - 1; i++) {
        pthread_create(&thread[i], &attr, DoThread, NULL);
    }
    std::cout << "all threads created" << std::endl;

    for(int i = 0; i < threadCount - 1; i++) {
        pthread_join(thread[i], NULL);
    }
    std::cout << "all threads joined" << std::endl;
}

int main(int argc, char* argv[]) {
    //first arg = length along horizontal axis
    horizPixels = atoi(argv[1]);

    //second arg = length along vertical axis
    vertPixels = atoi(argv[2]);

    //third arg = iterations
    maxiter = atoi(argv[3]);

    //fourth arg = threads
    int threadCount = atoi(argv[4]);

    result = std::vector<std::vector<int> >(horizPixels, std::vector<int>(vertPixels,21)); // init 2-dimensional vector
    if (threadCount <= 1) {
        SingleThreaded(result);
    } else {
        MultiThreaded(threadCount, result);
    }


    //TODO: remove these lines
    bitmapImage image(horizPixels, vertPixels);
    for(int y = 0; y < vertPixels; y++) {
      for(int x = 0; x < horizPixels; x++) {
            image.setPixelRGB(x,y,16777216*result[x][y]/maxiter % 256, 65536*result[x][y]/maxiter % 256, 256*result[x][y]/maxiter % 256);
            //std::cout << std::setw(2) << std::setfill('0') << std::hex << result[x][y] << " ";
        }
        std::cout << std::endl;
    }

    image.saveToBitmapFile("~/Desktop/test.bmp",32);
}
Se pueden obtener

buenos resultados utilizando el programa con los siguientes argumentos:

  

mandelbrot 5120 3840 256 3

de esa manera obtendrá una imagen de 5 * 1024 de ancho; 5 * 768 de alto con 256 colores (por desgracia, solo obtendrá 1 o 2) y 3 hilos (1 hilo principal que no hace ningún trabajo excepto crear los hilos de trabajo y 2 hilos de trabajo)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top