Question

Alors, je me rends compte cette question semble stupide (et oui, je me sers un dual core), mais j'ai essayé deux bibliothèques différentes (Grand Central Dispatch et OpenMP), et lorsque l'horloge en utilisant () en temps le code avec et sans lignes qui le rendent parallèle, la vitesse est la même. (Pour mémoire, ils étaient tous les deux en utilisant leur propre forme de parallèle). Ils font état en cours d'exécution sur différents threads, mais peut-être qu'ils sont en cours d'exécution sur le même noyau? Y at-il moyen de vérifier? (Les deux bibliothèques sont C, je suis mal à l'aise au niveau des couches inférieures.) C'est super bizarre. Toutes les idées?

Était-ce utile?

La solution

EDIT:. Ajouté pour Grand Central Dispatch en réponse aux commentaires OP

Alors que les autres réponses ici sont utiles en général, la réponse précise à votre question est que vous ne devriez pas utiliser clock() pour comparer le moment. clock() mesure le temps CPU qui est ajouté à travers les fils jusqu'à. Lorsque vous divisez un travail entre les cœurs, il utilise au moins autant de temps CPU (en général un peu plus en raison de filetage en tête). Rechercher horloge () sur cette page , trouver « si le processus est multi-thread, le temps cpu consommée par tous les threads individuels du processus sont ajoutés « .

Il est juste que le travail est divisé entre les threads, de sorte que le temps global que vous devez attendre moins. Vous devez utiliser le temps de mur (le temps sur une horloge murale). OpenMP fournit une omp_get_wtime() routine pour le faire. Prenez la routine suivante comme exemple:

#include <omp.h>
#include <time.h>
#include <math.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    int i, nthreads;
    clock_t clock_timer;
    double wall_timer;
    for (nthreads = 1; nthreads <=8; nthreads++) {
        clock_timer = clock();
        wall_timer = omp_get_wtime();
        #pragma omp parallel for private(i) num_threads(nthreads)
        for (i = 0; i < 100000000; i++) cos(i);
        printf("%d threads: time on clock() = %.3f, on wall = %.3f\n", \
            nthreads, \
            (double) (clock() - clock_timer) / CLOCKS_PER_SEC, \
            omp_get_wtime() - wall_timer);
    }
}

Les résultats sont les suivants:

1 threads: time on clock() = 0.258, on wall = 0.258
2 threads: time on clock() = 0.256, on wall = 0.129
3 threads: time on clock() = 0.255, on wall = 0.086
4 threads: time on clock() = 0.257, on wall = 0.065
5 threads: time on clock() = 0.255, on wall = 0.051
6 threads: time on clock() = 0.257, on wall = 0.044
7 threads: time on clock() = 0.255, on wall = 0.037
8 threads: time on clock() = 0.256, on wall = 0.033

Vous pouvez voir que le temps de clock() ne change pas beaucoup. Je reçois 0.254 sans pragma, il est donc un peu plus lent en utilisant openMP avec un fil de ne pas utiliser openMP du tout, mais le temps de la paroi diminue avec chaque fil.

L'amélioration ne sera pas toujours ce bon en raison, par exemple, les parties de votre calcul qui ne sont pas parallèles (voir Amdahl's_law ) ou la lutte contre différents fils sur la même mémoire.

EDIT: Pour Grand Central Dispatch, le référence GCD états, que GCD utilise gettimeofday pour le temps de mur. Alors, je crée une nouvelle application Cocoa, et je mets applicationDidFinishLaunching:

struct timeval t1,t2;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int iterations = 1; iterations <= 8; iterations++) {
    int stride = 1e8/iterations;
    gettimeofday(&t1,0);
    dispatch_apply(iterations, queue, ^(size_t i) { 
        for (int j = 0; j < stride; j++) cos(j); 
    });
    gettimeofday(&t2,0);
    NSLog(@"%d iterations: on wall = %.3f\n",iterations, \
                t2.tv_sec+t2.tv_usec/1e6-(t1.tv_sec+t1.tv_usec/1e6));
}

et je reçois les résultats suivants sur la console:

2010-03-10 17:33:43.022 GCDClock[39741:a0f] 1 iterations: on wall = 0.254
2010-03-10 17:33:43.151 GCDClock[39741:a0f] 2 iterations: on wall = 0.127
2010-03-10 17:33:43.236 GCDClock[39741:a0f] 3 iterations: on wall = 0.085
2010-03-10 17:33:43.301 GCDClock[39741:a0f] 4 iterations: on wall = 0.064
2010-03-10 17:33:43.352 GCDClock[39741:a0f] 5 iterations: on wall = 0.051
2010-03-10 17:33:43.395 GCDClock[39741:a0f] 6 iterations: on wall = 0.043
2010-03-10 17:33:43.433 GCDClock[39741:a0f] 7 iterations: on wall = 0.038
2010-03-10 17:33:43.468 GCDClock[39741:a0f] 8 iterations: on wall = 0.034

