OpenCL:x86 和 x64 版本的不同工作内核代码

OpenCL: different work kernel code for the x86 and x64 versions

本文关键字:工作 内核 代码 版本 x86 x64 OpenCL      更新时间:2023-10-16
//////////////////////////////////
// host code
//////////////////////////////////
// ...
// select device / create context / build program
// ...
cl_kernel k_func_Test = nullptr;
k_func_Test = clCreateKernel(prog, "k_Test", &error);
size_t localsize = 3;
size_t globalsize = localsize * 2;
error = clEnqueueNDRangeKernel(command_queue, k_func_Test, 1, NULL, &globalsize_, &localsize_, 0, nullptr, nullptr);
clFinish(command_queue);
//////////////////////////////////
// kernel code
//////////////////////////////////
__kernel void k_Test()
{
    if ( (get_group_id(0) + get_group_id(1) + get_group_id(2)) == 0 &&
         (get_local_id(0) + get_local_id(1) + get_local_id(2)) == 0)
    {
        printf( "get_global_size = %d x %d x %d | " 
                "get_local_size = %d x %d x %d | " 
                "get_work_dim = %d n" 
                "////////////////////////////////////n", 
                get_global_size(0), get_global_size(1), get_global_size(2), 
                get_local_size(0), get_local_size(1), get_local_size(2), 
                get_work_dim()
                );
    }
    printf( "get_group_id = %d x %d x %d n" 
            "get_global_id = %d x %d x %d n" 
            "get_local_id = %d x %d x %d n" 
            "get_num_groups = %d x %d x %d n" 
            "////////////////////////////////////n", 
            get_group_id(0), get_group_id(1), get_group_id(2), 
            get_global_id(0), get_global_id(1), get_global_id(2), 
            get_local_id(0), get_local_id(1), get_local_id(2), 
            get_num_groups(0), get_num_groups(1), get_num_groups(2)
            );
}

我有 3 台设备:

1) 英特尔® 酷睿™ i7-4790K CPU @ 4.00GHz

2) 英特尔® 核芯显卡 4600

3) GeForce GTX 780 Ti

对于 x86 的所有设备,我得到了以下结果:

////////////////////////////////////
get_global_size = 6 x 1 x 1 | get_local_size = 3 x 1 x 1 | get_work_dim = 1
////////////////////////////////////
get_group_id = 1 x 0 x 0
get_global_id = 3 x 0 x 0
get_local_id = 0 x 0 x 0
get_num_groups = 2 x 1 x 1
////////////////////////////////////
get_group_id = 1 x 0 x 0
get_global_id = 4 x 0 x 0
get_local_id = 1 x 0 x 0
get_num_groups = 2 x 1 x 1
////////////////////////////////////
get_group_id = 1 x 0 x 0
get_global_id = 5 x 0 x 0
get_local_id = 2 x 0 x 0
get_num_groups = 2 x 1 x 1
////////////////////////////////////
get_group_id = 0 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 0 x 0 x 0
get_num_groups = 2 x 1 x 1
////////////////////////////////////
get_group_id = 0 x 0 x 0
get_global_id = 1 x 0 x 0
get_local_id = 1 x 0 x 0
get_num_groups = 2 x 1 x 1
////////////////////////////////////
get_group_id = 0 x 0 x 0
get_global_id = 2 x 0 x 0
get_local_id = 2 x 0 x 0
get_num_groups = 2 x 1 x 1
////////////////////////////////////

但是,我从x64开始,我得到了非常不同的结果。对于"英特尔® 核芯显卡 4600" - 结果与 x86 版本相同。

但是对于"Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz"和"GeForce GTX 780 Ti",我得到了以下结果:

////////////////////////////////////
get_global_size = 6 x 0 x 1 | get_local_size = 0 x 1 x 0 | get_work_dim = 3
////////////////////////////////////
get_group_id = 1 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 3 x 0 x 0
get_num_groups = 0 x 0 x 0
////////////////////////////////////
get_group_id = 1 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 4 x 0 x 0
get_num_groups = 0 x 0 x 0
////////////////////////////////////
get_group_id = 1 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 5 x 0 x 0
get_num_groups = 0 x 0 x 0
////////////////////////////////////
get_group_id = 0 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 0 x 0 x 0
get_num_groups = 0 x 0 x 0
////////////////////////////////////
get_group_id = 0 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 1 x 0 x 0
get_num_groups = 0 x 0 x 0
////////////////////////////////////
get_group_id = 0 x 0 x 0
get_global_id = 0 x 0 x 0
get_local_id = 2 x 0 x 0
get_num_groups = 0 x 0 x 0
////////////////////////////////////

