【问题标题】:OpenMP Parallelizing not performing as expectedOpenMP 并行化未按预期执行
【发布时间】:2021-06-07 10:30:01
【问题描述】:

我的代码如下所示:

void SimulationStep ( float *In, float *Out, float L, int N)
{
   
  Out[0] = In[0] - 2.0f*L*In[0] + L*In[N-1] + L*In[1];
    for (int x=1; x<N-1; x++)
    {
      Out[x] = In[x] - 2.0f*L*In[x] + L*In[x-1] + L*In[x+1]; 
    }
  Out[N-1] = In[N-1] - 2.0f*L*In[N-1] + L*In[N-2] + L*In[0];
}

我正在尝试并行化它。我已经尝试了很多东西,这就是一个例子:

void SimulationStep ( float *In, float *Out, float L, int N)
{
   
  Out[0] = In[0] - 2.0f*L*In[0] + L*In[N-1] + L*In[1];
    #pragma omp parallel for 
    for (int x=1; x<N-1; x++)
    {
      Out[x] = In[x] - 2.0f*L*In[x] + L*In[x-1] + L*In[x+1]; 
    }
  Out[N-1] = In[N-1] - 2.0f*L*In[N-1] + L*In[N-2] + L*In[0];
}

我应用的更改只增加了 0.5 秒,从 14 秒到 13.5 秒,所以我怀疑代码确实没有并行化。我认为这可能是内存受限的瓶颈,所以我不知道该怎么办。先感谢您。 Ps:我正在用 gcc/9.2.0 和 -03 -fopenmp 编译。

【问题讨论】:

  • 您可以通过例如在循环内打印线程 ID (omp_get_thread_num()) 轻松找出它是否并行运行。由于内存瓶颈(内存受限),您可能是正确的。 N 有多大?
  • N= 10000000。如果我打印线程 ID,我可以看到从 0 到 7 的数字范围。(处理器有 4 核 8 线程)。所以并行化正在发生。有什么改进可以减少内存瓶颈吗?
  • 有很多方法,但据我所知,没有什么简单的。当您将线程数减少到 4 个甚至 2 个(例如使用 num_threads() 子句)时会发生什么?
  • 几乎没有什么变化。十分之一上升十分之一下降。可能是 CPU 噪音。
  • 我玩过这个代码示例,也无法通过并行化获得很大的加速。但我对你运行的速度有多慢感到有点惊讶。在我的 PC(i5-2500 CPU @ 3.30GHz,4 核,4 线程,10 岁)上,使用给定的N 的每个步骤只需要大约 7 毫秒。对您来说,它真的慢了 2000 倍,还是您正在测量对该函数的数千次调用?还是你把你的单位搞混了?

标签: c gcc parallel-processing openmp


【解决方案1】:

@Black_Alistar 暗指Amdahl's Law,加速量受限于可并行化的可执行文件的比例。看起来您的应用程序大部分时间都在模拟循环之外。

当我看到执行时间的微小改进时,我立即认为您的应用程序是 I/O 受限的:

  1. 如果您在模拟中存储中间步骤,那么您的应用程序可能会受到 I/O 限制而不是 CPU 限制。因此,使用任何并行算法几乎没有区别;
  2. 读取或写入缓慢可能是因为您一次处理一项数据;
  3. 在使用无缓冲 I/O 时,不要以小块的形式读取或写入。

【讨论】:

  • 我认为主要问题是它必须读取的数据量,但我不知道如何减少它。谢谢你的回答!
  • @Black_Alistar,谢谢。如果您可以点击此答案左上角的“这很有用”箭头或将其标记为“答案”,那就太好了。听起来您需要进行一些分析才能找到应用程序中的瓶颈,这超出了您的问题范围。也许您可以发布有关您的问题的另一个问题并在此处放置链接,以便我们都可以关注您的进度。祝你好运。
  • 很遗憾我不能。它说我需要 15 个声望点,这是我的第一篇文章。我只有13分。当我有 15 分时,我会回来投票。我不会忘记的。
【解决方案2】:

所以我不知道该怎么办

你必须测量。

我只做了一个简单的 for 循环来填充一个数组,这需要一半的时间。我用 10 个 Mio 浮点数制作了两个全局数组。

比较:

$ cc simst.c  
$ time ./a.out 
Fill  39577us
Simu  42061us

real    0m0.089s
user    0m0.064s
sys     0m0.025s

-O3:

$ cc simst.c -O3 
$ time ./a.out 
Fill  23295us
Simu  14735us

real    0m0.044s
user    0m0.017s
sys     0m0.028s

现在填充时间相对要长得多。


OpenMP:

$ cc simst.c -O3 -fopenmp
$ time ./a.out 
Fill  23044us
Simu   6345us

real    0m0.036s
user    0m0.031s
sys     0m0.035s

并行化循环的速度是原来的两倍多,但总体效果并不大。


加上特定于拱门的开关:

$ cc simst.c -O3 -march=skylake -fopenmp

$ time ./a.out 
Fill  16781us
Simu   6052us

real    0m0.030s
user    0m0.023s
sys     0m0.036s

所以 OpenMP 让函数快了 2-3 倍,但并没有真正体现在整体时间上。


时间安排是这样的:

...
clock_gettime(clock_id, &ts1);
dt.tv_nsec = ts1.tv_nsec - ts.tv_nsec;
printf("Fill %6ldus\n", dt.tv_nsec/1000);

SimulationStep(in,  out, 50.0, LEN);

clock_gettime(clock_id, &ts2);
dt.tv_nsec = ts2.tv_nsec - ts1.tv_nsec;
printf("Simu %6ldus\n", dt.tv_nsec/1000);

我有一个密码...

你肯定有更多代码

【讨论】:

  • 感谢您的建议。我会试试看。
猜你喜欢
  • 2019-07-30
  • 1970-01-01
  • 2013-08-20
  • 2017-12-15
  • 2011-02-25
  • 2012-11-23
  • 2014-10-20
  • 2015-09-17
  • 2015-07-22
相关资源
最近更新 更多