qui est environ le même que je devenais ci-dessus.

Ceci est un exemple très artificiel. En fait, vous devez être sûr de garder l'optimisation à -O0, ou bien le compilateur vous rendrez compte que nous ne conservons aucune des calculs et ne pas faire la boucle du tout. En outre, l'entier que je prends la cos de est différente dans les deux exemples, mais cela ne modifie pas les résultats trop. Voir la STRIDE sur la page de manuel dispatch_apply pour savoir comment le faire correctement et pourquoi iterations est largement comparable à num_threads dans ce cas.

EDIT: Je note que la réponse de Jacob comprend

  

J'utilise le omp_get_thread_num ()   fonction dans ma boucle parallélisée   pour imprimer ce noyau, il travaille   sur ... De cette façon, vous pouvez être sûr que   il fonctionne sur les deux noyaux.

qui est incorrect (il a été partiellement déterminée par une modification). L'utilisation omp_get_thread_num() est en effet un bon moyen de faire en sorte que votre code est multithread, mais il ne montre pas « qui base, il travaille sur », juste quel fil. Par exemple, le code suivant:

#include <omp.h>
#include <stdio.h>

int main() {
    int i;
    #pragma omp parallel for private(i) num_threads(50)
    for (i = 0; i < 50; i++) printf("%d\n", omp_get_thread_num());
}

imprime qu'il est l'aide de fils 0 à 49, mais cela ne montre pas quelle base il travaille sur, puisque je n'ai huit cœurs. En regardant le Moniteur d'activité (l'OP mentionné GCD, doit donc être sur un Mac - Window/CPU Usage aller!)., Vous pouvez voir changer d'emploi entre les cœurs, donc core = fil

Autres conseils

Très probablement votre temps d'exécution est pas lié par ces boucles vous parallélisées.

Ma suggestion est que vous profilez votre code pour voir ce qui se passe la plupart du temps. La plupart des ingénieurs vous diront que vous devriez le faire avant faire quoi que ce soit drastique pour optimiser les choses.

Il est difficile de deviner sans aucun détail. Peut-être que votre application n'est pas même lié CPU. Avez-vous regardé la charge du processeur pendant que votre code a été en cours d'exécution? At-il atteint 100% sur au moins un noyau?

Votre question manque des détails très importants tels que la nature de votre demande est, quelle partie de celui-ci vous essayez d'améliorer, les résultats de profilage (le cas échéant), etc ...

Après avoir dit que vous devriez vous rappeler plusieurs points critiques à l'approche d'un effort d'amélioration de la performance:

  • Les efforts doivent toujours se concentrer sur les zones de code qui ont fait leurs preuves, par profilage , être l'inefficacité
  • parallélisation du code lié à la CPU presque jamais améliorer les performances (sur une seule machine de base). Vous allez perdre un temps précieux sur les changements de contexte inutiles et gagner rien . Vous pouvez très facilement s'aggraver performances en faisant cela.
  • Même si vous parallélisation du code lié CPU sur une machine multi-cœurs, vous devez vous rappeler que vous n'avez aucune garantie d'exécution parallèle.

Assurez-vous que vous n'allez contre ces points, parce qu'une supposition (sauf des détails supplémentaires) dira c'est exactement ce que vous faites.

Si vous utilisez beaucoup de mémoire dans la boucle, qui pourrait l'empêcher d'être plus rapide. vous pouvez également regarder dans la bibliothèque pthread, pour gérer manuellement le filetage.

J'utilise la fonction omp_get_thread_num() dans mon boucle parallélisée pour imprimer ce noyau, il travaille sur si vous ne spécifiez pas num_threads . Pour par exemple.

printf("Computing bla %d on core %d/%d ...\n",i+1,omp_get_thread_num()+1,omp_get_max_threads());

Le ci-dessus fonctionne pour ce pragma     #pragma omp parallèle pour défaut (aucun) commun (a, b, c)

De cette façon, vous pouvez être sûr qu'il fonctionne sur les deux cœurs puisque seuls deux fils seront créés.

BTW, est OpenMP activé lorsque vous compilez? Dans Visual Studio, vous devez activer dans le Pages de propriétés , C++ -> Language et mis OpenMP Support à Yes

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