需要帮助使用openmp并行化C++代码

Need help parallelizing the C++ code using openmp

本文关键字:并行化 C++ 代码 openmp 帮助      更新时间:2023-10-16

我已经在文本文件values.txt中逐行生成了16 0和16 1的所有32位排列。例如

00000000000000001111111111111111
00000000000000010111111111111111
00000000000000011011111111111111
00000000000000011101111111111111

等等…

让我们考虑一下,文本文件的每一行都是一个布尔函数。我需要检查这个函数在域中的可逆性。

为此,我从文本文件中提取了第一行,并将其存储到维度为32x1的列矩阵中,矩阵a[][]。

在嵌套的for循环中,我基本上是以3x3矩阵的形式生成域值,我需要检查函数的可逆性。我创建了一个维数为3x3的矩阵g[][],它将存储从1到2^9的所有编号的二进制表示。例如-对于0,矩阵g看起来像-

0 0 0
0 0 0
0 0 0

对于1,矩阵g将是-

0 0 0 
0 0 0
0 0 1

对于2个矩阵,g将是

0 0 0
0 0 0
0 1 0

依此类推,直到2^9。

对于上面从0到2^9生成的每个矩阵,我正在基于我的函数计算一个维度为3x3的新矩阵u[][]。这是通过对矩阵的每个元素读取5个相邻值来完成的。

例如,考虑g矩阵为

0 0 0
0 1 1
1 0 0

我拾取第一个元素,即g[0][0],使用五个相邻值(顶部值、左侧值、元素本身、右侧值、下方值),即g[2][0]、g[0][2]、g[0][0]、g[0][1]、g[1][0],为其计算一个新值。这5个数字组合表示二进制数字。我计算它的十进制等价物,并且十进制值对应于矩阵a[][]的行号,我必须用它来更新u[0][0]的值。我将对g的每个元素重复上述过程,最终得到一个3x3的u矩阵。

这个完整的过程是针对一个矩阵,它的矩阵对应于0。像这样,对于从0到2^9的每个g[][]矩阵,我将创建2^9矩阵。

在任何时候,如果两个矩阵g[][],矩阵u[][]恰好相同,我中止函数,读取文本文件的第二行,然后再次开始上述过程,即,我对导致重复矩阵的函数不感兴趣。如果所有的2^9矩阵恰好不同,我将相应函数的值(文本文件中的行)写入另一个文本文件。

因此,总的来说,我需要创建一个总共60亿*2^9的矩阵来进行整体计算。

问题是,对于文本文件中的特定函数,2^9矩阵是单独计算的。如果我能以某种方式将它们并行化,我会大大减少计算时间。。。

#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <math.h>
using namespace std;
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
#include <boost/lexical_cast.hpp>
#include <cctype>
#include <boost/assign/list_of.hpp>
#include <set>
#include <stdint.h>
#include <omp.h>
#define convertToString(x) #x
using namespace boost::assign;
int main()
{
    ifstream infile;
    infile.open("values.txt");
    ofstream outfile;
    outfile.open("haha.txt");
    short a[32][1];
    while(!infile.eof())
    {
        string STRING;
        getline(infile,STRING);
        set<string> SET;
        int count=0;

        for(int i=0;i<32;i++)
        {
                a[i][0]=STRING.at(i)-'0';
        }

        int g[9];
        int u[9];
        char buffer[10];
        buffer[9] = 0;
        uint16_t f = 0;
        int max = (int)pow(2,3);

        for(int r=0;r<max && count!=1;r++)
        {
           for(int s=0;s<max && count!=1;s++)
           {
              for(int t=0;t<max && count!=1;t++)
              {
                for(int i = 0; i < 9; ++i)
                {
                   g[i] = (f & (1 << (8 - i))) != 0;
                }
                ++f;
                u[0]=a[(g[6]*2*2*2*2)+(g[2]*2*2*2)+(g[0]*2*2)+(g[1]*2)+(g[3]*1)][0];
                u[1]=a[(g[7]*2*2*2*2)+(g[0]*2*2*2)+(g[1]*2*2)+(g[2]*2)+(g[4]*1)][0];
                u[2]=a[(g[8]*2*2*2*2)+(g[1]*2*2*2)+(g[2]*2*2)+(g[0]*2)+(g[5]*1)][0];
                u[3]=a[(g[0]*2*2*2*2)+(g[5]*2*2*2)+(g[3]*2*2)+(g[4]*2)+(g[6]*1)][0];
                u[4]=a[(g[1]*2*2*2*2)+(g[3]*2*2*2)+(g[4]*2*2)+(g[5]*2)+(g[7]*1)][0];
                u[5]=a[(g[2]*2*2*2*2)+(g[4]*2*2*2)+(g[5]*2*2)+(g[3]*2)+(g[8]*1)][0];
                u[6]=a[(g[3]*2*2*2*2)+(g[8]*2*2*2)+(g[6]*2*2)+(g[7]*2)+(g[0]*1)][0];
                u[7]=a[(g[4]*2*2*2*2)+(g[6]*2*2*2)+(g[7]*2*2)+(g[8]*2)+(g[1]*1)][0];
                u[8]=a[(g[5]*2*2*2*2)+(g[7]*2*2*2)+(g[8]*2*2)+(g[6]*2)+(g[2]*1)][0];

                for(int i = 0; i < 9; ++i)
                {
                   buffer[i] = '0' + u[i];
                }
                if(!SET.insert(::std::string(buffer)).second)
                {
                   count = 1;
                }
             }
          }
        }
        if(count==0)
        {
            outfile<<STRING<<"n";
            cout<<STRING<<"n";
        }

    }
        infile.close();
        outfile.close();
        return 0;
    }

