以下是计算素数的简单代码:
#define END 100000000
// Only pass odd values to this function
int is_prime(uint32_t v)
{
uint32_t end = sqrt(v);
for (uint32_t i = 3; i <= end; i += 2) {
if ((v % i) == 0) {
return 0;
}
}
return 1;
}
int main(int argc, char **argv)
{
// We'll grab 2 as it's the only even prime
int prime_count = 1;
uint32_t bracket = 10;
#pragma omp parallel for num_threads(4)
for (uint32_t i = 3; i < END; i += 2) {
if (i > bracket) {
printf("%12d\t%12d\n", bracket, prime_count);
bracket *= 10;
}
if (is_prime(i)) {
prime_count++;
}
}
printf("%12d\t%12d\n", bracket, prime_count);
return 0;
}
我们的任务是使用OpenMP pragma来加速计算,因为循环100,000,000个整数需要很长时间。我尝试将#pragma语句放在我粘贴的代码中的当前位置,当我运行代码时,我得到以下输出:
10 4
10 1
10 4
10 4
100 25
1000 25
10000 25
100000 25
10000000 262
1410065408 5737822
显然这看起来不正确,我尝试在is_prime()函数的for循环上面放一个#pragma,但是我遇到了编译错误
错误:“与OpenMP结构块无关的分支”。
我意识到这个错误来自可能进入if语句的线程,并在并行操作的中间返回0。线程过早终止,整个操作分开。我觉得将#pragma放在函数调用中是最合适的,因为它可以在每次调用函数时将运行时间减少v / 4。但是,我不知道这样做的正确语法。如果有人能帮助我,我会非常感激。感谢
答案 0 :(得分:2)
鉴于上面已经指示了num_threads( 4 )
,即将进行的操作除了其他三个之外还会发生。这意味着如果一个线程进入prime_count++
并获取prime_count
的“当前”值,以便添加+1
,作为指令命令,其他线程无法在您的代码中等待,在第一个接受者完成++
之前并存储“新”值(应该由其他接受者使用)。在没有任何协调工具的情况下,不受控制的添加在第一眼看上去看起来很好,但是一旦你意识到,你有很多次错误的“初始”值(在很多情况下可以同时 {{1} } - 由任何其他线程组成),所以最终的结果只是一场残骸破坏。
可以明确协调和/或共享变量,但时间很长。
++
更好的举措是主要避免在所有级别上发生冲突读取/写入(最好避免错误对齐和缓存效率低下):
希望你也可以享受扩展的语法修饰,借用true-[PARALLEL]
language occam-pi,以强调线程对齐的代码执行)
[END] PRIME COUNT was omp.FOUND == 664579 <= 10000000
[END] PRIME COUNT was omp.FOUND == 1270607 <= 20000000
[END] PRIME COUNT was omp.FOUND == 1857859 <= 30000000
INF(t: 0)|||: _id
|||(t: 0)|||: 10 1
|||(t: 0)|||: 100 7
|||(t: 0)|||: 1000 44
|||(t: 0)|||: 10000 311
INF(t: 3)|||: _id
|||(t: 3)|||: 10 0
|||(t: 3)|||: 100 5
|||(t: 3)|||: 1000 37
|||(t: 3)|||: 10000 295
INF(t: 2)|||: _id
|||(t: 2)|||: 10 1
|||(t: 2)|||: 100 6
|||(t: 2)|||: 1000 43
|||(t: 2)|||: 10000 308
INF(t: 1)|||: _id
|||(t: 1)|||: 10 1
|||(t: 1)|||: 100 6
|||(t: 1)|||: 1000 43
|||(t: 1)|||: 10000 314
|||(t: 0)|||: 100000 2409
|||(t: 1)|||: 100000 2399
|||(t: 3)|||: 100000 2384
|||(t: 2)|||: 100000 2399
|||(t: 2)|||: 1000000 19669
|||(t: 3)|||: 1000000 19552
|||(t: 1)|||: 1000000 19623
|||(t: 0)|||: 1000000 19653
|||(t: 2)|||: 10000000 166237
|||(t: 0)|||: 10000000 166161
|||(t: 1)|||: 10000000 166204
|||(t: 3)|||: 10000000 165976
:--(t: 0):--: 100000000 464549
:--(t: 1):--: 100000000 464491
:--(t: 2):--: 100000000 464530
:--(t: 3):--: 100000000 464288
[END] PRIME COUNT was omp.FOUND == 1857859 <= 30000000
Feel free to further experiment with the code Live / on-line.
答案 1 :(得分:1)
所有线程可以同时传递if
块并修改bracket
不一致
编译指示“#pragma omp critical
”可以防止这种情况:
#define END 100000000
// Only pass odd values to this function
int is_prime(uint32_t v)
{
uint32_t end = sqrt(v);
for (uint32_t i = 3; i <= end; i += 2) {
if ((v % i) == 0) {
return 0;
}
}
return 1;
}
int main(int argc, char **argv)
{
// We'll grab 2 as it's the only even prime
int prime_count = 1;
uint32_t bracket = 10;
#pragma omp parallel for num_threads(4)
for (uint32_t i = 3; i < END; i += 2) {
#pragma omp critical
{
if (i > bracket) {
printf("%12d\t%12d\n", bracket, prime_count);
bracket *= 10;
}
}
if (is_prime(i)) {
prime_count++;
}
}
printf("%12d\t%12d\n", bracket, prime_count);
return 0;
}
上面的代码并不能完全回答你的问题:我想你想知道素数在10,100,1000之下的数量......
但处理for
的openmp方法如下:它在4中切断循环:
the first from 0 to N/4,
the second from N/4 to N/2,
the first from N/2 to 3*N/4,
the second from 3*N/4 to N.
为了知道素数如何分布,你应该有一些不同的方法:
int main(int argc, char **argv)
{
/* number_of_prime[0] is the number of primes between 0 and 10
number_of_prime[1] is the number of primes between 10 and 100
... */
int number_of_prime[8] = {1, 0};
uint32_t bracket = 10;
#pragma omp parallel for num_threads(4)
for (uint32_t i = 3; i < END; i += 2) {
if (is_prime(i)) {
/* here, i is a prime */
if (i > 10000000)
{
#pragma omp critical
number_of_prime[7]++;
}
else if (i > 1000000)
{
#pragma omp critical
number_of_prime[6]++;
}
else if (i > 100000)
{
#pragma omp critical
number_of_prime[5]++;
}
else if (i > 10000)
{
#pragma omp critical
number_of_prime[4]++;
}
else if (i > 1000)
{
#pragma omp critical
number_of_prime[3]++;
}
else if (i > 100)
{
#pragma omp critical
number_of_prime[2]++;
}
else if (i > 10)
{
#pragma omp critical
number_of_prime[1]++;
}
else
{
#pragma omp critical
number_of_prime[0]++;
}
}
}
/* add some printf hero to display results */
return 0;
}
编辑:
正如祖兰所说,我至少应该使用atomic instead of critical
答案 2 :(得分:1)
user3666197为您提供了如何手动执行此操作的示例。这对理解很好,但你不需要自己做 - 而是你应该使用OpenMP提供的抽象 - 这是一种减少。减少将为每个线程提供一个私有变量,并且在并行计算之后,它们将一起添加到原始变量中。没有中间输出的正确示例只是:
int prime_count = 1;
#pragma omp parallel for reduction(+:prime_count)
for (uint32_t i = 3; i < END; i += 2) {
if (is_prime(i)) {
prime_count++;
}
}
printf("%12d\t%12d\n", 0, prime_count);
并行重现中间输出。我宁愿所有线程都在括号上工作,然后报告聚合结果。通过减少仍然可以轻松实现这一点,并为您提供每个块中有多少素数的正确汇总信息:
int prime_count = 1;
#pragma omp parallel
for (uint32_t bracket = 10; bracket <= END; bracket *= 10)
{
uint32_t start = bracket / 10;
if (start == 1) start = 3;
else start++;
#pragma omp for reduction(+:prime_count)
for (uint32_t i = start; i < bracket; i += 2) {
if (is_prime(i)) {
prime_count++;
}
}
#pragma omp single
printf("%12d\t%12d %p\n", bracket, prime_count);
}
printf("%12d\t%12d\n", 0, prime_count);
我将for
和parallel
分开,因为一次又一次地重新打开并行区域可能代价高昂。