OpenCl内核的屏障是如何工作的?

OpenCl内核的屏障是如何工作的?,opencl,Opencl,内核代码: #pragma OPENCL EXTENSION cl_khr_fp64: enable #pragma OPENCL EXTENSION cl_amd_printf : enable __kernel void calculate (__global double* in) { int idx = get_global_id(0); // statement 1 printf("started for %d workitem\n", idx); // statem

内核代码:

#pragma OPENCL EXTENSION cl_khr_fp64: enable
#pragma OPENCL EXTENSION cl_amd_printf : enable

__kernel void calculate (__global double* in)
{
    int idx = get_global_id(0); // statement 1
    printf("started for %d workitem\n", idx); // statement 2
    in[idx] = idx + 100; // statement 3
    printf("value changed to %lf in %d workitem\n", in[idx], idx); // statement 4
    barrier(CLK_GLOBAL_MEM_FENCE); // statement 5
    printf("completed for %d workitem\n", idx); // statement 6
}
我使用ClenqueEndRangeKernel调用内核,方法是传递一个双数据类型数组的参数,该数组有5个元素,值初始化为0.0

我用5个全局工作大小调用内核,因此我将在每个工作项上求解数组的每个元素

但根据我对障碍的理论理解,为了同步工作组中的工作项,OpenCL提供了类似的功能 具有屏障功能。这将强制一个工作项等待,直到每个其他工作项 在小组中,他们到达了障碍物。通过创建屏障,可以确保每个工作项都达到相同的目标 在它的加工过程中。当工作项需要完成时,这是一个关键问题 计算将在未来计算中使用的中间结果

因此,我期望得到如下输出:

started for 0 workitem
started for 1 workitem
value changed to 100.000000 in 0 workitem
value changed to 101.000000 in 1 workitem
started for 3 workitem
value changed to 103.000000 in 3 workitem
started for 2 workitem
value changed to 102.000000 in 2 workitem
started for 4 workitem
value changed to 104.000000 in 4 workitem

completed for 3 workitem
completed for 0 workitem
completed for 1 workitem
completed for 2 workitem
completed for 4 workitem
这些已完成的语句将在最后一起,因为屏障将限制其他工作项,直到到达该点

但是,我得到的结果是

started for 0 workitem
value changed to 100.000000 in 0 workitem
completed for 0 workitem
started for 4 workitem
value changed to 104.000000 in 4 workitem
completed for 4 workitem
started for 1 workitem
started for 2 workitem
started for 3 workitem
value changed to 101.000000 in 1 workitem
value changed to 103.000000 in 3 workitem
completed for 3 workitem
value changed to 102.000000 in 2 workitem
completed for 2 workitem
completed for 1 workitem
我在逻辑上遗漏了什么吗?那么,屏障是如何为OpenCl内核工作的呢

在内核中添加了更多的检查,用于交叉检查屏障后的更新值,而不是打印语句

#pragma OPENCL EXTENSION cl_khr_fp64: enable
#pragma OPENCL EXTENSION cl_amd_printf : enable

__kernel void calculate (__global double* in)
{
    int idx = get_global_id(0);
    in[idx] = idx + 100;
    barrier(CLK_GLOBAL_MEM_FENCE);
    if (idx == 0) {
        in[0] = in[4];
        in[1] = in[3];
        in[2] = in[2];
        in[3] = in[1];
        in[4] = in[0];
    }
}
那么之后的数组应该是

after arr[0] = 104.000000
after arr[1] = 103.000000
after arr[2] = 102.000000
after arr[3] = 101.000000
after arr[4] = 100.000000
但结果是,我得到了:

after arr[0] = 0.000000
after arr[1] = 101.000000
after arr[2] = 102.000000
after arr[3] = 103.000000
after arr[4] = 104.000000

是的,您缺少这样一个事实:添加
printf()
会使所有结果顺序无效

事实上,OpenCL声明,
printf()
的使用是实现定义的,
如果printf是从多个工作项同时执行的,则无法保证对写入数据进行排序。
简单的逻辑将告诉您,队列将按顺序为每个WI刷新,因为这是在并行执行填满多个缓冲区(每个WI printf一个缓冲区)后序列化刷新的更简单的方法


