【问题标题】:OpenCL/GL Interop slow on nvidia/win but fast on linux?OpenCL/GL 互操作在 nvidia/win 上慢,但在 linux 上快?
【发布时间】:2013-08-28 15:40:32
【问题描述】:

以下问题已在 nvidia 的新驱动程序版本 331.xx 中得到修复,目前作为测试版驱动程序提供。

感谢您的所有 cmets!

我有一个多平台应用程序,它在 OpenGL 纹理上执行许多片段操作和 gpgpu 内容。该应用程序大量使用 GL/CL 互操作,每个纹理都可以绑定到 OpenCL 图像并使用 CL 内核进行操作。

问题是,应用程序在 AMD 卡(Linux 和 Windows)上运行速度很快。在 NVIDIA 卡上,它在 Linux 上运行速度很快,但在 Windows 7 上运行速度很慢。问题似乎是 enqueueAcquireGLObjects 和 enqueueReleaseGLObjects。我创建了一个最小的示例,通过简单地展示了糟糕的性能:

  1. 创建 2 个 OpenGL 纹理(1600x1200 像素,RGBA 浮点数)
  2. 创建 2 个 OpenCL 图像,共享 2 个纹理
  3. 重复(50 次)获取、释放、完成

结果(执行获取、发布、完成的平均时间)

  • AMD HD 6980,Linux:
  • AMD HD 6980,Win7:
  • NVIDIA GTX590,Linux:
  • NVIDIA GTX590、Win7:16.0 毫秒

我尝试了几个来自 nvidia 的不同驱动程序,从旧的 295.73 到当前的 beta 驱动程序 326.80,都表现出相同的行为。

我现在的问题是,nvidia 驱动程序严重损坏还是我在这里做错了什么?该代码在 linux 上运行速度很快,因此它不会是 nvidia 对 OpenCL 支持的普遍问题。该代码在 AMD+Win 上运行速度很快,因此我的代码没有针对 Windows 进行优化不会是问题。例如,通过将 cl 图像更改为只读/只写来优化代码是没有意义的,因为性能损失几乎是 30 倍!

您可以在下面找到我的测试用例的相关代码,我也可以提供完整的源代码。

上下文创建的相关代码

{ // initialize GLEW
  glewInit();
}

{ // initialize CL Context, sharing GL Contet
  std::vector<cl::Platform> platforms;
  cl::Platform::get(&platforms);
  cl_context_properties cps[] = {
             CL_GL_CONTEXT_KHR,(cl_context_properties)wglGetCurrentContext(),
             CL_WGL_HDC_KHR,(cl_context_properties)wglGetCurrentDC(),
             CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0]()),
             0};
  std::vector<cl::Device> devices;
  platforms[0].getDevices((cl_device_type)CL_DEVICE_TYPE_GPU, &devices);
  context_ = new cl::Context(devices, cps, NULL, this);
  queue_ = new cl::CommandQueue(*context_, devices[0]);
}

创建纹理和共享 CL 图像的相关代码

width_ = 1600;
height_ = 1200;

float *data = new float[ 1600*1200*4 ];

textures_.resize(2);
glGenTextures(2, textures_.data());

for (int i=0;i<2;i++) {
  glBindTexture(GL_TEXTURE_2D, textures_[i]);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  // "data" pointer holds random/uninitialized data, do not care in this example
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, width_,height_, 0, GL_RGBA, GL_FLOAT, data);
}

delete data;
{ // create shared CL Images
#ifdef CL_VERSION_1_2
  clImages_.push_back(cl::ImageGL(*context_, CL_MEM_READ_WRITE, GL_TEXTURE_2D, 0, textures_[0]));
  clImages_.push_back(cl::ImageGL(*context_, CL_MEM_READ_WRITE, GL_TEXTURE_2D, 0, textures_[1]));
#else
  clImages_.push_back(cl::Image2DGL(*context_, CL_MEM_READ_WRITE, GL_TEXTURE_2D, 0, textures_[0]));
  clImages_.push_back(cl::Image2DGL(*context_, CL_MEM_READ_WRITE, GL_TEXTURE_2D, 0, textures_[1]));
#endif
}

