我应该如何在 opencl 中正确设计矢量规范化(工作组大小和内核)

How should I properly design a vector normalization in opencl (work group sizes and kernels)

我是 OpenCL 的新手,在处理内存和本地工作组时遇到了一些麻烦。

我有一个 float[30][100][100][2025] 的四维数据结构。我目前遍历每个法线的前两个维度,并将生成的 2025x100 矩阵传递给以下 CPU 函数。

// num_columns = 100; num_rows = 2025
for (int column = 0; column < num_cols; column++){
    vector = matrix[column];

    float min = vector[0];
    float max = vector[0];
    for (int i = 0; i < num_rows; i++){
        if(vector[i] < min){min = vector[i];}
        if(vector[i] > max){max = vector[i];}
    }
    float diff = max-min + epsilon;

    for(int i = 0; i < num_rows; i++){
        vector[i] = (vector[i]-min)/diff; //MULTI-THREAD HERE
    }

    float mean = 0;
    for (int i = 0; i < num_rows; i++){
        mean += vector[i];
    }
    mean = mean/num_rows;

    for (int i = 0; i < num_rows; i++){
        vector[i] = vector[i]-mean + epsilon; //MULTI-THREAD HERE
    }

    float norm = 0;
    for (int i = 0; i < num_rows; i++){
        norm += vector[i]*vector[i];
    }
    norm = (float) sqrt(norm);

    if (norm > 0){
        for (int i = 0; i < num_rows; i++){
            vector[i] = vector[i]/norm; //MULTI-THREAD HERE
        }
    }
}

我已经评论了我认为使用 OpenCL 会受益的地方。遍历每一列、将向量写入设备缓冲区、执行规范化,然后将其读回主机的成本很高。 GPU(htc one m8 上的 Adreno 330)具有一次缓冲整个 2025x100 矩阵的内存。但是,我必须将它展平为一维浮点数组,而且我不知道如何一次仅在缓冲区的一部分(单个向量)上执行内核。

我已经阅读了很多关于 GPU 架构的文章,但我似乎无法理解设计它的正确方法以及我是否应该利用本地工作组来解决问题。我希望我已经提供了足够的信息让别人引导我朝着正确的方向前进。

编辑:我知道有一个内置的 CLnormalize 函数,但我需要实现三个不同的规范化函数,但我不知道它们是否与内置的函数匹配。无论如何我都试图测试它,但我收到一个错误,它发现没有匹配的内置函数用于 normalize(float *vector).

您可以轻松地在 GPU 上并行化完整的函数,而不仅仅是您标记为 "MULTI-THREAD HERE" 的部分。在 OpenCL 中,您的 threads/work-items 被划分为本地工作组。在同一个工作组中,线程可以一起工作,使用本地共享内存共享数据并使用屏障进行同步。您可以在每个工作组中处理一列。每列都是完全独立的,因此不需要工作组之间的同步。

在每个工作组中,我们可以使用每个线程来处理向量的 4 或 8 个元素。 (如果你只处理每个工作项的元素,你的工作组将大于最大工作组大小)这里棘手的部分是如何并行计算向量的最大值、最小值和总和:这里我们可以使用并行编程称为 "parallel reduction" 的原语。这是带有漂亮幻灯片的视频,解释了该技术: https://www.youtube.com/watch?v=siHYfH1RQ_s

您可以将此技术用于矢量、最小值和最大值的总和,以并行计算最小值、最大值和平均值。如果您同时执行所有这三个操作,它甚至会比三个单独的归约快一点,因为您不需要经常同步。您还可以使用局部变量来存储 vector[i] 的临时结果,并且只在开始时读入 vector[i] 一次,并在 vector[i] 上的所有计算完成后在最后写回 vector[i] .那可以节省很多内存带宽。