【问题标题】:Making C++ Pi approximation running in Paralell on GPU Nvidia 970M CUDA's [closed]使 C++ Pi 近似值在 GPU Nvidia 970M CUDA 上并行运行 [关闭]
【发布时间】:2019-02-11 03:43:18
【问题描述】:

我从用于 Pi 逼近的 Chudnovsky 公式中得到了这段代码,我想做得越来越快。但我对如何处理 GPU 没有经验或想法。如何让这段代码在 GPU Nvidea 970M 上运行?它在 C++ 上。有一些简单的库可以做到吗?使用我的处理器,它运行大约 3~4 秒...

#include <iostream>
#include <windows>
#include <iomanip>
#include <cmath>

double fac(double num) {
    double result = 1.0;
    for (double i=2.0; i<num; i++)
       result *= i;
    return result;
}

int main() {
    using namespace std;
    double pi=0.0;
    for (double k = 0.0; k < 10.0; k++) {
        pi += (pow(-1.0,k) * fac(6.0 * k) * (13591409.0 + (545140134.0 * k)))
            / (fac(3.0 * k) * pow(fac(k), 3.0) * pow(640320.0, 3.0 * k + 3.0/2.0));
    }
    pi *= 12.0;
    cout << setprecision(100000000) << 1.0 / pi << endl;
system("Pause");    
return 0;
}

【问题讨论】:

  • 要利用显卡上的多个核心,您需要将其拆分为可以委托给不同核心的部分;想想多线程。比如你可以把算法分成两个线程,一个线程计算k的偶数,另一个线程计算k的奇数。 main 程序可以在线程完成后将这两个加在一起。
  • 您可以通过保持 3 个 running 阶乘值来加速您的程序。每个阶乘值将乘以 k 的下一个值(而不是从 1.0 重新开始阶乘计算)。
  • 另一种优化是计算fac(k),放入一个临时变量中并乘以3次:double m = fac(k); double power3 = m * m * m;
  • 根据我的测试,这段代码中的所有时间都花在了cout &lt;&lt; setprecision(100000000)...语句中。用更合理的setprecision(40) 替换它(尽管double 最多只能给出10-15 位的精度),执行时间会下降到大约0(~1 毫秒)。因此,所提供的答案中提出的优化都没有产生任何明显的差异。
  • 为了好玩,请尝试运行以下代码:#include &lt;iostream&gt; #include &lt;iomanip&gt; using namespace std; int main() { double pi=1.0/3.0; cout &lt;&lt; setprecision(100000000) &lt;&lt; pi &lt;&lt; endl; return 0;} 记下输出以及执行时间。

标签: c++ cuda gpu nvidia pi


【解决方案1】:

在委派给多核 GPU 之前,我建议你先优化单核算法。

运行因子值

对于每个 k 值,您的阶乘函数始终从 2.0 开始。这有以下计算:

+----+-------------------+  
| 1! | 1                 |   
+----+-------------------+  
| 2! | 1 * 2             |  
+----+-------------------+  
| 3! | 1 * 2 * 3         |  
+----+-------------------+  
| 5! | 1 * 2 * 3 * 4 * 5 |   
+----+-------------------+  

这些迭代的这些时间将随着k 的值变大而累加。

阶乘可以迭代表示为:

+----+--------+   
| 1! | 1      |   
+----+--------+  
| 2! | 1! * 2 |  
+----+--------+  
| 3! | 2! * 3 |  
+----+--------+  
| 5! | 4! * 5 |   
+----+--------+  

换句话说,下一个阶乘使用前一个阶乘值并乘以k的值。

您的main 可能如下所示:

    int main()
    {
      double k_factorial = 1.0;
      //...
      for (/* ... */)
      {
        if (k > 1.0) k_factorial *= k;
        //...
                / (fac(3.0 * k) * pow(k_factorial, 3.0) * pow(640320.0, 3.0 * k + 3.0/2.0));
    //...
  }

由于您有 3 个不同的阶乘:fac(3.0 * k), fac(k),fac(6.0 * k),您可以使用 3 个不同的阶乘变量并在 for 循环内更新它们的值。

运行pow变量

与阶乘变量类似,您可以为 pow 函数设置运行值。
pow(-1.0, k) 可以替换为:

double pow_sign = 1.0; // pow(-1.0, 0)
//...
for (//...
{
  pow_sign *= -1.0;
  pi += (pow_sign * //...

此外,我们知道pow(x, 3)x * x * x 相同。进行替换:
pow(fac(k), 3.0) --> factorial_k * factorial_k * factorial_k

代数化简

您还可以通过代数方式简化您的赋值语句。 例如,制作一个包含以下列的表格:

+-----+--------------+--------------+  
|  k  | fac(6.0 * k) | fac(3.0 * k) |  
+-----+--------------+--------------+  
|  0  |              |              |  
+-----+--------------+--------------+  
|  1  |              |              |  
+-----+--------------+--------------+  

看看你是否注意到任何模式。
还要垂直重写分配,看看是否可以通过因式分解来简化。

利用 GPU 内核

将计算优化为运行或迭代方法后,您可以委托给多个内核。

第一步是弄清楚如何将工作分成可以并行运行的部分。

将您的计算分成三部分并运行(在一个核心上)。验证正确性。

下一步是研究“c++ 并行编程”以获取有关如何在您的平台上使用并行处理运行程序的说明,或者在互联网上搜索“c++ 使用 gpu 多核”。

您希望每个核心接收的工作量消耗的时间比设置核心的开销更多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-22
    • 2022-07-15
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多