当第二维度仅为1时,不需要使用二维阵列。只需定义一个[32],并在访问数组的任何地方省略第二个索引运算符([0])(可能只会提高可读性,我希望编译器无论如何都会对此进行优化,但这样做是安全的)。

您的convert函数无效,将所有时间都准备为字符串,每次都会创建一个新的字符串对象。在缓冲区中这样做一次:

char buffer[10];
buffer[9] = 0;
for(int i = 0; i < 9; ++i)
{
    buffer[i] = '0' + ((dec & (1 << (8 - i))) != 0);
}
return ::std::string(buffer);

为什么只输出9位数字而不是全部输出16位?

循环中的u数组也是如此。。。

更高一级:

string binary=in.convert(f++);
for(int i=0;i<9;i++)
    g[i]=binary.at(i)-'0';

你先转换一个字符串,然后再将其转换回数字?为什么不将数组传递给转换函数并直接赋值(0和1,而不是"0"answers"1")?

您只在一个地方使用convert函数——也许您想将其内联。至少,要使它成为静态的,因为它不依赖于任何类成员(如果没有其他成员函数,则使用名称空间而不是类)。


编辑:我允许简单地内联整个内容(忽略杂注):

int g[9];
int u[9];
char buffer[10];
buffer[9] = 0;
uint16_t f = 0;
int max = (int)pow(2,3);
for(int r=0;r<max;r++
{
    for(int s=0;s<max;s++)
    {
        for(int t=0;t<max;t++)
        {
            for(int i = 0; i < 9; ++i)
            {
                g[i] = (f & (1 << (8 - i))) != 0;
            }
            ++f;
            /* calculate the u array here */
            for(int i = 0; i < 9; ++i)
            {
                buffer[i] = '0' + (u[i] != 0);
            }
            if(!SET.insert(::std::string(buffer)).second)
            {
                count = 1;
            }
        }
    }
}

预先计算了功率,不确定编译器是否会对其进行优化。。。

如果将大小与CPU寄存器大小匹配的整数类型用于u和g数组,则可能会获得一些额外的性能增益。。。

您没有检查您的数组a可以获得哪些值。可能,任何一个都可能。如果你保证这些值总是只有0或1,你甚至可以将代码缩短得最少:

buffer[i] = '0' + u[i];

早点离开你的循环:

#pragma omp parallel
{
    for(int r=0;r<(int)pow(2,3);r++)
    {
        for(int s=0;s<(int)pow(2,3);s++)
        {
            #pragma omp parallel for shared(SET,count,f)
            for(int t=0;t<(int)pow(2,3);t++)
            {
                /* ... */
                    count = 1;
                    goto EndOfLoop;
                /* ... */
            }
        }
    }
    :EndOfLoop;
}

"分支(goto)进入或离开平行区域是非法的",但不是在内部,正如我读到的。。。变体是具有

for(int r=0; count == 0 && r<(int)pow(2,3);r++)

对于所有三个环路,但这些额外的if性价比。。。