并行化Eratosthenes算法寻找素数

时间:2012-02-16 07:28:51

标签: java c++ multithreading algorithm

以两种方式并行化Eratosthenes方法的筛选

  1. 使用Java和
  2. 使用C / C ++和Pthreads
  3. 为2个和4个核心CPU找到THRESHOLD的最佳值。

    任何人都可以帮助我如何做到这一点。我正在学习java&的线程C / C ++。我需要用什么东西来并行化这个算法

5 个答案:

答案 0 :(得分:10)

请注意,使用Sieve of Eratostheens方法查找素数表后,找到素数i后,您会将i*n设置为每个n的非素数

请注意,对于您知道它们是素数的2个元素 - i,j您可以并行执行,i不需要j中的任何信息,反之亦然。
当然每个k素数都是一样的。

但请注意,查找数字是否为素数 - 取决于最后的计算!因此,在将数字标记为非素数并找到要使用的下一个素数之间需要barrier

一个好的起点可能是:

repeat until finished filling the table:
    1. Find k prime numbers [serially]
    2. Fill the table for these k numbers [parallely]
    3. after all threads finished step 2 [barrier] - return to 1

由于它似乎是家庭作业,我只会给出这些提示,让你做其余的工作。如果您以后遇到任何具体问题 - 请提出一个新问题并告诉我们您已做过的事情。

编辑:一个更重要的提示,请注意,如果i为素数,则计算从i派生的所有非素数不会影响以下事实:每个j都会i < j < 2i为素数。使用此事实来查找k素数,例如,您不希望在算法的第一次迭代中将2,3,4作为素数。

答案 1 :(得分:5)

Prime筛子占用大量空间。

如果每个数字为1位,则需要1千兆位(230)来存储N = ~109的筛子。并且1太比特(240)存储筛,N = ~1012,这是237字节= 27GB = 128GB。 如果仅存储奇数,则N = 109需要16个数字/ B,64GB。使用16GB的笔记本电脑,您将被限制在N&lt; 235。很高兴开始破解RSA 70位。 如果使算法更加复杂并且不会在seive中存储3 5或7的倍数,则1个字节将保存30个数字的筛位,这将使~236 = 2(30 + 6)= ~64 x 109,这对于RSA72:lol:要破解RSA1024,你需要(1024/2 = 512,512-36 = 476)
2476(~10159)倍的记忆。

因此,内存使用是主要问题。

毕竟,即使是快速RAM也比CPU的L1缓存快数百倍,因此您希望将数据设置为32KB或高达256KB而不是数十亿TB。

由于筛网是随机访问的,因此它不会自动加载到缓存中,并且您将获得连续的缓存未命中。你需要通过大块完成整个工作块的数字范围。虽然chunk-size是你最快的内存(CPU或GPU的L1 L2)。

让我们跳过复杂的部分......

您将获得取消标记当前垃圾中复合材料所需的素数列表。如果已经筛选了大数字,那就说1030到1030 + 108,100M数字的范围有大约1M的素数在列表中等待,只是为了取消其中一个复合物的标记,这个大小的每个素数需要(我猜)大约128B是存储,所以你需要128MB。幸运的是,这个列表是按顺序访问的,并且在需要时它可能已经在缓存中。但无论如何,你需要zillion字节来存储下一个筛子的素数列表。

关于多线程,GPU的可扩展性,数千个线程,例如

列表中的每个素数在最快的内存中取消标记1位,基本上随机访问它。 GPu不会逐位写入,而是每次操作写入一个la 128B,它将是1024位。当1个线程试图取消标记1位时,另一个线程取消标记另一个位,而第一个线程的位将再次取值1。为了阻止/屏障/锁定内存将停止所有线程,并且在许多线程运行时没有速度增加。

所以线程不应该分享筛子。需要分享筛分素数列表,以便每个线程都有自己的一大块筛子,并使用相同的素数进行取消标记。但是在取消标记之后,他们需要将“素数”安排到共享的素数列表中以用于未来的筛选,这将在数百万次筛选之后执行,同时其他线程正在更改相同的列表。 Pretyy再次陷入困境。

在减慢速度的同时使其平行非常容易。通常情况下,重新计算每个线程中的内容比通过缓慢的总线(如RAM,PCIe,SSD,GBit-net)进行重新计算更快......但如果您只有一些线程,则可能并且不是很复杂。

答案 2 :(得分:1)

一种方法是让单个线程在筛子中找到下一个素数,然后让所有线程同时标记多个。将为每个线程分配一个不同的数组部分,以尽可能避免内存共享。因此,每个线程都需要确定它将处理的倍数范围。

答案 3 :(得分:0)

首先,并行化是否会影响表的创建,或者 只需使用它来确定数字是否为素数。在一个 真正的应用程序,我会做第一个离线,所以表格会出现 在最终的C ++代码中作为静态初始化的C样式数组。所以 并行化问题无关紧要。从来没有 应该在第二个修改,你可以从尽可能多的线程访问 你想要,不用担心。

但是,我怀疑这个练习的目的是让你使用 多个线程来构造表。 (否则,它没有 感觉。)在这种情况下:表由一系列循环构成, 步骤2,3,5 ......这些循环中的每一个都可以单独执行 线程,但......需要某种同步 并发访问。如果将表视为单个对象,则使用 只有一个锁定,你最终会以顺序方式运行循环 (因为你在循环之外获得锁定)或支出 获得和释放锁的时间多于做任何实际工作的时间。 (获取和释放无争议的锁定可能非常快。但不是 和设置bool一样快。在这种情况下,锁定正在进行 非常非常有争议,因为所有的线程都想要它的大部分 时间。)如果你按bool创建一个锁,那将是非常多的 锁 - 它可能需要更少的时间来构建表 一个线程而不是创建所有互斥锁。

当然,在C ++中(也许在Java中),你会想要使用 位图,而不是每个条目一个bool;表越大, 您可以处理的最大数量越大。 (bool sieve[INT_MAX];之类的东西几乎肯定会失败;你可能会得到 然而,离开unsigned char sieve[INT_MAX / 8 + 1];。)在此 如果你需要每个元素的互斥量,而不是每个条目(这将是一个 元素中的单个位)。鉴于每个互斥量都会占用一些 资源,你可能想把表分成离散的 块,每个块使用一个互斥锁,并使用嵌套循环:

int j = 0;
for ( int i = 0; i < numberOfBlocks; ++ i ) {
    scoped_lock( mutex_table[i] );
    while ( j < (i + 1) * bitsInBlock ) {
        //  ...
        j += step;
}

一旦这个工作,需要进行一些调整以确定 最佳块大小(但我觉得相当大)。

答案 4 :(得分:0)

你的老师不会喜欢这个,但我想有一种残酷的方法值得考虑。

让每个帖子都重复

find the next prime in the sieve
mark all multiples of this prime

独立于所有其他人而没有任何同步。当它找不到更多素数时,每个线程都会停止。

这是残酷的,因为几个线程可能偶然在同一个素数上工作,但最终的筛子将是正确的(检测到所有复合物),并且由于缺少同步,您可以重新获得重复工作的松散。 / p>