使用 MPI 的质数计数器(埃拉托色尼筛法),太慢了
Posted
技术标签:
【中文标题】使用 MPI 的质数计数器(埃拉托色尼筛法),太慢了【英文标题】:Prime Number Counter (Sieve of Eratosthenes) Using MPI, Too Slow 【发布时间】:2014-06-28 15:38:24 【问题描述】:以下代码对所有质数进行计数,直到 50,000,000 并且 100% 正确运行。问题是它需要的时间太长了。使用 32 个处理器,我得到了大约 42 秒。我的一个同行有 16 秒,我似乎无法找到我的代码滞后的地方。请留下任何建议:)
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
const int n=50000000; //number until which primes are counted (inclusive)
int main (int argc, char *argv[])
long local_count=0, global_count=0, //variables used to keep count of the primes
start, finish, //highest and lowest possible primes for this processor
i;
int rank, //processor id
p, //number of processes
size, proc0_size; //amount of numbers to check on any processor and proc 0
double runtime; //variable used to keep track of total elapsed time
//initialize the MPI execution environment
MPI_Init (NULL, NULL);
//determine the rank of the calling processor in the communicator
MPI_Comm_rank (MPI_COMM_WORLD, &rank);
//determine the size of communicator
MPI_Comm_size (MPI_COMM_WORLD, &p);
//Ensures that every processor enters code at around the same time
MPI_Barrier(MPI_COMM_WORLD);
//Start the timer
runtime = -MPI_Wtime();
//compute the range and size to be used for this processor
start = 2 + rank*(n-1)/p;
finish = 1 + (rank+1)*(n-1)/p;
size = finish - start + 1;
//determine the size of processor 0
proc0_size = (n-1)/p;
//in the case where there are too many processors for the amount of numbers
//to check...
if ((2 + proc0_size) < (long) sqrt((double) n))
if (rank == 0)
printf ("Too many processors to calculate the number of primes up to %d\n", n);
MPI_Finalize();
exit(1);
int j;
//check every number in the range of this processor
for (j=start; j<=finish; j++)
//if the number is not composite, ie prime, increment the local counter
if (isComposite(j)==0)
local_count++;
//MPI_Reduce used to combine all local counts
MPI_Reduce (&local_count, &global_count, 1, MPI_LONG_LONG, MPI_SUM, 0, MPI_COMM_WORLD);
//adjust the total elapsed runtime
runtime += MPI_Wtime();
printf("Process %d finished\n", rank);
// print the results from process ranked root
if (rank == 0)
printf ("There are %ld primes less than or equal to %d\n", global_count, n);
printf ("Total elapsed time: %f seconds\n", runtime);
//terminate the execution environment
MPI_Finalize ();
return 0;
//function to check if a number is composite
//returns 0 if it is not composite (prime) and returns a 1 if it is composite
int isComposite (int num)
int retval=0;
if(num==1)
retval = 1;
else if(num%2 == 0 && num!=2)
retval = 1;
if(retval != 1)
int j;
for(j=3; j<num; j+=2)
if(num%j == 0 )
retval = 1;
break;
if(j*j>num) break;
if(retval == 0)
return 0;
else
return 1;
【问题讨论】:
【参考方案1】:我编译了你的测试并在本地机器上运行它。
我明白了
3001134 primes less than or equal to 50000000
Total elapsed time:
大约需要 35-37 秒。我的电脑有 4 个 2.4 GHz 的 Q6600 内核,64 位 ubuntu。测试编译为mpicc t.c -o t -lm -O3
。
只有两个核心 - 时间是 66 秒。
我注意到:排名较高的进程有更多的工作要做,并且它们比排名较低的进程更晚结束计算。因此,当您使用批次流程时,总执行时间由排名最高的最后一个流程定义。
尝试重新分配工作(为更高级别的进程分配更少的段)并优化您的isComposite
函数。正如我在使用 perf record
进行分析的结果中看到的那样,num%j == 0
行需要很长时间(大约 80%)。
最好先准备小素数列表并用它们进行筛分,然后再切换到更昂贵的筛分。
筛分也意味着不在 start..finish 上迭代 j
并测试每个 j
,而是从 start..finish 创建数组,然后使用 i
迭代 - 每个数字减去 sqrt(finish)
,并标记 @ 987654334@、array[3*i]
、array[4*i]
、array[5*i]
等作为复合(您可以使用加法而不是乘法)。然后,您将从数组中计算未标记的元素,以从区间中获取素数。 (请检查来自http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes 的动画)。您的代码可能是最慢的http://en.wikipedia.org/wiki/Trial_division
【讨论】:
在实现 simples sieve 之后,我的程序可以在 2 个或 4 个 CPU 上在 21 秒内找到高达 500000000 的所有素数(是原始情况的十倍)。原稿尺寸 - 2.2 - 2.6 秒。 PS:验证表在这里:primes.utm.edu/howmany.shtml以上是关于使用 MPI 的质数计数器(埃拉托色尼筛法),太慢了的主要内容,如果未能解决你的问题,请参考以下文章