OpenCL:为啥这两种情况的性能差异如此之大?
Posted
技术标签:
【中文标题】OpenCL:为啥这两种情况的性能差异如此之大?【英文标题】:OpenCL: Why does the performance differ so greatly between these two cases?OpenCL:为什么这两种情况的性能差异如此之大? 【发布时间】:2011-12-03 03:09:36 【问题描述】:这是我正在处理的 OpenCL 内核中的两段代码;它们显示出截然不同的运行时间。
代码比较复杂,我把它简化了。
这个版本运行不到一秒:
for (int ii=0; ii<someNumber;ii++)
for (int jj=0; ii<someNumber2;jj++)
value1 = value2 + value3;
value1 = value1 * someFunction(a,b,c);
double nothing = value1;
这个版本大约需要 38 秒才能运行:
for (int ii=0; ii<someNumber;ii++)
for (int jj=0; ii<someNumber2;jj++)
value1 = value2 + value3;
value1 = value1 * someFunction(a,b,c);
double nothing = value1;
正如我所说,代码比这要复杂一些(循环中还有很多其他事情),但变量“nothing”确实会从大括号之前立即移动到大括号之后。
我对 OpenCL 很陌生,我无法弄清楚发生了什么,更不用说如何解决它了。不用说,缓慢的情况实际上是我在实施中所需要的。我试过搞乱地址空间(这里的所有变量都在 __private 中)。
我只能想象,由于某种原因,当大括号关闭时,GPU 会将变量“value1”推到较慢的内存中。这是一个可能的解释吗?我能做什么?
提前致谢!
更新:这也在不到一秒的时间内运行:(但如果取消注释任何一行,它就会恢复到极度缓慢)。这没有对循环进行任何其他更改,并且 value1 仍然声明在与以前相同的位置。
for (int ii=0; ii<someNumber;ii++)
for (int jj=0; ii<someNumber2;jj++)
// value1 = value2 + value3;
// value1 = value1 * someFunction(a,b,c);
double nothing = value1;
更新 2:代码实际上嵌套在另一个循环中,如下所示,value1
的声明:
double value1=0;
for (int kk=0; kk<someNumber3;kk++)
for (int ii=0; ii<someNumber;ii++)
for (int jj=0; ii<someNumber2;jj++)
value1 = value2 + value3;
value1 = value1 * someFunction(a,b,c);
double nothing = value1;
移动到声明 value1
的位置也让我们回到快速案例:
for (int kk=0; kk<someNumber3;kk++)
double value1=0;
for (int ii=0; ii<someNumber;ii++)
for (int jj=0; ii<someNumber2;jj++)
value1 = value2 + value3;
value1 = value1 * someFunction(a,b,c);
double nothing = value1;
OpenCL 似乎是一门非常棘手的艺术!我仍然不明白发生了什么,但至少我现在知道如何解决它!
【问题讨论】:
这很奇怪。您确定需要使用较慢的版本吗?从这些 sn-ps 来看,它们在功能上是相同的。 感谢您的回复。是的,我敢肯定,但你是对的,我给出的示例在功能上是相同的。内大括号中的代码应该有一个 +=。 根据这些代码 sn-ps,我看不出第二个应该变慢的任何原因。我猜想移动分配一定会在某处产生副作用,例如增加分支(一个工作单元执行if
,下一个执行else
),这确实会减慢GPU。
另外,我知道你说过所有的变量都是__private
,但是如果你完全与全局内存同步,你可能会破坏对内存的合并访问。优化 OpenCL 内存访问可能很棘手:***.com/questions/3841877/… 只是抛出一些想法。 :)
这给了我很多思考。我已经用一些可能会带来更多启示的东西更新了我的问题。该代码没有if
s。我怀疑这将成为一个合并问题。
【参考方案1】:
您使用的是什么实现?我希望“双重无= value1;”在任何情况下都会被任何合理的编译器作为死代码消除。
【讨论】:
我想我找到了问题所在,感谢您的帖子。在案例 1(我的问题中的第一个框)中,我认为编译器通过“消除死代码”来优化内部循环。在情况 2 中,它意识到变量value1
需要在内循环之外,所以它运行它。函数someFunction(a,b,c)
非常慢,因此会导致速度变慢。仅供参考,实施是 AMD 的 Linux SDK。感谢大家的帮助!
您是说因为 value1 未使用,编译器优化了对 someFunction 的调用。如何确定 someFunction 没有副作用?
因为“无”未使用。我不是在谈论 value1。
@vocaro 我不确定,但这是我能找到的最佳解释。它实际上是一个非常简单的函数,它只是对内存造成了很大的影响(我自己的稀疏矩阵访问实现)。我对编译器一无所知,但由于它实际上只是一个复杂的取消引用,也许编译器会注意到?当然,它可能是完全不同的东西,但我不知道是什么!【参考方案2】:
第一种情况只是一个循环(带有编译器优化) 但第二个是带有嵌套循环的循环。那是个大问题。大量检查全局/局部变量。(确定它们是私有的?你在内核中声明了所有这些?)
我建议您在开始循环之前将其保存为私有变量(somenumber 和 somenumber2)。因为这样你每次都会检查私人数据。 作为个人经验,用作 OpenCL 循环检查用例的每个 var 都必须是私有的。 这可以节省高达 80% 的全局内存访问。 (特别是如果循环很短或很简单)
例如,这应该可以快速运行:
int c_somenumber = someNumber;
for (int ii=0; ii<c_someNumber;ii++)
int c_somenumber2 = someNumber2;
for (int jj=0; ii<c_someNumber2;jj++)
value1 = value2 + value3;
value1 = value1 * someFunction(a,b,c);
double nothing = value1;
编辑: 此外,value1 应该缓存在私有内存中。 (就像您在上次编辑中所做的那样)
【讨论】:
是的,所有内容都保存为私有。请参阅我对问题的解释作为对其他答案的评论!以上是关于OpenCL:为啥这两种情况的性能差异如此之大?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Google Analytics 和 BigQuery 之间的独特事件差异如此之大?