【问题标题】:Why does clGetPlatformInfo get called in every clEnqueue function?为什么在每个 clEnqueue 函数中都会调用 clGetPlatformInfo?
【发布时间】:2020-08-23 02:32:21
【问题描述】:

我们正在分析在主机和设备上的 NVidia GPU 上运行的 OpenCL 应用程序。我们惊讶地发现(基于 gperftools)主机在 clGetPlatformInfo 上花费了 44% 的时间,这个方法在我们自己的代码中只调用了一次。它由clEnqueueCopyBuffer_hidclEnqueueWriteBuffer_hidclEnqueueNDRangeKernel_hid 调用(可能还有所有其他clEnqueue 方法,但它们在我们的代码中很少被调用)。由于这占用了我们的主机时间,而且我们现在似乎受到主机速度的限制,我需要知道是否有办法消除这些额外的调用。

为什么每次 OpenCL 调用都会调用它? (大概是可以存储在上下文中的静态信息?)我们是否可能错误地初始化了上下文?

编辑:我被要求提供 MWE:

#include <CL/opencl.h>

#include <vector>
using namespace std;


int main ()
{
    cl_uint numPlatforms;
    clGetPlatformIDs (0, nullptr, &numPlatforms);

    vector<cl_platform_id> platformIdArray (numPlatforms);
    clGetPlatformIDs (numPlatforms, platformIdArray.data (), nullptr);

    // Assume the NVidia GPU is the first platform
    cl_platform_id platformId = platformIdArray[0];

    cl_uint numDevices;
    clGetDeviceIDs (platformId, CL_DEVICE_TYPE_GPU, 0, nullptr, &numDevices);

    vector<cl_device_id> deviceArray (numDevices);
    clGetDeviceIDs (platformId, CL_DEVICE_TYPE_GPU, numDevices, deviceArray.data (), nullptr);

    // Assume the NVidia GPU is the first device
    cl_device_id deviceId = deviceArray[0];

    cl_context context = clCreateContext (
        nullptr,
        1,
        &deviceId,
        nullptr,
        nullptr,
        nullptr);

    cl_command_queue commandQueue = clCreateCommandQueue (context, deviceId, {}, nullptr);

    cl_mem mem = clCreateBuffer (context, CL_MEM_READ_WRITE, sizeof(cl_int),
                                 nullptr, nullptr);

    cl_int i = 0;

    while (true)
    {
        clEnqueueWriteBuffer (
            commandQueue,
            mem,
            CL_TRUE,
            0,
            sizeof (i),
            &i,
            0,
            nullptr,
            nullptr);

        ++i;
    }
}

此 MWE 在几秒钟内生成以下配置文件。请注意,99% 的时间都花在了 clGetPlatformInfo 中。

【问题讨论】:

    标签: performance profiling opencl


    【解决方案1】:

    我们发现了问题:gproftools 很难给出正确的回溯。代码实际上并没有像 gperftools 所说的那样调用 clGetPlatformInfo 数千次。根据在 Khronos 论坛上与 bashbaug 的对话:

    当我使用我们的 GPU 驱动程序使用 gperftools 运行测试时,我发现大部分时间都归因于 GTPin_Init,正如您所提到的。我认为这是因为 OpenCL ICD 必须导出很少的符号,因为对大多数 OpenCL API 的调用都是通过 ICD 调度表进行的。

    我们使用他建议的分析工具(OpenCL 拦截层,位于https://github.com/intel/opencl-intercept-layer)让我们更好地了解内核的运行时特性,并帮助我们找到一些内存泄漏。实际上导致速度变慢的是内存泄漏 --- 如果内核以高引用计数作为参数传递给内核,它们似乎需要很长时间才能启动。

    您可以在此处的 Khronos 论坛上找到完整的对话:https://community.khronos.org/t/why-does-clgetplatforminfo-get-called-in-every-clenqueue-function/105756/5

    【讨论】:

      【解决方案2】:

      尝试将NULL 作为第一个参数传递给clCreateContext。设备 ID 已被传递,因此第一个参数可能不是必需的,并且可能会导致这些额外调用 clGetPlatformInfo

      要尝试的另一件事是链接非 Nvidia OpenCL 库。不需要使用 GPU 供应商的 OpenCL 库,只要您使用的功能在这个其他 OpenCL 库中实现,任何都应该可以工作。使用 Nvidia 就没有风险,因为目前支持的最新版本是 OpenCL 1.2,即使不是所有供应商,大多数供应商都已经支持。因此,您可以尝试其他供应商 SDK (如 Intel 或 AMD)的 OpenCL lib。如果你在 Ubuntu 上,你可以使用ocl-icd-opencl-dev

      ====== 更新 =========

      创建上下文时尝试指定平台:

      const cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties) platformId, 0}; 
      
      cl_context context = clCreateContext (
          properties, // <-- here
          1,
          &deviceId,
          nullptr,
          nullptr,
          nullptr);
      

      可能是在创建上下文时未指定平台时,每次需要时都会查询它,但如上指定时则不会。

      【讨论】:

      • 我尝试将 {0} 作为第一个参数传递(NULL 不是有效参数)。它成功运行,但也有同样的分析问题。
      • 我不确定我是否理解您关于使用不同 OpenCL 实现的评论。我认为各个供应商的观点是,每个设备将根据设备的结构进行不同的映射。英特尔的实现(不是为与 NVidia GPU 通信而设计的)如何与我的 NVidia GPU 配合使用?
      • @geppettovacin OpenCL 库使用通用接口与特定的供应商驱动程序实现进行对话,这就是为什么您可以将任何与您的应用程序链接起来,只要您不使用任何特定于供应商的东西,它就可以工作,并且供应商支持代码中使用的给定 OpenCL 版本。另请注意,我只讨论 OpenCL 库,而不是供应商驱动程序,也不是整个 SDK。
      • @geppettodivacin 和 clCreateContext(const cl_context_properties *properties,...) 一样,我看不出有任何理由说明您无法通过 NULLnullptr
      • 所以,现在我们正在链接-lOpenCL。对于特定供应商的实施,我们将其更改为什么?
      猜你喜欢
      • 1970-01-01
      • 2020-05-10
      • 2016-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多