它们是按照您期望的顺序执行的,但是标准输出的输出刷新是在内核已经完成之后发生的,并且没有按照原始顺序执行。

是的,添加
printf()
会使所有结果顺序无效

事实上,OpenCL声明,
printf()
的使用是实现定义的,
如果printf是从多个工作项同时执行的,则无法保证对写入数据进行排序。
简单的逻辑将告诉您,队列将按顺序为每个WI刷新,因为这是在并行执行填满多个缓冲区(每个WI printf一个缓冲区)后序列化刷新的更简单的方法


它们按照您期望的顺序执行,但是标准输出的输出刷新发生在内核已经完成之后,并且没有按照原始顺序执行。

代码看起来非常好,我怀疑本地工作组的大小,如果您没有指定本地工作组的大小,OpenCL编译器根据一些检查来选择最佳(通常是ONE

请检查您的ClenqueueEndRangeKernel呼叫w.r.t以下呼叫

size_t global_item_size = 5; //Specifies no. of total work items
size_t local_item_size = 5; // Specifies no. of work items per local group
clEnqueueNDRangeKernel( command_queue, kernel, 1, NULL, &global_item_size,    &local_item_size, 0, NULL, NULL );
注意:这个答案假设您没有指定本地工作组的大小,或者没有按照您的要求正确设置

关于工作组的更多信息:


屏障将阻止工作组中的所有线程,因为您尚未指定工作组大小(其大小视为一个),并且您将有5个工作组,每个工作组只有一个线程

代码看起来非常好,我怀疑本地工作组的大小,如果您没有指定本地工作组的大小,OpenCL编译器会根据一些检查选择最佳(通常是ONE

请检查您的ClenqueueEndRangeKernel呼叫w.r.t以下呼叫

size_t global_item_size = 5; //Specifies no. of total work items
size_t local_item_size = 5; // Specifies no. of work items per local group
clEnqueueNDRangeKernel( command_queue, kernel, 1, NULL, &global_item_size,    &local_item_size, 0, NULL, NULL );
注意:此答案假设您没有指定本地工作组大小,或者其未按照您的要求正确设置

关于工作组的更多信息:


屏障将阻止工作组中的所有线程,因为您尚未指定工作组大小(其大小视为一个),并且您将有5个工作组,每个工作组只有一个线程

感谢您的回复@DarkZeros,我现在明白printf并不是检查正确输出流的真正解决方案。执行顺序是随机变化的,这很好。但是,那么我的屏障逻辑是否正确?@DardZeros因为我刚才所做的不是在屏障后打印,而是在屏障后为单个WI重新排列了更新的“in”数组的值(因为只有一个项应该进行重新排列,其余项不应该进行,否则值将不同)。每个元素的重新排列的值都应该更新,因为它是在barrier之后调用的,所以“in”数组应该有更新的值。因此,重新排列的数组应该是正确的。这将清楚地显示屏障效应。但我的成绩仍然参差不齐。。每次迭代都会改变,我为相同的问题添加了更多的检查代码,并更新了问题中的代码。请提供一些指导。@Vishwadeep检查答案是的,因为您指定的是项目数量,而不是组数量。两件完全不同的事情。谢谢你们的回复@DarkZeros,我现在明白printf不是检查输出正确流程的真正解决方案。执行顺序是随机变化的,这很好。但是,那么我的屏障逻辑是否正确?@DardZeros因为我刚才所做的不是在屏障后打印,而是在屏障后为单个WI重新排列了更新的“in”数组的值(因为只有一个项应该进行重新排列,其余项不应该进行,否则值将不同)。每个元素的重新排列的值都应该更新,因为它是在barrier之后调用的,所以“in”数组应该有更新的值。因此,重新排列的数组应该是正确的。这将清楚地显示屏障效应。但我的成绩仍然参差不齐。。每次迭代都会改变,我为相同的迭代添加了更多的检查代码,并更新了c