2013-06-26 2 views
3

Je demande seulement ceci pour essayer de comprendre ce que j'ai passé 24 heures à essayer de résoudre.Matlab limite TBB mais pas OpenMP

Mon système: Ubuntu 12.04.2, Matlab R2011a, tous les deux 64 bits, le processeur Intel Xeon basé sur Nehalem. Le problème est simple, Matlab permet aux programmes basés sur OpenMP d'utiliser tous les cœurs de processeurs avec l'hyper-threading activé mais ne permet pas la même chose pour TBB. Lors de l'exécution de TBB, je ne peux lancer que 4 threads, même si je change le nombre maxNumCompThreads à 8. Avec OpenMP, je peux utiliser tous les threads que je veux. Sans Hyper-Threading, TBB et OpenMP utilisent tous les 4 cœurs bien sûr.

Je comprends Hyper-threading et que son virtuel, mais la limitation matlab fait, effectivement fait une pénalité sur la performance (un reference supplémentaire).

J'ai testé cette question à l'aide de 2 programmes, un simple boucle avec

#pragma omp parallel for 

et une autre boucle très simple basé sur un exemple de code TBB.

tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred); 
tbb::parallel_for_each(tasks.begin(),tasks.end(),invoker<mytask>()); 

et enveloppé les deux avec une fonction mex matlab.

Quelqu'un at-il une explication à cela? Y a-t-il une différence inhérente dans la méthode ou la structure de création de thread qui permet à matlab d'étrangler TBB mais n'autorise pas ce throttoling pour OpenMP?

Code pour référence:

OpenMP:

#include "mex.h" 

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ 
    threadCount = 100000; 
#pragma omp parallel for 
    for(int globalId = 0; globalId < threadCount ; globalId++) 
    { 
     for(long i=0;i<1000000000L;++i) {} // Deliberately run slow 
    } 
} 

TBB:

#include "tbb/parallel_for_each.h" 
#include "tbb/task_scheduler_init.h" 
#include <iostream> 
#include <vector> 
#include "mex.h" 

struct mytask { 
    mytask(size_t n) 
    :_n(n) 
    {} 
    void operator()() { 
    for (long i=0;i<1000000000L;++i) {} // Deliberately run slow 
    std::cerr << "[" << _n << "]"; 
    } 
    size_t _n; 
}; 

template <typename T> struct invoker { 
    void operator()(T& it) const {it();} 
}; 

void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const 
mxArray* prhs[]) { 

    tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred); // Automatic number of threads 

    std::vector<mytask> tasks; 
    for (int i=0;i<10000;++i) 
    tasks.push_back(mytask(i)); 

    tbb::parallel_for_each(tasks.begin(),tasks.end(),invoker<mytask>()); 

} 
+0

Essayez d'initialiser 'tbb :: task_scheduler_init' avec' 8' au lieu d'utiliser 'différé'. –

+0

essayez de contrôler explicitement le nombre de threads: 'maxNumCompThreads' dans MATLAB, la fonction' omp_set_num_threads' ou la variable d'environnement 'OMP_NUM_THREADS' pour OpenMP, et [équivalent] (http://stackoverflow.com/q/3786408/97160) pour TBB – Amro

+0

J'ai essayé vos deux suggestions, je n'ai pas travaillé. – omarzouk

Répondre

2

Désolé il a fallu si longtemps pour répondre. La spécification de deferred empêche simplement le planificateur de tâches de créer le pool de threads jusqu'à ce que la première construction parallèle démarre. Par défaut, le nombre de threads est automatic, ce qui correspond au nombre de noyaux

I modifié légèrement votre code (le réglage du code ceci est en src/tbb/tbb_misc_ex.cpp, et dépend aussi de l'affinité du processeur entre autres Voir initialize_hardware_concurrency_info().):

#include "tbb/parallel_for_each.h" 
#include "tbb/task_scheduler_init.h" 
#include "tbb/atomic.h" 
#include "tbb/spin_mutex.h" 
#include <iostream> 
#include <vector> 

// If LOW_THREAD == 0, run with task_scheduler_init(automatic), which is the number 
// of cores available. If 1, start with 1 thread. 

#ifndef NTASKS 
#define NTASKS 50 
#endif 
#ifndef MAXWORK 
#define MAXWORK 400000000L 
#endif 
#ifndef LOW_THREAD 
#define LOW_THREAD 0 // 0 == automatic 
#endif 

tbb::atomic<size_t> cur_par; 
tbb::atomic<size_t> max_par; 

#if PRINT_OUTPUT 
tbb::spin_mutex print_mutex; 
#endif 

struct mytask { 
    mytask(size_t n) :_n(n) {} 
    void operator()() { 
     size_t my_par = ++cur_par; 
     size_t my_old = max_par; 
     while(my_old < cur_par) { my_old = max_par.compare_and_swap(my_par, my_old); } 

     for (long i=0;i<MAXWORK;++i) {} // Deliberately run slow 
#if PRINT_OUTPUT 
     { 
      tbb::spin_mutex::scoped_lock s(print_mutex); 
      std::cerr << "[" << _n << "]"; 
     } 
#endif 
     --cur_par; 
    } 
    size_t _n; 
}; 

template <typename T> struct invoker { 
    void operator()(T& it) const {it();} 
}; 

void mexFunction(/*int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]*/) { 

    for(size_t thr = LOW_THREAD; thr <= 128; thr = thr ? thr * 2: 1) { 
     cur_par = max_par = 0; 
     tbb::task_scheduler_init init(thr == 0 ? (unsigned int)tbb::task_scheduler_init::automatic : thr); 

     std::vector<mytask> tasks; 
     for (int i=0;i<NTASKS;++i) tasks.push_back(mytask(i)); 

     tbb::parallel_for_each(tasks.begin(),tasks.end(),invoker<mytask>()); 
     std::cout << " for thr == "; 
     if(thr) std::cout << thr; else std::cout << "automatic"; 
     std::cout << ", maximum parallelism == " << (size_t)max_par << std::endl; 
    } 
} 

int main() { 
    mexFunction(); 
} 

j'ai couru ceci sur un système 16 ici-core:

for thr == automatic, maximum parallelism == 16 
for thr == 1, maximum parallelism == 1 
for thr == 2, maximum parallelism == 2 
for thr == 4, maximum parallelism == 4 
for thr == 8, maximum parallelism == 8 
for thr == 16, maximum parallelism == 16 
for thr == 32, maximum parallelism == 32 
for thr == 64, maximum parallelism == 50 
for thr == 128, maximum parallelism == 50 

Le o limite f 50 est le nombre total de tâches créées par le programme.

Les threads créés par TBB sont partagés par les constructions parallèles démarrées par le programme, donc si vous avez deux for_each parallèles exécutées simultanément, le nombre maximum de threads ne changera pas; Chaque for_each fonctionnera plus lentement. La bibliothèque TBB ne contrôle pas le nombre de threads utilisés dans les constructions OpenMP, de sorte qu'un OpenMP parallel_for et un TBB parallel_for_each vont généralement surcharger la machine.