C ++中并行openmp多循环的问题
Prolem with parallel openmp multi loop in C++
我编写了一个程序来查找具有 8 个循环的密钥。
我花了 2 个小时才找到钥匙。
现在我想使用 openmp 来减少时间,但我无法让它工作,它一无所获。
请帮助我,我是 openmp 的新手,谢谢。
int main()
{
cout << endl;
clock_t start, end;
cout << "\nStart\n";
start = clock();
int i1, i2, i3, i4, i5, i6, i7, i8;
#pragma omp parallel num_threads(12)
{
#pragma omp for collapse(8)
for (i1 = 'a'; i1 <= 'z'; i1++)
for (i2 = 'a'; i2 <= 'z'; i2++)
for (i3 = 'a'; i3 <= 'z'; i3++)
for (i4 = 'a'; i4 <= 'z'; i4++)
for (i5 = 'a'; i5 <= 'z'; i5++)
for (i6 = 'a'; i6 <= 'z'; i6++)
for (i7 = 'a'; i7 <= 'z'; i7++)
for (i8 = 'a'; i8 <= 'z'; i8++)
{
unsigned short ot_2[6] = { 0x66c0, 0xc5cc, 0x0bcd ,0x7201, 0x0923,0xfc29 };
unsigned short wt_2[6] = { 0xdb93, 0xe790, 0x1dc0 ,0xbf79, 0x1fdf,0x5bdb };
unsigned short Key[4];
Key[0] = ((i1 << 8) ^ i2);
Key[1] = ((i3 << 8) ^ i4);
Key[2] = ((i5 << 8) ^ i6);
Key[3] = ((i7 << 8) ^ i8);
if (encrypt(ot_2[0], Key) == wt_2[0])
if (encrypt(ot_2[1], Key) == wt_2[1])
if (encrypt(ot_2[2], Key) == wt_2[2])
if (encrypt(ot_2[3], Key) == wt_2[3])
if (encrypt(ot_2[4], Key) == wt_2[4])
if (encrypt(ot_2[5], Key) == wt_2[5])
printf("%c%c%c%c%c%c%c%c", i1, i2, i3, i4, i5, i6, i7, i8);
}
}
end = clock();
double time = (end - start) / CLOCKS_PER_SEC;
int hour = time / 60 / 60, minutes = (time - hour * 60 * 60) / 60, second = time - hour * 60 * 60 - minutes * 60;
cout << "Time: " << hour << ":" << minutes << ":" << second << endl;
}
顺便说一句,我在 windows 上使用 g++。
更新:这里是加密函数
unsigned short encrypt(unsigned short x, unsigned short Key[])
{
for (int i = 0; i < 2; i++)
{
x ^= Key[i];
x = (Pi[x & 0xf] ^ (Pi[(x >> 4) & 0xf] << 4) ^ (Pi[x >> 8 & 0xf] << 8) ^ (Pi[x >> 12 & 0xf] << 12));
x = functionP(x);
}
x ^= Key[2];
x = (Pi[x & 0xf] ^ (Pi[(x >> 4) & 0xf] << 4) ^ (Pi[x >> 8 & 0xf] << 8) ^ (Pi[x >> 12 & 0xf] << 12));
x ^= Key[3];
return x;
}
这里是函数P
#include<iostream>
#include<fstream>
#include<time.h>
#include<omp.h>
#include<vector>
#include <string>
using namespace std;
unsigned char Pi[16] = { 0xa,5,6,4,0xb,3,0xd,0x9,0xc,0xf,0xe,0,7,1,0x8,2 };
unsigned char P[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
unsigned short functionP(unsigned short x)
{
unsigned short temp = x;
x = 0;
for (int i = 0; i < 16; i++)
x ^= (((temp >> (15 - P[i])) & 0x1) << (15 - i));
return x;
}
编辑:由于 OP 是新的 openMP,我在我的回答中添加了更多细节。
您的代码存在问题,折叠循环总共有 25^8=152,587,890,625 次迭代,大于(32 位)int
(2,147,483,647) 的最大值。编译器 (g++ 10.2) 无法处理这种情况并生成不正确的代码。有 2 种解决方案来处理它:
使用 long long
而不是 int
。在这种情况下,折叠所有循环对编译器来说不是问题。
删除 collapse(8)
或将其更改为 collapse(n) n=2..6
。如果你删除 collapse(8)
上面提到的问题就解决了,但在这种情况下你的循环变量(i2,i3,..i8
)变得共享,因为它们不再是并行循环构造的循环变量并且定义在并行区域之外.它会导致数据竞争,因为它们被不同的线程使用并给出不正确的结果。请注意,(并行循环构造的)循环变量由编译器私有化,因此默认情况下 i1
是私有的,如果您使用 collapse(8)
所有循环变量都被私有化。要使您的循环变量私有,您可以通过 private
子句显式设置它:
#pragma omp parallel num_threads(12) private(i2,i3,i4,i5,i6,i7,i8)
由于您是 openMP 的新手,使用 default(none)
子句是一种很好的做法,因此您不得不定义所有变量的共享属性。如果您忘记这样做,您将收到一个编译器错误。
#pragma omp parallel num_threads(12) default(none) private(i2,i3,i4,i5,i6,i7,i8)
另一条评论是,您应该始终在所需的最小范围内定义变量。由于在循环之后不使用循环变量,因此最好在 for 循环的 init-statement 中定义它们:
for (int i1 = 'a'; i1 <= 'z'; i1++)
在这种情况下,变量 i1,i2..i8
定义在并行区域内,因此默认情况下它们是私有的,因此您不必担心它们。另请注意,2 个 openmp 指令可以合并为一个所谓的组合指令,因此将它们放在一起您的代码将如下所示:
#pragma omp parallel for default(none) num_threads(12) collapse(6)
for (int i1 = 'a'; i1 <= 'z'; i1++)
for (int i2 = 'a'; i2 <= 'z'; i2++)
for (int i3 = 'a'; i3 <= 'z'; i3++)
for (int i4 = 'a'; i4 <= 'z'; i4++)
for (int i5 = 'a'; i5 <= 'z'; i5++)
for (int i6 = 'a'; i6 <= 'z'; i6++)
for (int i7 = 'a'; i7 <= 'z'; i7++)
for (int i8 = 'a'; i8 <= 'z'; i8++)
{ .....
我编写了一个程序来查找具有 8 个循环的密钥。 我花了 2 个小时才找到钥匙。 现在我想使用 openmp 来减少时间,但我无法让它工作,它一无所获。 请帮助我,我是 openmp 的新手,谢谢。
int main()
{
cout << endl;
clock_t start, end;
cout << "\nStart\n";
start = clock();
int i1, i2, i3, i4, i5, i6, i7, i8;
#pragma omp parallel num_threads(12)
{
#pragma omp for collapse(8)
for (i1 = 'a'; i1 <= 'z'; i1++)
for (i2 = 'a'; i2 <= 'z'; i2++)
for (i3 = 'a'; i3 <= 'z'; i3++)
for (i4 = 'a'; i4 <= 'z'; i4++)
for (i5 = 'a'; i5 <= 'z'; i5++)
for (i6 = 'a'; i6 <= 'z'; i6++)
for (i7 = 'a'; i7 <= 'z'; i7++)
for (i8 = 'a'; i8 <= 'z'; i8++)
{
unsigned short ot_2[6] = { 0x66c0, 0xc5cc, 0x0bcd ,0x7201, 0x0923,0xfc29 };
unsigned short wt_2[6] = { 0xdb93, 0xe790, 0x1dc0 ,0xbf79, 0x1fdf,0x5bdb };
unsigned short Key[4];
Key[0] = ((i1 << 8) ^ i2);
Key[1] = ((i3 << 8) ^ i4);
Key[2] = ((i5 << 8) ^ i6);
Key[3] = ((i7 << 8) ^ i8);
if (encrypt(ot_2[0], Key) == wt_2[0])
if (encrypt(ot_2[1], Key) == wt_2[1])
if (encrypt(ot_2[2], Key) == wt_2[2])
if (encrypt(ot_2[3], Key) == wt_2[3])
if (encrypt(ot_2[4], Key) == wt_2[4])
if (encrypt(ot_2[5], Key) == wt_2[5])
printf("%c%c%c%c%c%c%c%c", i1, i2, i3, i4, i5, i6, i7, i8);
}
}
end = clock();
double time = (end - start) / CLOCKS_PER_SEC;
int hour = time / 60 / 60, minutes = (time - hour * 60 * 60) / 60, second = time - hour * 60 * 60 - minutes * 60;
cout << "Time: " << hour << ":" << minutes << ":" << second << endl;
}
顺便说一句,我在 windows 上使用 g++。
更新:这里是加密函数
unsigned short encrypt(unsigned short x, unsigned short Key[])
{
for (int i = 0; i < 2; i++)
{
x ^= Key[i];
x = (Pi[x & 0xf] ^ (Pi[(x >> 4) & 0xf] << 4) ^ (Pi[x >> 8 & 0xf] << 8) ^ (Pi[x >> 12 & 0xf] << 12));
x = functionP(x);
}
x ^= Key[2];
x = (Pi[x & 0xf] ^ (Pi[(x >> 4) & 0xf] << 4) ^ (Pi[x >> 8 & 0xf] << 8) ^ (Pi[x >> 12 & 0xf] << 12));
x ^= Key[3];
return x;
}
这里是函数P
#include<iostream>
#include<fstream>
#include<time.h>
#include<omp.h>
#include<vector>
#include <string>
using namespace std;
unsigned char Pi[16] = { 0xa,5,6,4,0xb,3,0xd,0x9,0xc,0xf,0xe,0,7,1,0x8,2 };
unsigned char P[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
unsigned short functionP(unsigned short x)
{
unsigned short temp = x;
x = 0;
for (int i = 0; i < 16; i++)
x ^= (((temp >> (15 - P[i])) & 0x1) << (15 - i));
return x;
}
编辑:由于 OP 是新的 openMP,我在我的回答中添加了更多细节。
您的代码存在问题,折叠循环总共有 25^8=152,587,890,625 次迭代,大于(32 位)int
(2,147,483,647) 的最大值。编译器 (g++ 10.2) 无法处理这种情况并生成不正确的代码。有 2 种解决方案来处理它:
使用
long long
而不是int
。在这种情况下,折叠所有循环对编译器来说不是问题。删除
collapse(8)
或将其更改为collapse(n) n=2..6
。如果你删除collapse(8)
上面提到的问题就解决了,但在这种情况下你的循环变量(i2,i3,..i8
)变得共享,因为它们不再是并行循环构造的循环变量并且定义在并行区域之外.它会导致数据竞争,因为它们被不同的线程使用并给出不正确的结果。请注意,(并行循环构造的)循环变量由编译器私有化,因此默认情况下i1
是私有的,如果您使用collapse(8)
所有循环变量都被私有化。要使您的循环变量私有,您可以通过private
子句显式设置它:#pragma omp parallel num_threads(12) private(i2,i3,i4,i5,i6,i7,i8)
由于您是 openMP 的新手,使用default(none)
子句是一种很好的做法,因此您不得不定义所有变量的共享属性。如果您忘记这样做,您将收到一个编译器错误。#pragma omp parallel num_threads(12) default(none) private(i2,i3,i4,i5,i6,i7,i8)
另一条评论是,您应该始终在所需的最小范围内定义变量。由于在循环之后不使用循环变量,因此最好在 for 循环的 init-statement 中定义它们:
for (int i1 = 'a'; i1 <= 'z'; i1++)
在这种情况下,变量 i1,i2..i8
定义在并行区域内,因此默认情况下它们是私有的,因此您不必担心它们。另请注意,2 个 openmp 指令可以合并为一个所谓的组合指令,因此将它们放在一起您的代码将如下所示:
#pragma omp parallel for default(none) num_threads(12) collapse(6)
for (int i1 = 'a'; i1 <= 'z'; i1++)
for (int i2 = 'a'; i2 <= 'z'; i2++)
for (int i3 = 'a'; i3 <= 'z'; i3++)
for (int i4 = 'a'; i4 <= 'z'; i4++)
for (int i5 = 'a'; i5 <= 'z'; i5++)
for (int i6 = 'a'; i6 <= 'z'; i6++)
for (int i7 = 'a'; i7 <= 'z'; i7++)
for (int i8 = 'a'; i8 <= 'z'; i8++)
{ .....