【发布时间】:2012-01-28 18:07:58
【问题描述】:
我正在用 C 语言实现一个 MPI 程序,它在网格上执行 SOR(连续过度松弛)。在对它进行基准测试时,我遇到了一些非常出乎意料的事情,即地址运算符& 似乎很慢。这里无法展示完整的代码,也太长了,不过相关部分如下。
double maxdiff, diff;
do {
maxdiff = 0.0;
/* inner loops updating maxdiff a lot */
/* diff is used as a receive buffer here */
MPI_Allreduce(&maxdiff, &diff, 1, MPI_DOUBLE, MPI_MAX, my_comm);
maxdiff = diff;
} while(maxdiff > stopdiff);
在这里,stopdiff 是一个神奇的值。缓慢的行为出现在MPI_Allreduce() 操作中。奇怪的是,当仅在单个节点上运行时,该操作甚至非常慢,即使在这种情况下不需要通信。当我将操作注释掉时,一个节点上特定问题的运行时间从 290 秒减少到仅 225 秒。此外,当我使用其他虚假变量将操作替换为 MPI_Allreduce() 调用时,我也会得到 225 秒。所以看起来它是专门获取maxdiff 和diff 的地址,这导致了速度变慢。
我通过将两个额外的double 变量用作临时发送/接收缓冲区来更新程序,如下所示。
send_buf = maxdiff;
MPI_Allreduce(&send_buf, &recv_buf, 1, MPI_DOUBLE, MPI_MAX, my_comm);
maxdiff = recv_buf;
这也使程序在 225 秒而不是 290 秒内运行。我的问题是,很明显,这怎么可能?
我确实有一个怀疑:程序是使用优化级别 O3 的 gcc 编译的,所以我怀疑编译器正在做一些优化,这使得引用操作非常慢。例如,变量可能存储在 cpu 寄存器中,因为它们在循环中经常使用,因此,每当请求它们的地址时,它们必须被刷新回内存。但是,我似乎无法通过谷歌搜索找出什么样的优化可能会导致这个问题,我想确定这个问题。有人知道是什么原因造成的吗?
提前致谢!
我应该在这里添加一些其他重要信息。正在运行的特定问题非常糟糕地填满了内存。它使用 3GB 内存,节点总共有 4GB RAM。我还观察到,随着 RAM 被填满,较大的问题规模会变得更慢,因此 RAM 上的负载量似乎是问题的一个因素。另外,奇怪的是,当我在循环之后而不是在循环内添加一次MPI_Allreduce() 时,在程序的非优化版本中仍然存在减速,并且仍然一样糟糕。该程序不会以这种方式运行得更快。
按照下面的要求,这里是 gcc 程序集输出的一部分。不幸的是,我没有足够的组装经验来收集这个问题。这是添加了发送和接收缓冲区的版本,因此运行时间为 225 秒而不是 290 秒。
incl %r13d
cmpl $1, %r13d
jle .L394
movl 136(%rsp), %r9d
fldl 88(%rsp)
leaq 112(%rsp), %rsi
leaq 104(%rsp), %rdi
movl $100, %r8d
movl $11, %ecx
movl $1, %edx
fstpl 104(%rsp)
call MPI_Allreduce
fldl 112(%rsp)
incl 84(%rsp)
fstpl 40(%rsp)
movlpd 40(%rsp), %xmm3
ucomisd 96(%rsp), %xmm3
jbe .L415
movl 140(%rsp), %ebx
xorl %ebp, %ebp
jmp .L327
我认为这是程序中的相应部分,没有额外的发送和接收缓冲区,所以运行时间为 290 秒的版本。
incl %r13d
cmpl $1, %r13d
jle .L314
movl 120(%rsp), %r9d
leaq 96(%rsp), %rsi
leaq 88(%rsp), %rdi
movl $100, %r8d
movl $11, %ecx
movl $1, %edx
call MPI_Allreduce
movlpd 96(%rsp), %xmm3
incl 76(%rsp)
ucomisd 80(%rsp), %xmm3
movsd %xmm3, 88(%rsp)
jbe .L381
movl 124(%rsp), %ebx
jmp .L204
【问题讨论】:
-
请注意,“
&”是“address-of”运算符,而不是“reference”运算符。 -
我认为“address-of”和“reference”运算符是一回事。与“取消引用”运算符
*相反。不过我可能是错的。 -
注意:您在每次迭代时将 maxdiff 设置为 0.0。我希望在
do {行之前看到maxdiff = 0.0。 -
maxdiff和diff定义在哪里?它们是局部变量还是全局变量?如果它们是全局的,如果将它们设为静态会发生什么? -
函数调用的时钟计数可能在 10 到 100 次之间。在两个版本中。 inside 函数会发生什么更昂贵(向 all slaves 发送/接收消息)加上等到 all 已回答。我建议使用 -pg 进行编译(如果您使用的是 gcc)并在其上运行 gprof。
标签: c performance reference mpi operator-keyword