OpenMP,循环并行,处理时间差异大

OpenMP, parallel for loop, Large differences in processing time

本文关键字:时间差 并行 循环 OpenMP 处理      更新时间:2023-10-16

我开发了一个程序,可以从.txt文件中读取数字,并将其存储到一个向量中,经过一系列组合和计算,以确定结果是否与我想要的数字匹配。这些过程将在多个线程中完成,其中每个线程将负责处理并行for循环中的各种迭代次数。

长话短说,当涉及到大数字(例如9个数字)时,处理时间变化很大,其中处理时间可能短至3分钟,也可能超过10分钟。

以下是我迄今为止尝试过的基准:

8 numbers serial : 18.119 seconds
8 numbers multithread (first-try): 10.238 seconds
8 numbers multithread (second-try): 18.943 seconds
9 numbers serial : 458.980 seconds
9 numbers multithread (first-try): 172.347 seconds
9 numbers multithread (second-try): 519.532 seconds   //Seriously?
//Another try after suggested modifications
9 numbers multithread (first-try): 297.017 seconds
9 numbers multithread (second-try): 297.85 seconds
9 numbers multithread (third-try): 304.755 seconds
9 numbers multithread (fourth-try): 396.391 seconds

因此,问题是,是否有任何可能的方法来改进程序(多线程),使其只需要最少的时间来打乱/计算数字?

以下是发生并行for循环的代码部分(经过轻微修改后更新):

#include <iostream>    
#include <fstream>
#include <string>
#include <vector>
#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include <Windows.h>
#include <omp.h>
#define OPERATORSIZE 3
using namespace std;
int cur_target;
ofstream outFile;
string get_operator(int i) {
        switch (i) {
        case 0:
            return "+";
        case 1:
            return "-";
        case 2:
            return "*";
        case 3:
            return "/";
        default:
            return "";
        }
}
int prev_num_pos(vector<int> &cur_equation, int count) { 
    for (int i = count - 1; i >= 0; i--) { 
        if (cur_equation[i] != -1) return i + 1;
    }
    return 0;
}
bool nextoperator(int k, vector<int> &operator_array) {
        for (int i = k - 2; i >= 0; i--) {
            if (operator_array[i] < OPERATORSIZE) {
                operator_array[i] += 1;
                break;
            }
            else
                operator_array[i] = 0;
            switch (i) {
            case 0:
                return false;
            }
        }
        return true;
}
void vector_combination(vector<int> int_list) {                                             // Generate the number combinations from the number list
    bool div_remainder = false;
    int count = 0;
    #pragma omp parallel for schedule(dynamic) firstprivate(div_remainder) reduction(+:count) 
    for (int i = 0; i < int_list.size(); ++i) {
        vector<int> cur_equation, cur_temp, cur_list, operator_array;
        auto list = int_list;
        rotate(list.begin(), list.begin() + i, list.begin() + i + 1);
        do
        {
            cur_list.clear();
            operator_array.clear();
            for (auto x : list)
                cur_list.push_back(x);
            for (int i = 0; i < cur_list.size() - 1; i++)
                operator_array.push_back(0);
            do
            {
                div_remainder = false;
                count = 0;
                cur_equation = operator_array;
                cur_temp = cur_list;
                for (int i = 0; i < cur_equation.size(); ++i) {                                 // Check for equation priorities
                    if (cur_equation[i] == 3) {
                        count = i;
                        if (cur_temp[count] % cur_temp[count + 1] != 0) {
                            div_remainder = true;
                            break;
                        }
                    }
                }
                if (div_remainder)
                    continue;
                for (int i = 0; i < cur_temp.size() - 1; ++i) {
                    count = -1;
                    if (cur_equation[i] == 2 || cur_equation[i] == 3) {
                        count = prev_num_pos(cur_equation, i);
                    }
                    else
                        continue;
                    if (cur_equation[i] == 2) {
                        cur_temp[count] *= cur_temp[i + 1];
                        cur_equation[i] = -1;
                    }
                    else if (cur_equation[i] == 3) {
                        if (cur_temp[i + 1] != 0) {
                            cur_temp[count] /= cur_temp[i + 1];
                            cur_equation[i] = -1;
                        }
                        else {
                            div_remainder = true;
                            break;
                        }
                    }
                }
                if (div_remainder)
                    continue;
                for (int i = 0; i < cur_temp.size() - 1; ++i) {
                    switch (cur_equation[i]) {
                    case 0: {
                        cur_temp[0] += cur_temp[i + 1];                                             // Addition
                        cur_equation[i] = -1;
                        break;
                    }
                    case 1: {                                                                       // Subtraction
                        cur_temp[0] -= cur_temp[i + 1];
                        cur_equation[i] = -i;
                        break;
                    }
                    }
                }

                if (cur_temp[0] == cur_target) {
                    #pragma omp critical
                    {
                        for (int i = 0; i < cur_list.size(); ++i) {
                            outFile << cur_list[i];
                            if (i < cur_list.size() - 1) { outFile << get_operator(operator_array[i]); }
                        }
                        outFile << "n";
                    }
                }
            } while (nextoperator(cur_list.size(), operator_array));
            // Send to function to undergone a list of operator combinations
        } while (next_permutation(list.begin() + 1, list.end()));
    }
}
int main(void) {
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    vector<int> int_list;
    string line;
    ifstream myfile("Problem.txt");
    if (myfile.is_open()) {
        while (getline(myfile, line)) {
            int num = stoi(line);
            int_list.push_back(num);
            cur_target = num;
        }
    }
    else
        cout << "Unable to open file." << endl;
    myfile.close();
    int_list.pop_back();
    sort(int_list.begin(), int_list.end());
    outFile.open("answerswer.txt");
    vector_combination(int_list);
    outFile.close();
    int answer_count = 0;
    myfile.open("answerswer.txt");
    if (myfile.is_open()) {
        while (getline(myfile, line)) {
            ++answer_count;
            if (answer_count > 1)
                break;
        }
    }
    myfile.close();
    if (answer_count == 0) {
        outFile.open("answerswer.txt");
        outFile << "-1" << endl;
    }
    outFile.close();
    return 0;
}

至于样本输入,创建一个名为"Problem.txt"的.txt文件,其中包含这样的随机数(最后一个数字是目标结果)(更新为用于基准测试的当前样本输入):

28
55
78
77
33
65
35
62
19
221

程序运行的硬件/软件规范:处理器:i5 Sandy Bridge 2500K,Ram:8GB,操作系统:Windows 10专业版,IDE:Visual Studio 2015 Enterprise Edition,

在if条件内移动#pragma omp critical。由于cur_temp是线程专用的,而cur_target是全局只读的,因此没有必要用关键节来保护条件。这一变化极大地减少了线程之间的直接交互,并且在我的系统上,始终如一地加快了并行版本的速度。

我只能微弱地猜测性能变化受到不同线程上运行的循环之间的相移(看似随机)的影响。

如果性能变化仍然存在,请尝试启用线程绑定。查看OpenMP实现的文档,查找OMP_PROC_BIND、"线程固定"、"绑定"或"亲和性"。

显然,运行时的差异是由向量引起的。我使用性能分析器对其进行了检查,并注意到在向量之间复制值所花费的时间不一致。我已经将它修改为指针数组,运行时现在得到了极大的改进和一致性。