2012-02-15 1 views
5

Mon processeur est un Core i3 330M avec 2 coeurs et 4 threads. Quand j'exécute la commande cat /proc/cpuinfo dans mon terminal, c'est comme si j'avais 4 CPUS. Lorsque j'utilise la fonction OpenMP get_omp_num_procs(), j'obtiens également 4.OpenMP et cores/threads

Maintenant, j'ai une classe de vecteur C++ standard, je veux dire une classe de double tableau de taille fixe qui n'utilise pas de modèles d'expression. J'ai soigneusement parallélisé toutes les méthodes de ma classe et j'obtiens l'accélération "attendue". La question est: puis-je deviner l'accélération attendue dans un cas si simple? Par exemple, si j'ajoute deux vecteurs sans for-loops parallélisées, je reçois un peu de temps (en utilisant la commande shell time). Maintenant, si j'utilise OpenMP, devrais-je obtenir un temps divisé par 2 ou 4, selon le nombre de cœurs/threads? J'insiste sur le fait que je ne demande que ce simple problème particulier, où il n'y a pas d'interdépendance dans les données et où tout est linéaire (ajout de vecteurs).

Voici un code:

Vector Vector::operator+(const Vector& rhs) const 
{ 
    assert(m_size == rhs.m_size); 
    Vector result(m_size); 
    #pragma omp parallel for schedule(static) 
    for (unsigned int i = 0; i < m_size; i++) 
      result.m_data[i] = m_data[i]+rhs.m_data[i]; 

    return result; 
} 

J'ai déjà lu ce post: OpenMP thread mapping to physical cores.

J'espère que quelqu'un m'en dira plus sur la façon dont OpenMP fait le travail dans ce cas simple. Je devrais dire que je suis un débutant en informatique parallèle.

Merci!

Répondre

3

EDIT: Maintenant que du code a été ajouté.

Dans cet exemple particulier, il y a très peu de calculs et beaucoup d'accès à la mémoire. Donc, la performance dépendra fortement de:

  • La taille du vecteur.
  • Comment vous le chronométriez. (avez-vous une boucle externe à des fins de synchronisation?)
  • Si les données sont déjà dans le cache.

Pour les plus grandes tailles de vecteur, vous trouverez probablement que la performance est limitée par votre bande passante mémoire. Dans ce cas, le parallélisme ne va pas beaucoup aider. Pour les plus petites tailles, les frais généraux de filetage domineront. Si vous obtenez l'accélération "attendue", vous êtes probablement quelque part entre les deux où le résultat est optimal. Je refuse de donner des nombres durs parce qu'en général, la «devinette» des performances, en particulier dans les applications multithread, est une cause perdue à moins que vous ayez déjà testé des connaissances ou une connaissance intime du programme et du système sur lequel il fonctionne.

Tout comme un exemple simple tiré de ma réponse ici: How to get 100% CPU usage from a C program

Sur un Core i7 920 @ 3.5 GHz (4 noyaux, 8 sujets):

Si je cours avec 4 fils, le résultat est:

This machine calculated all 78498 prime numbers under 1000000 in 39.3498 seconds 

Si je cours avec 4 fils et explicitement (en utilisant le gestionnaire de tâches) la broche sur les fils 4 noyaux physiques distincts, le résultat est:

This machine calculated all 78498 prime numbers under 1000000 in 30.4429 seconds 

Cela montre à quel point il est imprévisible, même pour une application parallèle très simple et embarrassante. Les applications impliquant une utilisation intensive de la mémoire et la synchronisation deviennent beaucoup plus laides ...

1

Pour ajouter à la réponse Mystical. Votre problème est purement bande passante de mémoire limitée. Jetez un oeil à la STREAM benchmark. Exécutez-le sur votre ordinateur dans des cas uniques et multithread, et regardez les résultats Triad - c'est votre cas (enfin, presque, puisque votre vecteur de sortie est en même temps l'un de vos vecteurs d'entrée). Calculer combien de données vous déplacez et vous saurez exactement ce que la performance à attendre.

Le multi-thread fonctionne-t-il pour ce problème? Oui. Il est rare qu'un seul cœur de CPU puisse saturer toute la bande passante mémoire du système. Les ordinateurs modernes équilibrent la bande passante mémoire disponible avec le nombre de cœurs disponibles. D'après mon expérience, vous aurez besoin d'environ la moitié des cœurs pour saturer la bande passante de la mémoire avec une simple opération memcopy. Cela pourrait prendre un peu plus si vous faites des calculs sur le chemin.

Notez que sur les systèmes NUMA, vous devrez lier les unités d'exécution aux cœurs de processeur et utiliser l'allocation de mémoire locale pour obtenir des résultats optimaux. En effet, sur de tels systèmes, chaque CPU possède sa propre mémoire locale, à laquelle l'accès est le plus rapide. Vous pouvez toujours accéder à toute la mémoire du système comme sur les SMP habituels, mais cela implique des coûts de communication - les CPU doivent échanger des données de manière explicite. La liaison des threads aux processeurs et l'utilisation de l'allocation locale sont extrêmement importantes. Ne pas le faire tue l'évolutivité. Vérifiez libnuma si vous voulez faire cela sur Linux.