一个获取、发布、完成周期的相关代码

try {
  queue_->enqueueAcquireGLObjects( &clImages_ );
  queue_->enqueueReleaseGLObjects( &clImages_ );
  queue_->finish();
} catch (cl::Error &e) {
  std::cout << e.what() << std::endl;
}

【问题讨论】:

  • 我可以把它归结为更多,只是 enqueueAcquireGLObjects,enqueueReleaseGLObjects 在 Win7 上会导致 16ms 的时间。相应地改变了问题
  • 可能 GL 设备和 CL 设备不一样,导致复制开销?只是猜测......
  • 我假设如果你使用 OpenGl,你会在屏幕上显示一些东西......对于你的测试,你是否还在显示一些东西?
  • 为了测试,我根本没有渲染任何东西,GL 上下文创建和纹理分配是对 GL 的唯一调用。 GL 和 CL 设备可能不同,因为 GTX590 是双芯片卡,是的。但我尝试了从 460 到 680 的 GeForce 卡。
  • 该问题已在最新的 beta 驱动程序 331.xx 中得到修复!感谢您的所有 cmets!

标签: performance opengl opencl nvidia


【解决方案1】:

我会假设由于您使用的是 OpenGL,因此在 OCL 计算之后您会在屏幕上显示一些内容。

因此,基于这个假设,我的第一个想法是检查 NVIDIA 控制面板是否启用了 VSync,如果是,则禁用它并重新测试。

据我所知,AMD 和 NVIDIA 的默认 vsync 选项是不同的;这将解释两个 GPU 之间的差异。

以防万一,这里有一个post,它解释了垂直同步如何减慢渲染速度。

【讨论】:

  • 在处理/渲染之前获取/释放 CL/GL 对象的一致性。所以,看起来渲染根本没有参与。我建议触发隐藏的 I/O 以保持一致性。
  • 正如我所说,它基于 OP 在屏幕上显示某些内容的假设,即在屏幕上呈现某些内容。我曾经有一个小型视频处理程序在强大的桌面 NVIDIA GPU 上仅以 60 fps 运行,但在移动 AMD 上它达到了 100 fps 以上。当我在使用 NVIDIA 的计算机上禁用 vsync 时,我达到了超过 500 fps。顺便说一句,我的屏幕刷新率为 60Hz...
  • 在我的大型应用程序中,我在屏幕上显示了一些纹理,这是正确的,但在这个测试中根本没有渲染。我知道 vsync 的问题,因为我在其他时候遇到了一些问题。在 Windows 上,我在驱动程序控制面板上尝试了所有可能的设置,但没有帮助。我发现我什至可以扩大减速:如果我获得一个纹理 = 8 毫秒,两个纹理 16 毫秒,10 个纹理 = 80 毫秒。 @DarkZeros 隐藏的 i/o 听起来很有趣这是什么意思?
  • @user2725937 这意味着当您调用acquire()方法时,驱动程序将复制/锁定OpenGL,以保持一致性。类似于 clEnqueueMapBuffer() 发生的事情。如果 GL 和 CL 位于不同的设备或不同的驱动程序等中。此调用“可能”会触发 GL->CL 或 CL->GL 缓冲区的一些 IO 复制。与在不同设备中运行 2 个内核时发生的情况相同,其中一个使用另一个结果,存在一个隐藏的 IO 副本会减慢进程。
  • 我向 nvidia 报告了这个问题,它确实在他们的新驱动程序版本 331.xx 中得到了修复,目前作为 beta 驱动程序提供。
猜你喜欢
  • 2013-10-12
  • 2015-06-02
  • 2011-09-01
  • 1970-01-01
  • 2013-02-17
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 2013-08-18
相关资源
最近更新 更多