在TBB中通过递归,基于任务的并行编程获得不同的输出?

时间:2014-05-08 13:57:39

标签: c++ parallel-processing tbb

我担心这是一个很长的代码。我正在使用英特尔TBB和C ++编写一个基于任务的并行,递归,基于任务的Euler分区公式版本,我不会认为这个程序的逻辑存在很多问题,但我有一种感觉变量被错误地访问,我可能已经在错误的地方或其他地方声明了它们。我这样说是因为输入数字n应该总是给出相同的结果,并且它在n = 11以下,但是在它之上它给出了不同的答案。更奇怪的是,添加输出行来尝试对程序进行故障排除会导致稍微更准确的答案(好像以某种方式填充计算的每个部分所花费的时间有助于它)。我不知道如何避免这个问题或者哪个变量确切地导致它,因为答案通常相当接近,它不仅仅是一个随机数。所以这有点棘手,我道歉,但如果有人能帮助我,我会非常感激,我已经花了好几个小时来解决这个问题。

这是并行任务:

class ParallelFormula : public task {
public:
int n;
int* pTot;

//Task constructor
ParallelFormula(int n_, int* pTot_) : n(n_), pTot(pTot_) {}

//Task definition
task* execute() {
    //Iterating for formula to work
    for (int k = 1; k > 0; k++) {
        //Add fixed values to pTot for any case where 2 >= n >= 0
        switch (n) {
        case 0:
            if (k % 2 != 0)
                *pTot += 1;
            else
                *pTot -= 1;
            return NULL;
        case 1:
            if (k % 2 != 0)
                *pTot += 1;
            else
                *pTot -= 1;
            return NULL;
        case 2:
            if (k % 2 != 0)
                *pTot += 2;
            else
                *pTot -= 2;
            return NULL;
        }
                    //Calculate p numbers using section of Euler's formula (relies on iteration number)
        p1 = (k*((3 * k) - 1)) / 2;
        p2 = (k*((3 * k) + 1)) / 2;
        if (n >= p2) {
            //If n is more than p2, must call recursive tasks to break down problem to smaller n's, and adds result to total result pTot (i.e. p(n))
            int x = 0;
            int y = 0;
            ParallelFormula& a = *new(allocate_child()) ParallelFormula(n - p1, &x);
            ParallelFormula& b = *new(allocate_child()) ParallelFormula(n - p2, &y);

            //Set ref_count to two children plus one for the wait
            set_ref_count(3);
            //Start b running
            spawn(b);
            //Start a running and wait for all children (a and b)
            spawn_and_wait_for_all(a);
            //Sum the total
            if (k % 2 != 0)
                *pTot += (x + y);
            else
                *pTot -= (x + y);
        }
        else if (n >= p1) {
                            //If n is more than p1, problem is small and therefore need not be parallelised, result added to pTot
            if (k % 2 != 0)
                *pTot += serialLoop(n - p1);
            else
                *pTot -= serialLoop(n - p1);
            return NULL;
        }
        else
            return NULL;
    }
}
};

调用并行任务的方法:

int parallelLoop(int n) {
int pTot = 0;
ParallelFormula& a = *new(task::allocate_root()) ParallelFormula(n, &pTot);
task::spawn_root_and_wait(a);
return pTot;
}

如果您想查看所有上下文的完整代码:

// Assignment2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "iostream"
#include "tbb/task_scheduler_init.h"
#include "tbb/parallel_reduce.h"
#include "tbb/partitioner.h"
#include "tbb/blocked_range.h"
#include "tbb/tick_count.h"
#include "math.h"

using namespace tbb;
using namespace std;
int p, p1, p2;
int serialLoop(int n);
int n;
int m;

int serialFormula(int pTemp) {
    switch (pTemp) {
    case 0: 
        return 1;
    case 1:
        return 1;
    case 2:
        return 2;
    }
    //If p is any other value it is less than 0 and therefore has nothing to calculate - the current calculation is complete
    return 0;
}

int serialLoop(int n) {
    int pTot = 0;
    for (int k = 1; k > 0; k++) {
        //Checking whether k is even or odd to determine if adding or substracting value of p(x) to make p(n)
        if (n == 0)
            return pTot += 1;
        else if (k % 2 != 0) {
            //Calculate p number using section of Euler's formula
            p = n - ((k*((3 * k) - 1)) / 2);
            //If p is more than 2, must call recursive function to break down problem to smaller n's, and adds result to total result P (i.e. p(n))
            if (p > 2) {
                pTot += serialLoop(p);
            }
            else if (p >= 0) {
                pTot += serialFormula(p);
            }
            else return pTot;

        p = n - ((k*((3 * k) + 1)) / 2);
        if (p > 2) {
            pTot += serialLoop(p);
        }
        else if (p >= 0) {
            pTot += serialFormula(p);
        }
        else return pTot;
    }
    else {
        p = n - ((k*((3 * k) - 1)) / 2);
        if (p > 2) {
            pTot -= serialLoop(p);
        }
        else if (p >= 0) {
            pTot -= serialFormula(p);
        }
        else return pTot;

        p = n - ((k*((3 * k) + 1)) / 2);
        if (p > 2) {
            pTot -= serialLoop(p);
        }
        else if (p >= 0) {
            pTot -= serialFormula(p);
        }
        else return pTot;
    }
}
}

