Question

Pour l’un des projets que je mène actuellement, je dois examiner les performances (entre autres choses) de différents langages de programmation activés simultanément .

Pour le moment, je cherche à comparer les python sans pile et C ++ PThreads , l'accent est donc mis sur ces deux langues, mais d'autres langues seront probablement testées ultérieurement. Bien entendu, la comparaison doit être aussi représentative et précise que possible. Aussi, ma première idée a-t-elle été de commencer à chercher des problèmes de référence simultanés / multithreads standard , hélas je ne pouvais pas trouvez des tests / problèmes / points de repère corrects ou standard.

Ma question est donc la suivante: Avez-vous une suggestion pour un bon problème, simple ou rapide, permettant de tester les performances du langage de programmation (et d'exposer ses points forts et ses points faibles dans le processus )?

Était-ce utile?

La solution

Depuis que le jeu de références a été transféré sur une machine quadricœur en septembre 2008, de nombreux programmes dans différents langages de programmation ont été réécrits pour exploiter quad-core - par exemple, les 10 premiers programmes mandelbrot .

Autres conseils

Vous devriez sûrement tester du matériel et des compilateurs plutôt qu'un langage pour les performances d'accès simultané?

J'examinerais un langage du point de vue de sa facilité et de sa productivité en termes de simultanéité et de la mesure dans laquelle il "empêche" le programmeur de faire des erreurs de verrouillage.

EDIT: à partir de l’expérience passée en tant que chercheur dans la conception d’algorithmes parallèles, vous constaterez que, dans la plupart des cas, les performances simultanées dépendent en grande partie de la façon dont un algorithme est parallélisé et de la manière dont il cible le matériel sous-jacent.

De plus, les points de repère sont notoirement inégaux; c'est encore plus vrai dans un environnement parallèle. Par exemple, un repère qui "crunch" de très grandes matrices conviendrait à un processeur vectoriel, alors qu'un tri en parallèle conviendrait mieux à des processeurs multi-core plus généraux.

Cela pourrait être utile:

Tests de performance parallèles

Tests de performances parallèles du NAS

Eh bien, il existe quelques classiques, mais différents tests mettent l’accent sur différentes caractéristiques. Certains systèmes distribués peuvent être plus robustes, avoir une transmission de messages plus efficace, etc. Une surcharge de messages peut nuire à l'évolutivité, car la méthode normale pour passer à plus de machines consiste à envoyer un plus grand nombre de petits messages. Certains problèmes classiques que vous pouvez essayer sont un tamis d’eratosthène distribué ou un calculateur de séquence de fibonacci mal implémenté (c’est-à-dire calculer le huitième numéro de la série, rotation d’une machine pour le septième, et une autre pour le sixième). Pratiquement tous les algorithmes de division et de conquête peuvent être réalisés simultanément. Vous pouvez également réaliser une implémentation simultanée du jeu de vie ou du transfert de chaleur de Conway. Notez que tous ces algorithmes ont des objectifs différents et que, par conséquent, vous ne réussirez probablement pas à obtenir le meilleur système distribué.

Je dirais que le plus simple à implémenter rapidement est le calculateur Fibonacci mal implémenté, bien qu'il mette trop l'accent sur la création de threads et trop peu sur la communication entre ces threads.

  

Vous devriez sûrement tester le matériel   et compilateurs plutôt qu'une langue   pour les performances d'accès simultané?

Non, le matériel et les compilateurs ne sont pas pertinents pour mes tests. Je cherche simplement quelques bons problèmes qui permettent de vérifier dans quelle mesure un code écrit dans une langue peut concurrencer un code d’une autre langue. Je teste vraiment les constructions disponibles dans les langages spécifiques pour la programmation simultanée. Et l’un des critères est la performance (mesurée dans le temps).

Certains des critères de test que je recherche sont les suivants:

  • comment il est facile d’écrire du code correct; car, comme nous le savons tous, la programmation simultanée est plus difficile que l’écriture de programmes à un seul thread
  • quelle est la technique utilisée pour la programmation concurrente: événementielle, basée sur les acteurs, analyse de messages, ...
  • combien de code doit être écrit par le programmeur lui-même et combien est fait automatiquement pour lui: cela peut également être testé avec les problèmes de benchmark donnés
  • quel est le niveau d'abstraction et combien de temps système est impliqué lors de la conversion en code machine

Donc, en fait, je ne cherche pas les performances comme le seul et meilleur paramètre (ce qui me permettrait effectivement de recevoir le matériel et les compilateurs au lieu du langage lui-même), je cherche en fait du point de vue des programmeurs, vérifier quel langage convient le mieux à quel type de problèmes, en quoi consistent ses faiblesses et ses points forts, etc.

N'oubliez pas qu'il s'agit d'un petit projet et que les tests doivent donc également être limités. (il est donc impossible de tout tester de manière rigoureuse)

J'ai décidé d'utiliser le ensemble Mandelbrot (le algorithme de temps d'échappement pour être plus précis) afin de comparer les différentes langues.
Cela me convient tout à fait, car l’algorithme original peut facilement être implémenté et créer la variante multithread à partir de celui-ci n’est pas si difficile.

ci-dessous est le code que j'ai actuellement. C'est toujours une variante à un seul thread, mais je la mettrai à jour dès que je serai satisfait du résultat.

#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;
    }
}

Je l’ai testé avec gcc sous Linux, mais je suis sûr que cela fonctionne également sous d’autres compilateurs / systèmes d’exploitation. Pour que cela fonctionne, vous devez entrer des arguments en ligne de commande comme suit:

  

mandelbrot 106 500 255 1

le premier argument est la largeur (axe des x)

le deuxième argument est la hauteur (axe des y)
le troisième argument est le nombre d'itérations maximum (le nombre de couleurs)
le dernier ons est le nombre de threads (mais celui-ci n'est pas utilisé)

Sur ma résolution, l'exemple ci-dessus me donne une belle représentation ASCII d'un ensemble de Mandelbrot. Mais essayez vous-même avec différents arguments (le premier sera le plus important, car ce sera la largeur)

Vous trouverez ci-dessous le code que j'ai piraté afin de tester les performances multithread des pthreads. Je ne l'ai pas nettoyé et aucune optimisation n'a été faite; le code est donc un peu brut .

le code pour enregistrer le jeu de mandelbrot calculé en tant que bitmap n'est pas le mien, vous pouvez le trouver ici

#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);
}

de bons résultats peuvent être obtenus en utilisant le programme avec les arguments suivants:

  

mandelbrot 5120 3840 256 3

Ainsi, vous obtiendrez une image large de 5 * 1024; 5 * 768 avec 256 couleurs (hélas, vous n’en obtiendrez qu’un ou deux) et 3 threads (1 thread principal qui ne fait aucun travail, sauf la création des threads de travail, et 2 threads de travail)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top