所以,正如我们所看到的结果非常不同。例如,在函数"clEnqueueNDRangeKernel"中,我们将参数设置为 work_dim = 1,但我不明白为什么 x64 版本应用程序 - 它的工作方式不同!和 get_work_dim() 得到 1 或 3(最后我认为得到 local_work_size 的值)。

也许有人经历过这个? 实现不同的驱动程序?这是什么?

我相信

您在这里的问题可能在于您在调用 printf 时使用的格式说明符。

OpenCL C 内置的 get_global_size()、get_local_size()、get_num_groups()、get_global_id()、get_local_id() 和 get_group_id() 都返回一个 size_t 值。size_t类型因体系结构的地址宽度而异;它的大小等于指针的大小(即 sizeof(size_t) == sizeof(void *)),因此在 x86 上size_t是 4 字节,在 x86_64 size_t 上是 8 字节。因此,printf 具有 size_t 值的"%zu"格式说明符。这可以解释为什么您在 x86 和 x86_64 内核构建中看到不同的输出。

此外,OpenCL C 内置的 get_work_dim() 返回一个无符号的 int 值,为此您应该使用 "%u" 格式说明符。

也许为您的内核尝试一下:

__kernel void k_Test()
{
    if ( (get_group_id(0) + get_group_id(1) + get_group_id(2)) == 0 &&
         (get_local_id(0) + get_local_id(1) + get_local_id(2)) == 0)
    {
        printf( "get_global_size = %zu x %zu x %zu | " 
                "get_local_size = %zu x %zu x %zu | " 
                "get_work_dim = %u n" 
                "////////////////////////////////////n", 
                get_global_size(0), get_global_size(1), get_global_size(2), 
                get_local_size(0), get_local_size(1), get_local_size(2), 
                get_work_dim()
                );
    }
    printf( "get_group_id = %zu x %zu x %zu n" 
            "get_global_id = %zu x %zu x %zu n" 
            "get_local_id = %zu x %zu x %zu n" 
            "get_num_groups = %zu x %zu x %zu n" 
            "////////////////////////////////////n", 
            get_group_id(0), get_group_id(1), get_group_id(2), 
            get_global_id(0), get_global_id(1), get_global_id(2), 
            get_local_id(0), get_local_id(1), get_local_id(2), 
            get_num_groups(0), get_num_groups(1), get_num_groups(2)
            );
}

是的,空中螳螂的权利!函数在内核上的 printf 的问题。在我对函数 Test() 进行更改后,工作函数是相同的,并且在所有平台上进行预测。

__kernel void k_Test()
{
    if ( (get_group_id(0) + get_group_id(1) + get_group_id(2)) == 0 &&
         (get_local_id(0) + get_local_id(1) + get_local_id(2)) == 0)
    {
        printf( "get_global_size = %d x %d x %d | " 
                "get_local_size = %d x %d x %d | " 
                "get_work_dim = %d n" 
                "////////////////////////////////////n", 
                (int)get_global_size(0), (int)get_global_size(1), (int)get_global_size(2), 
                (int)get_local_size(0), (int)get_local_size(1), (int)get_local_size(2), 
                (int)get_work_dim()
                );
    }
    printf( "get_group_id = %d x %d x %d n" 
            "get_global_id = %d x %d x %d n" 
            "get_local_id = %d x %d x %d n" 
            "get_num_groups = %d x %d x %d n" 
            "////////////////////////////////////n", 
            (int)get_group_id(0), (int)get_group_id(1), (int)get_group_id(2), 
            (int)get_global_id(0), (int)get_global_id(1), (int)get_global_id(2), 
            (int)get_local_id(0), (int)get_local_id(1), (int)get_local_id(2), 
            (int)get_num_groups(0), (int)get_num_groups(1), (int)get_num_groups(2)
            );
}   

按照建议并使用我编写的 %u 和 %zu 格式说明符以下代码:

__kernel void k_Test2()
{
    printf( "get_work_dim = %u get_global_id = %zu x %zu x %zu n", get_work_dim(), get_global_id(0), get_global_id(1), get_global_id(2));
}

并理解:说明符 %u - 有效!%zu - 不适用于所有平台。例如,英特尔 HD 返回"无效表达式:(空)错误:printf 引发异常"。所以我通过添加前缀 (int) 在函数k_Test中进行了强制转换。