4

我刚刚开始使用 OpenCL,我一直坚持如何以合理有效的方式构建程序(主要是避免大量数据与 GPU 之间的传输或任何正在完成的工作)

我想做的是,鉴于:

v = r*i + b*j + g*k

..我知道,和, 但是,和v的各种值是未知的。我想通过蛮力计算//的合理值rgbijkijk

换句话说,我有一堆“原始”RGB 像素值,并且我有这些颜色的去饱和版本。我不知道用于计算去饱和值的权重 (i/j/k)。

我最初的计划是:

  1. 将数据加载到 CL 缓冲区(因此输入 r/g/b 值和输出)

  2. 有一个内核,它接受三个可能的矩阵值和各种像素数据缓冲区。

    然后它执行v = r*i + b*j + g*k,并将 的值减去v已知值,并将其存储在“分数”缓冲区中

  3. 另一个内核计算该值的 RMS 误差(如果所有输入值的差异为零,则 i/j/k 的值是“正确的”)

我有这个工作(使用 Python 和 PyCL 编写,代码在这里),但我想知道如何将这部分工作更多地并行化(通过一次尝试多个 i/j/k 值)

我的问题是,我有 4 个只读缓冲区(3 个用于输入值,1 个用于预期值),但对于 i/j/k 的每个组合,我都需要一个单独的“分数”缓冲区

另一个问题是 RMS 计算是最慢的部分,因为它实际上是单线程的(将“score”和 sqrt() 中的所有值加起来)

基本上,我想知道是否有一种合理的方式来构建这样的程序。

这似乎是一项非常适合 OpenCL 的任务——希望我的目标描述不会太复杂!如前所述,我当前的代码在这里,如果更清楚,这是我正在尝试做的 Python 版本:

import sys
import math
import random


def make_test_data(w = 128, h = 128):
    in_r, in_g, in_b = [], [], []

    print "Make raw data"
    for x in range(w):
        for y in range(h):
            in_r.append(random.random())
            in_g.append(random.random())
            in_b.append(random.random())

    # the unknown values
    mtx = [random.random(), random.random(), random.random()]

    print "Secret numbers were: %s" % mtx

    out_r = [(r*mtx[0] + g*mtx[1] + b*mtx[2]) for (r, g, b) in zip(in_r, in_g, in_b)]

    return {'in_r': in_r, 'in_g': in_g, 'in_b': in_b,
            'expected_r': out_r}


def score_matrix(ir, ig, ib, expected_r, mtx):
    ms = 0
    for i in range(len(ir)):
        val = ir[i] * mtx[0] + ig[i] * mtx[1] + ib[i] * mtx[2]
        ms += abs(val - expected_r[i]) ** 2
    rms = math.sqrt(ms / float(len(ir)))
    return rms


# Make random test data
test_data = make_test_data(16, 16)


lowest_rms = sys.maxint
closest = []

divisions = 10
for possible_r in range(divisions):
    for possible_g in range(divisions):
        for possible_b in range(divisions):

            pr, pg, pb = [x / float(divisions-1) for x in (possible_r, possible_g, possible_b)]

            rms = score_matrix(
                test_data['in_r'], test_data['in_g'], test_data['in_b'], 
                test_data['expected_r'],
                mtx = [pr, pg, pb])

            if rms < lowest_rms:
                closest = [pr, pg, pb]
                lowest_rms = rms

print closest
4

2 回答 2

1

i,j,k 集是独立的吗?我认为是的。很少有事情会影响你的表现:

  1. 运行太多小内核
  2. 使用全局内存在 score_matrix 和 rm_to_rms 之间进行通信

您可以通过以下更改将两个内核重写为一个:

  1. 使一个 OpenCL 工作组可以在不同的 i、j、k 上工作 - 您可以在 CPU 上预先生成它
  2. 为了做1,你需要用一个线程处理数组的多个元素,你可以这样做:

    int i = get_thread_id(0);
    float my_sum = 0;
    
    for (; i < array_size; i += get_local_size(0)){
        float val = in_r[i] * mtx_r + in_g[i] * mtx_g + in_b[i] * mtx_b;
        my_sum += pow(fabs(expect_r[i] - val), 2);
    }
    
  3. 在此之后,您将每个线程的 my_sum 写入本地内存并用 reduce (O(log(n)) 算法) 进行总结。

  4. 将结果保存到全局内存中

或者,如果您需要按顺序计算 i、j、k,您可以在 OpenCL 规范中查找屏障和内存围栏函数,这样您就可以使用这些函数而不是运行两个内核,只需记住在第一步总结所有内容,写入全局同步所有线程,然后再次总结

于 2011-08-31T18:06:43.093 回答
1

有两个潜在的问题:

  1. 如果处理每个图像所需的工作量很小,内核启动开销可能会很大。这就是您将通过i,j,k在单个内核中组合多个值的评估来解决的问题。
  2. RMSE 总和计算的序列化。目前,这可能是更大的问题。

为了解决 (2),请注意求和可以并行计算,但它并不像在输入中的每个像素上单独映射一个函数那么简单。这是因为求和需要在相邻元素之间传递值,而不是独立处理所有元素。这种模式通常称为减少

PyOpenCL 包括对常见归约的高级支持。您在这里想要的是总和减少:pyopencl.array.sum(array).

进一步研究如何在原始 OpenCL 中实现这一点,Apple 的 OpenCL 文档包含一个sum 并行归约的示例。与您想要做的最相关的部分是内核以及运行归约的主机 C 程序main的和create_reduction_pass_counts函数。

于 2011-08-31T18:27:34.043 回答