2013-03-14 5 views
1

Я изучаю задачу выполнения в TBB и запускаю код для параллельного и последовательного вычисления серии Фибоначчи.Параллельное выполнение занимает больше времени, чем последовательный?

Кодекс:

#include <iostream> 
#include <list> 
#include <tbb/task.h> 
#include <tbb/task_group.h> 
#include <stdlib.h> 
#include "tbb/compat/thread" 
#include "tbb/task_scheduler_init.h" 
using namespace std; 
using namespace tbb; 

#define CutOff 2 

long serialFib(long n) { 
if(n<2) 
return n; 
else 
return serialFib(n-1) + serialFib(n-2); 
} 


class FibTask: public task 
{ 
    public: 
    const long n; 
    long* const sum; 

    FibTask(long n_, long* sum_) : n(n_), sum(sum_) {} 

    task* execute() 
    { 
     // cout<<"task id of thread is \t"<<this_thread::get_id()<<"FibTask(n)="<<n<<endl; // Overrides virtual function task::execute  
       // cout<<"Task Stolen is"<<is_stolen_task()<<endl; 
     if(n<CutOff) 
     { 
      *sum = serialFib(n); 
     } 
     else 
     { 
      long x, y; 
      FibTask& a = *new(allocate_child()) FibTask(n-1,&x); 
      FibTask& b = *new(allocate_child()) FibTask(n-2,&y); 
      set_ref_count(3); // 3 = 2 children + 1 for wait // ref_countis used to keep track of the number of tasks spawned at       the current level of the task graph 
      spawn(b); 
         // cout<<"child id of thread is \t"<<this_thread::get_id()<<"calculating n ="<<n<<endl; 
      spawn_and_wait_for_all(a); //set tasks for execution and wait for them 
      *sum = x+y; 
     } 
     return NULL; 
    } 
}; 


long parallelFib(long n) 
{ 
    long sum; 
    FibTask& a = *new(task::allocate_root()) FibTask(n,&sum); 
    task::spawn_root_and_wait(a); 
    return sum; 
} 


int main() 
{  
    long i,j; 
    cout<<fixed; 

    cout<<"Fibonacci Series parallelly formed is "<<endl; 
     tick_count t0=tick_count::now(); 
    for(i=0;i<50;i++) 
    cout<<parallelFib(i)<<"\t"; 
    // cout<<"parallel execution of Fibonacci series for n=10 \t"<<parallelFib(i)<<endl; 

    tick_count t1=tick_count::now(); 
    double t=(t1-t0).seconds(); 
    cout<<"Time Elapsed in Parallel Execution is \t"<<t<<endl; 
    cout<<"\n Fibonacci Series Serially formed is "<<endl; 
    tick_count t3=tick_count::now(); 

    for(j=0;j<50;j++) 
    cout<<serialFib(j)<<"\t"; 
    tick_count t4=tick_count::now(); 
    double t5=(t4-t3).seconds(); 
    cout<<"Time Elapsed in Serial Execution is \t"<<t5<<endl; 
    return(0); 
} 

Параллельное выполнение занимает больше времени по сравнению с серийным execution.In этого параллельного выполнения потребовалось 2500 сек, тогда как серийный взял около 167 секунд. Может ли кто-нибудь объяснить причину этого?

ответ

6

Накладные расходы.

Когда ваша фактическая задача является легкой, координация/связь доминирует, и вы не получаете (автоматически) выигрыш от параллельного выполнения. Это довольно распространенная проблема.

Попробуйте вместо этого вычислить числа M Фибоначчи (с достаточно высокой стоимостью), затем вычислить их параллельно. Вы должны увидеть выигрыш.

0

Без дополнительной информации будет сложно рассказать. вам нужно проверить: сколько процессов у вашего компьютера? были ли какие-либо другие программы, которые могли бы использовать процессоры? , если вы хотите запустить (правда) параллельно и получить преимущества производительности, чем операционная система должна иметь возможность выделять не менее 2 бесплатных процессоров. Кроме того, для небольших задач накладные расходы на распределение потоков и сбор их результата могут превышать преимущества параллельного выполнения.

+0

его четырехъядерный процессор i3 ... да другие приложения также работают –

0

Я правильно понимаю, что каждая задача делает result of fib(n-1) + result of fib(n-2) - так что вы начинаете задачу, которая затем запускает другую задачу и так далее, пока у нас не будет очень большого количества задач (я немного потерялся, пытаясь подсчитать их все - Я думаю, что это квадрат в квадрате). И результат каждой такой задачи используется для добавления числа фибоначчи.

Прежде всего, здесь нет никакого параллельного выполнения (кроме, возможно, двух независимых рекурсивных вычислений). Каждая задача зависит от результата ее подзадачи и не может ничего сделать параллельно. С другой стороны, вы выполняете большую работу по настройке каждой задачи. Совсем не удивительно, что вы не видите никакой пользы)

Теперь, если бы вы вычислили число фибоначчи 1 .. 50 по итерации, и вы начали, скажем, одну задачу на процессорное ядро ​​в вашей системе и сравнил это с итеративным решением, используя только один цикл, я уверен, что это продемонстрировало бы гораздо лучшее улучшение.

2

Изменить Cutoff на 12, скомпилировать с оптимизацией (-O на Linux;/O2 в Windows), и вы должны увидеть значительное ускорение.

В примере есть много параллелизма. Проблема в том, что при Cutoff = 2 отдельные единицы полезного параллельного вычисления затухают из-за накладных расходов на планирование. Повышение значения Cutoff должно решить проблему.

Вот анализ. Для анализа параллелизма существует два важных момента:

  • Работа - общий объем вычислительной работы.
  • span - длина критического пути.

Доступный параллелизм - это работа/пролет.

Для fib (n), когда n достаточно велико, произведение примерно пропорционально fib (n) [да, оно описывает себя!]. Пролет - это глубина дерева вызовов - он примерно пропорционален n. Таким образом, параллелизм пропорционален fib (n)/n. Таким образом, даже при n = 10 существует много доступного параллелизма, чтобы сохранить типичную машину для настольных компьютеров 2013 года.

Проблема в том, что задачи TBB требуют времени для создания, выполнения, синхронизации и уничтожения. Изменение Cutoff от 2 до 12 позволяет серийному коду принимать во внимание, когда работа настолько мала, что планирование накладных расходов будет замачивать ее. Это обычная закономерность в рекурсивном параллелизме: повторяйте параллель, пока не упадете до кусков работы, которые также могут выполняться последовательно. В других параллельных структурах (например, OpenMP или Cilk Plus) возникает одна и та же проблема: для задач есть накладные расходы, хотя они могут быть более или менее TBB. Все, что меняется, является наилучшим пороговым значением.

Попробуйте изменить Cutoff. Более низкие значения должны дать вам больше параллелизма, но больше затрат на планирование. Более высокие значения дают вам меньше параллелизма, но меньше затрат на планирование. Между тем, вы, вероятно, найдете ряд значений, которые дают хорошее ускорение.

Смежные вопросы