【问题标题】:Parallelizing Sieve of Eratosthenes Algorithm for finding Prime Number寻找素数的并行埃拉托色尼筛法
【发布时间】:2012-02-16 07:28:51
【问题描述】:

以两种方式并行化 Eratosthenes 筛法

  1. 使用 Java 和
  2. 将 C/C++ 与 Pthread 结合使用

找到 2 核和 4 核 CPU 的 THRESHOLD 的最佳值。

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

【问题讨论】:

  • 是作业吗?你尝试了什么?
  • 你做了什么?你读过一些东西吗?试了一些样品?
  • 我认为主要挑战之一是有效地同时标记素数的倍数(或者,提示,以某种方式避免同时访问同一位置)。例如,3*5 就是 5*3。
  • @PeterG.:我对此表示怀疑,因为串行算法同时迭代 3*5 和 5*3。
  • “找到 2 核和 4 核 CPU 的最佳阈值”:这是什么???

标签: java c++ multithreading algorithm


【解决方案1】:

请注意,使用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 作为素数。

【讨论】:

  • 真正的问题是访问表。您将需要某种同步,以便两个线程不会同时访问同一个单词。
  • @JamesKanze 在并行步骤中从两个线程同时访问表没有问题,因为所有表访问都是table[i] = false。此阶段不依赖于 table[i] 的先前值 [或任何 jtable[j]],因此此处不需要同步。 [在最坏的情况下,您将有两个线程将相同的元素设置为 false,这里没有数据竞争]
  • @amt 您正在从两个不同的线程修改一个对象,除非有同步,否则这是未定义的行为。现在,如果您实际上做了一些愚蠢的事情,例如每个条目使用一个变量,那么未定义行为的表现很可能是代码可以工作,但它仍然是未定义行为(至少根据 Posix 和 C++11)。当然,在任何实际使用中,您都会执行类似 sieve[i >> 3] |= 1 << (i & 7) 的操作,它确实有依赖关系。
【解决方案2】:

初筛占用大量空间。

如果每个数字 1 位,您需要 1 千兆位 (2ˇ30) 来存储 N = ~10ˇ9 的筛子。和 1 太比特 (2ˇ40) 来存储 N = ~10ˇ12 的筛子,即 2ˇ37 字节 = 2ˇ7 GB = 128GB。 如果只存储奇数,16个数字/B,N=10ˇ9需要64GB。使用 16GB 的笔记本电脑,您将被限制为 N 2ˇ476 (~10ˇ159) 倍的内存。

所以内存使用是主要问题。

毕竟,即使是快速 RAM 也比 CPU 的 L1 缓存慢数百倍,因此您希望将数据调整到 32KB 或高达 256KB,而不是数以亿计的 TB。

由于筛子是随机访问的,它不会自动加载到缓存中,您会得到连续的缓存未命中。您需要逐块完成整个工作的数字范围。虽然块大小是您最快的内存(CPU 或 GPU 的 L1 L2)。

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

您将获得在当前垃圾中取消标记复合材料所需的素数列表。如果已经筛选大数,假设 10ˇ30 到 10ˇ30+10ˇ8,则 100M 数字的范围内有大约 1M 素数在列表中等待,只是为了取消标记它们的复合数,每个这种大小的素数需要(我猜)大约 128B存储,因此您需要 128MB 的空间。幸运的是,这个列表是按顺序访问的,并且在需要时它可能已经在缓存中。但无论如何,您将需要无数字节来存储下一个筛子的素数列表。

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

列表中的每个素数在最快的内存中取消标记筛子中的 1 位,基本上随机访问它。 GPU不是逐位写入,而是每次操作写入一个la 128B,它将是1024位。当 1 个线程尝试取消标记 1 位时,另一个线程取消标记另一个位,而第一个线程的位将再次具有值 1。栅栏/屏障/锁定内存将停止所有线程,尽管有很多线程在运行,但速度不会增加。

所以线程不应该共享筛子。需要共享筛选素数列表,以便每个线程都有自己的一块筛子,并使用相同的素数进行取消标记。但是在取消标记之后,他们需要将该素数“调度”到共享素数列表中,以用于其未来的筛分,这将在数百万次筛分后执行,同时其他线程正在更改同一列表。几乎又卡住了。

在减慢速度的同时使其并行非常容易。通常,在每个线程中重新计算某些东西会比通过 RAM、PCIe、SSD、GBit-net 等慢速总线询问它更快……但如果你只有一些线程,这是可能的,而且不是很复杂。

【讨论】:

  • 请注意,您应该按两次 ENTER 来创建新段落。我在这里为你修好了:)
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    首先,并行化是否会影响表的创建,或者 简单地使用它来确定一个数字是否是素数。在一个 真正的应用程序,我会做第一个离线,所以表格会出现 在最终的 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;
    }
    

    一旦这工作正常进行,就需要进行一些调整来确定 最佳块大小(但我猜相当大)。

    【讨论】:

      【解决方案5】:

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

      让每个线程重复

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

      独立于所有其他并且没有任何同步。每个线程都在找不到更多素数时停止。

      这是残酷的,因为多个线程可能会意外地在同一个素数上工作,但最终的筛子将是正确的(检测到所有复合材料),并且您在重复工作方面丢失的内容可能会因缺乏同步而重新获得。

      【讨论】:

        猜你喜欢
        • 2011-04-25
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 2019-07-17
        • 2011-12-16
        • 2017-11-14
        相关资源
        最近更新 更多