使用通过引用传递的参数(通过指针)优化 OpenCL 函数中的指针访问

Posted

技术标签:

【中文标题】使用通过引用传递的参数(通过指针)优化 OpenCL 函数中的指针访问【英文标题】:Optimization of pointer access in OpenCL functions with parameters passed by reference(via pointers) 【发布时间】:2017-10-24 23:07:11 【问题描述】:

我目前正在尝试优化我的 OpenCL 代码。我使用一些指针来返回值。创建存储在寄存器中的临时变量是否更有效(至少我猜)或者我可以直接访问指针并在整个函数中使用它们,因为它不会对性能产生任何影响,因为编译器无论如何都会优化它?

让我给你看一个简单的盒子射线相交的例子:

tmin 和 tmax 存储在临时变量中:

bool intersect(const Ray* ray, float3 boxmin, float3 boxmax, float* tmin, float* tmax)

  float3 invR = 1.0f / ray->dir;

  float t1 = (boxmin.x - ray->origin.x) * invR.x;
  float t2 = (boxmax.x - ray->origin.x) * invR.x;
  float t3 = (boxmin.y - ray->origin.y) * invR.y;
  float t4 = (boxmax.y - ray->origin.y) * invR.y;
  float t5 = (boxmin.z - ray->origin.z) * invR.z;
  float t6 = (boxmax.z - ray->origin.z) * invR.z;

  float tmin_ = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6));
  float tmax_ = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6));

  if (tmax_ < 0)
    return false;
  if (tmin_ > tmax_)
    return false;

  *tmax = tmax_;
  *tmin = tmin_;
  return true;

直接访问:

bool intersect(const Ray* ray, float3 boxmin, float3 boxmax, float* tmin, float* tmax)

  float3 invR = 1.0f / ray->dir;

  float t1 = (boxmin.x - ray->origin.x) * invR.x;
  float t2 = (boxmax.x - ray->origin.x) * invR.x;
  float t3 = (boxmin.y - ray->origin.y) * invR.y;
  float t4 = (boxmax.y - ray->origin.y) * invR.y;
  float t5 = (boxmin.z - ray->origin.z) * invR.z;
  float t6 = (boxmax.z - ray->origin.z) * invR.z;

  *tmin = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6));
  *tmax = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6));

  if (*tmax < 0)
    return false;
  if (*tmin > *tmax)
    return false;
  return true;

这不是最好的例子,因为只有三个取消引用的指针,在性能上可能没有差异,但它只是为了说明我的意思。 是否有知识渊博的人知道大多数 OpenCL 编译器在这种情况下会做什么? 了解 C/C++ 编译器在这种情况下会做什么也会很有趣。

【问题讨论】:

哪个更快?是的。一是更快。也许。简介两人。对每一项进行基准测试。查看两个版本产生的机器代码。简而言之:相互测试两者。 【参考方案1】:

取决于您传递的指针的内存位置(tmaxtmin)。

Private:编译器可能会将它们优化掉。 本地:编译器无法优化它们,除非编译器清楚指针始终由组中的单个工作项指向。 (即使它可能仍然无法做到)。在使用案例 2 的某些情况下,您可能会有所下降。 全局:编译器永远不会优化它们,因为它无法知道全局内存是否正在被另一个工作组甚至 CPU 端更改。在第二种情况下会有惩罚和额外的内存访问。

性能的最佳解决方案是第一个。您在私有空间中进行大部分检查,并且仅在需要时访问指向内存的指针。不用担心使用额外的变量,GPU 有足够的寄存器来处理它:

  float tmin_ = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6));
  float tmax_ = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6));

  if (tmax_ < 0)
    return false;
  if (tmin_ > tmax_)
    return false;

  *tmax = tmax_;
  *tmin = tmin_;

【讨论】:

【参考方案2】:

对于任何体面的编译器,这两个选项之间的性能不会有任何差异。大多数 OpenCL 实现都是基于 llvm 的,它绝对会优化诸如此类的临时文件。

也就是说,通常唯一容易判断的方法是对两个选项都计时。这始终是优化的证明,但使用基于 CPU 的编译器,人们通常也可以查看汇编输出。使用 OpenCL 更难做到这一点。

注意使用可以使用向量操作来使这段代码更加简洁。它是否有助于提高性能是另一回事。

您还可以返回一个我认为完全避免使用指针的结构。

【讨论】:

感谢您的回答。我是这么认为的,编译器真的很聪明。对编译器进行优化是可取的。也许我会开始一些测试,看看各种理论优化是否会有所作为。好吧,我只是从我用 C++ 制作的一个项目中复制了代码,确实可以使用向量运算来优化代码。我一定会这样做的。 真正要做的第一件事就是优化显式内存层次结构。 GPU 确实需要一种经验方法来优化性能——使用分析工具。 CPU 也可以,但很多人似乎都摆脱了 ad hoc 方法。

以上是关于使用通过引用传递的参数(通过指针)优化 OpenCL 函数中的指针访问的主要内容,如果未能解决你的问题,请参考以下文章

如何通过引用传递子字符串?

取消引用指针会复制它吗?

PHP是不是优化数组类型的函数参数,而不是通过引用显式传递,当它们没有被修改时?

小白学习C++ 教程八在C++指针传递引用和Const关键字

优化C++软件

优化C++软件