【问题标题】:Parallel computing of array elements on GPUGPU上数组元素的并行计算
【发布时间】:2013-10-09 21:36:16
【问题描述】:

我正在使用 C# 创建一个数据库。问题是我有接近 400 万个数据点,完成数据库需要很长时间(可能几个月)。代码看起来像这样。

int[,,,] Result1=new int[10,10,10,10];
int[,,,] Result2=new int[10,10,10,10];
int[,,,] Result3=new int[10,10,10,10];
int[,,,] Result4=new int[10,10,10,10];

for (int i=0;i<10;i++)
{
  for (int j=0;j<10;j++)
  {
    for (int k=0;k<10;k++)
    {
      for (int l=0;l<10;l++)
      {
        Result1[i,j,k,l]=myFunction1(i,j,k,l);
        Result2[i,j,k,l]=myFunction2(i,j,k,l);
        Result3[i,j,k,l]=myFunction3(i,j,k,l);
        Result4[i,j,k,l]=myFunction4(i,j,k,l);
      }
    }
  }
}

Result 数组的所有元素是完全相互独立的。我的电脑有 8 个内核,我为每个 myFunction 方法创建了一个线程,但整个过程仍然需要很多时间,因为有很多情况。我想知道是否有任何方法可以在 GPU 而不是 CPU 上运行它。我以前没有做过,我不知道它会如何工作。如果有人可以帮助我,我将不胜感激。

【问题讨论】:

  • 您是否在表中插入行?使用 Sql 服务器?尝试批量插入;您的瓶颈可能是数据库,而不是 c# 程序。

标签: c# parallel-processing gpu


【解决方案1】:

是的,这些场景的直觉是使用多线程/甚至 GPU 来加速。但重要的是要弄清楚该场景是否适合并行计算。

正如您所建议的,这些数据集彼此独立,但是当您在 8 核上运行多线程版本时,没有明显的改进:这表明潜在的问题:您关于数据集独立性的陈述是错误的,或者您的实现多线程代码的未优化。我建议您首先调整您的代码以查看改进,然后寻找将其移植到 GPU 平台的方法。

或者您可以查看适用于并行线程/GPU 内核的 OPENCL。 但重要的是弄清楚你的问题是否真的适合并行计算

【讨论】:

  • 感谢您的评论。我看到改进购买在多个内核上运行代码。事实上,在 8 核上运行代码可以将速度提高 8 倍(因为元素是独立的),但鉴于数据点如此之多,创建数据库仍然需要很多时间。我需要将速度提高超过 8 倍。我一直在研究 CUDA 和 CUDAfy,但我还没有弄清楚如何去做。
【解决方案2】:

您可以考虑使用 C++ AMP 重写应用程序的这一部分,并从您的 .NET 代码中调用它。欲了解更多信息,请参阅http://blogs.msdn.com/b/nativeconcurrency/archive/2012/08/30/learn-c-amp.aspx

但是,在您显示的代码中,有 40,000 个数据点,而不是 4,000,000 个。

一个月大约有 260 万秒。对于 40,000 个数据点,每个数据点可以为您提供超过一分钟的时间。 (即使你确实有 400 万个数据点,每个数据点仍然会超过半秒。)我不知道这些函数在做什么,但我会惊讶于需要运行这么长时间的东西非常适合在 GPU 上运行。

也许您需要重新审视这些函数中使用的算法,看看它们是否可以优化。您甚至可能不得不重新考虑您的想法,以独立于其他数据点来计算每个数据点。如果您已经知道其他一些结果,您确定不能更有效地计算一个结果吗?

更新:

我所说的最后一句话的意思是,可能会有重复的计算。例如,如果myFunction1 完成的部分计算仅依赖于前两个参数,您可以将代码重构如下:

for (int i = 0; i < 10; i++)
{
  for (int j = 0; j < 10; j++)
  {
    var commonPartValue = commonPart(i, j);

    for (int k = 0; k < 10; k++)
    {
      for (int l = 0; l < 10; l++)
      {
        Result1[i, j, k, l] = myFunction1b(i, j, k, l, commonPartValue);
      }
    }
  }
}

最终的结果是您计算了这个“公共部分”一次,而您过去要计算一百次。

另一种情况是,您可以使用先前的结果更有效地计算结果,而不是必须从头开始计算。例如,n² 可以很容易地计算为 n * n,但如果您已经知道 (n - 1)²,则 n² = (n - 1)² + 2 * n - 1。在整数算术中,这意味着您替换 a乘以移位和递减,速度更快。

现在,我并不是说您的问题像这些示例一样简单,而是说您应该首先寻找这些优化,然后再寻找更好的编译器或不同的硬件。

另外,作为旁注:我假设您将计算的内容存储在磁盘上,而不是存储在 RAM 中的数组中。我可不想等一个月等结果出来,然后就停电了……

【讨论】:

  • 我上面写的代码只是一个简化的例子。我使用的函数(myFunctions)相当复杂,但我以最佳优化的方式编写了它们。这里的问题是案例的剪切数量而不是函数本身。
  • 好吧,如果你确定这一点,那么看看 C++ AMP。
【解决方案3】:

我不认为您的代码示例使用了所有八个内核 - 只有一个。 以下应使用全部 8 个:

 private void Para()
    {
        int[, , ,] Result1 = new int[10, 10, 10, 10];
        int[, , ,] Result2 = new int[10, 10, 10, 10];
        int[, , ,] Result3 = new int[10, 10, 10, 10];
        int[, , ,] Result4 = new int[10, 10, 10, 10];

        Parallel.For(0L, 10, i =>
        {
            Parallel.For(0L, 10, j =>
            {
                Parallel.For(0L, 10, k =>
                {
                    Parallel.For(0L, 10, l =>
                    {
                        Result1[i, j, k, l] = myFunction1(i, j, k, l);
                        Result2[i, j, k, l] = myFunction2(i, j, k, l);
                        Result3[i, j, k, l] = myFunction3(i, j, k, l);
                        Result4[i, j, k, l] = myFunction4(i, j, k, l);
                    });
                });
            });
        });
    }

如果这还不够,have a look at Cudafy 应该比用 C++ 重写所有复杂的函数更轻松。

【讨论】:

    猜你喜欢
    • 2018-08-26
    • 2020-07-10
    • 1970-01-01
    • 2011-05-20
    • 2018-09-02
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2021-10-10
    相关资源
    最近更新 更多