2013-06-26 2 views
3

Я прошу это попытаться понять, что я потратил 24 часа, пытаясь исправить.Matlab ограничивает TBB, но не OpenMP

Моя система: Ubuntu 12.04.2, Matlab R2011a, оба из них 64-разрядный процессор Intel Xeon на основе Nehalem.

Проблема заключается в том, что Matlab позволяет программам на основе OpenMP использовать все ядра ЦП с поддержкой гиперпотока, но не допускает то же самое для TBB.

При запуске TBB я могу запускать только 4 потока, даже когда я изменяю maxNumCompThreads на 8. В OpenMP я могу использовать все потоки, которые я хочу. Без Hyper-threading, как TBB, так и OpenMP используют все 4 ядра, конечно.

Я понимаю, что Hyper-threading и что его виртуальный, но ограничение matlab делает, на самом деле вызывает штраф за производительность (дополнительный reference).

Я проверил эту проблему с помощью 2 программы, простой цикл с

#pragma omp parallel for 

и другой очень простой цикл на основе кода TBB образца.

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

и завернутый в оба из них с помощью matlab mexFunction.

У кого-нибудь есть объяснение? Есть ли неотъемлемая разница в методе или структуре создания потоков, которая позволяет Matlab дросселировать TBB, но не позволяет использовать эту функцию для OpenMP?

Код для ссылки:

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

Попробуйте инициализировать 'tbb :: task_scheduler_init' с помощью' 8' вместо использования 'offferred'. –

+0

попытайтесь явно контролировать количество потоков: 'maxNumCompThreads' в MATLAB,' omp_set_num_threads' или переменную среды 'OMP_NUM_THREADS' для OpenMP и [эквивалент] (http://stackoverflow.com/q/3786408/97160) для TBB – Amro

+0

Я пробовал оба ваши предложения, не работал. – omarzouk

ответ

2

К сожалению это заняло так много времени, чтобы ответить. Задание deferred просто позволяет планировщику задач создавать пул потоков до начала первой параллельной конструкции. По умолчанию количество потоков automatic, что соответствует количеству ядер

Я слегка измененный код (код установки это в src/tbb/tbb_misc_ex.cpp, а также зависит от близости процессора среди прочего См 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(); 
} 

Я побежал это на 16-ядерной системы здесь:

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 

предел O f 50 - общее количество задач, созданных программой.

Нити, созданные TBB, совместно используются параллельными конструкциями, запущенными программой, поэтому, если у вас есть одновременно два параллельных for_each, максимальное число потоков не изменится; каждый for_each будет работать более медленно. Библиотека TBB не контролирует количество потоков, используемых в конструкциях OpenMP, поэтому OpenMP parallel_for и TBB parallel_for_each обычно переписывают машину.