class ParallelFormula : public task {

public:
    int n;
    int* pTot;

//Task constructor
ParallelFormula(int n_, int* pTot_) : n(n_), pTot(pTot_) {}

//Task definition
task* execute() {
    //Checking task is called
    for (int k = 1; k > 0; k++) {
        //Calculate p number using section of Euler's formula
        switch (n) {
        case 0:
            if (k % 2 != 0)
                *pTot += 1;
            else
                *pTot -= 1;
            cout << "Case 0" << endl;
            cout << *pTot << endl;
            return NULL;
        case 1:
            if (k % 2 != 0)
                *pTot += 1;
            else
                *pTot -= 1;
            cout << "Case 1" << endl;
            cout << *pTot << endl;
            return NULL;
        case 2:
            if (k % 2 != 0)
                *pTot += 2;
            else
                *pTot -= 2;
            cout << "Case 2" << endl;
            cout << *pTot << endl;
            return NULL;
        }
        p1 = (k*((3 * k) - 1)) / 2;
        p2 = (k*((3 * k) + 1)) / 2;
        if (n >= p2) {
            //If p is more than 2, must call recursive function to break down problem to smaller n's, and adds result to total result P (i.e. p(n))
            int x = 0;
            int y = 0;
            ParallelFormula& a = *new(allocate_child()) ParallelFormula(n - p1, &x);
            ParallelFormula& b = *new(allocate_child()) ParallelFormula(n - p2, &y);

            //Set ref_count to two children plus one for the wait
            set_ref_count(3);
            //Start b running
            spawn(b);
            //Start a running and wait for all children (a and b)
            spawn_and_wait_for_all(a);
            //Sum the total
            if (k % 2 != 0)
                *pTot += (x + y);
            else
                *pTot -= (x + y);
            cout << "Double p" << endl;
            cout << *pTot << endl;
        }
        else if (n >= p1) {
            if (k % 2 != 0)
                *pTot += serialLoop(n - p1);
            else
                *pTot -= serialLoop(n - p1);
            cout << "Single p" << endl;
            cout << *pTot << endl;
            return NULL;
        }
        else
            return NULL;
    }
}
};

int parallelLoop(int n) {
    int pTot = 0;
    ParallelFormula& a = *new(task::allocate_root()) ParallelFormula(n, &pTot);
    task::spawn_root_and_wait(a);
    return pTot;
}

int main()
{
//Take inputs n and m.
cout << "Enter partition number n:" << endl;
cin >> n;

cout << "Enter modulo m:" << endl;
cin >> m;

//Start timer for serial method
tick_count serial_start = tick_count::now();

//Serial method for computing partition function modulo m.
int sP = serialLoop(n);
int serialMod = sP % m;

//Finish timer for serial method
tick_count serial_end = tick_count::now();

//Output serial results
cout << "Serial result for p(n) is: " << sP << endl;
cout << "Serial result for p(n) mod m is: " << serialMod << endl;
cout << "Serial time (s): " << (serial_end - serial_start).seconds() << endl;

//Start timer for parallel method
tick_count parallel_start = tick_count::now();

//Parallel method for computing partition function
int pP = parallelLoop(n);
int parallelMod = pP % m;

//Finish timer for parallel method
tick_count parallel_end = tick_count::now();

//Output parallel results
cout << "Parallel result for p(n) is: " << pP << endl;
cout << "Parallel result for p(n) mod m is: " << parallelMod << endl;
cout << "Parallel time (s): " << (parallel_end - parallel_start).seconds() << endl;

//Acceleration achieved
cout << "Acceleration achieved was: " << (serial_end - serial_start).seconds() / (parallel_end - parallel_start).seconds() << endl;

return 0;
};

P.S。这部分是基于英特尔TBB文档中的Fibonacci序列示例,所以如果我通过遵循该示例做了一些非常愚蠢的事情,那么我也为此道歉。

1 个答案:

答案 0 :(得分:1)

变量p1p2是全局变量,但您同时在ParallelFormula::execute中写入变量。尝试在ParallelFormula::execute方法中声明它们,例如

int p1 = (k*((3 * k) - 1)) / 2;
int p2 = (k*((3 * k) + 1)) / 2;

同样不要忘记p中的int serialLoop(int n)变量,因为您从ParallelFormula::execute调用此函数。