并行算法

元素表达式求值(“map”)

计算上涉及的表达式 pyopencl.array.Array 使用重载运算符的实例可能会有些低效,因为会为每个中间结果创建一个新的临时实例。模块中的功能 pyopencl.elementwise 包含有助于生成内核的工具,这些内核在一次传递中对一个或多个操作数计算多级表达式。

class pyopencl.elementwise.ElementwiseKernel(context, arguments, operation, name='kernel', preamble='', options=[])

一种采用标量或向量的核 参数 并执行 操作 在这些参数上指定为C的片段。

参数
  • arguments -- 格式化为C参数列表的字符串。

  • operation -- 执行所需“映射”操作的C代码片段。当前索引可用作变量 i . 操作 可能包含以下语句 PYOPENCL_ELWISE_CONTINUE ,将终止对当前元素的处理。

  • name -- 编译内核时使用的函数名

  • options -- 未经修改传递给 pyopencl.Program.build() .

  • preamble -- 在elementwise操作的内核源代码的函数上下文之外插入的一段C源代码。

警告

使用A return 语句在 操作 将导致不正确的结果,因为某些元素可能永远不会得到处理。使用 PYOPENCL_ELWISE_CONTINUE 相反。

在 2013.1 版更改: 补充 PYOPENCL_ELWISE_CONTINUE .

__call__(*args, wait_for=None)

调用生成的标量内核。参数可以是标量或 pyopencl.array.Array 实例。

Returns a new pyopencl.Event. wait_for may either be None or a list of pyopencl.Event instances for whose completion this command waits before starting exeuction.

下面是一个用法示例:

.. literalinclude:: ../examples/demo_elementwise.py

(你可以找到这个例子 examples/demo_elementwise.py 在PyOpenCL发行版中。)

总和和计数(“减少”)

class pyopencl.reduction.ReductionKernel(ctx, dtype_out, neutral, reduce_expr, map_expr=None, arguments=None, name='reduce_kernel', options=[], preamble='')

生成一个核,该核接受多个标量或向量 参数 (至少一个向量参数),执行 map_expr 在向量参数的每个条目上,然后 reduce_expr 关于结果。 中立的 作为初始值。 序言 提供了在实际的缩减内核代码之前添加预处理器指令和其他代码(如助手函数)的可能性。

向量在 map_expr 应该由变量索引 i . reduce_expr 使用形式值“a”和“b”指示二进制缩减操作的两个操作数。如果未指定 map_exprin[i] 自动假定并视为唯一一个输入参数。

dtype_out 指定 numpy.dtype 其中执行缩减并返回结果。 中立的 指定为浮点或格式为字符串的整数。 reduce_exprmap_expr 指定为字符串格式的操作和 参数 指定为格式化为C参数列表的字符串。 name 指定编译内核的名称。 选项 未经修改传递给 pyopencl.Program.build() . 序言 指定在实际内核之前插入的代码字符串。

__call__(*args, queue=None, wait_for=None, return_event=False, out=None)

wait_for may either be None or a list of pyopencl.Event instances for whose completion this command waits before starting exeuction.

out 生成的单个条目 pyopencl.array.Array 可以指定。由于支持偏移,因此可以将结果存储在任何位置(例如。 out=a[3]

返回

作为单个项的结果标量 pyopencl.array.Array 如果 return_event ,否则为元组 (scalar_array, event) .

注解

归还的人 pyopencl.Event 只对应于部分执行还原。它不适合于分析。

2011.1 新版功能.

在 2014.2 版更改: 补充 out 参数。

下面是一个用法示例:

a = pyopencl.array.arange(queue, 400, dtype=numpy.float32)
b = pyopencl.array.arange(queue, 400, dtype=numpy.float32)

krnl = ReductionKernel(ctx, numpy.float32, neutral="0",
        reduce_expr="a+b", map_expr="x[i]*y[i]",
        arguments="__global float *x, __global float *y")

my_dot_prod = krnl(a, b).get()

前缀和(“扫描”)

前缀和是数组的运行和,例如。 numpy.cumsum() ::

>>> import numpy as np
>>> a = [1,1,1,1,1,2,2,2,2,2]
>>> np.cumsum(a)
array([ 1,  2,  3,  4,  5,  7,  9, 11, 13, 15])

这是一个非常简单的扫描示例。结果表明,扫描的功能明显更为广泛。它们是许多非平凡并行算法的基本构造块。由于循环携带的依赖关系,许多由扫描启用的操作似乎很难并行化。

参见

Prefix sums and their applications <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.128.6230> _,作者盖伊·布莱洛。

本文概述了一些令人惊讶的扫描应用。

简单/传统界面

这些内置到PyOpenCL中的操作是通过 GenericScanKernel .

使用示例

此示例演示了的简化版本的实现 pyopencl.algorithm.copy_if() ,如果整数大于300,则将其从数组复制到(可变大小)输出:

knl = GenericScanKernel(
        ctx, np.int32,
        arguments="__global int *ary, __global int *out",
        input_expr="(ary[i] > 300) ? 1 : 0",
        scan_expr="a+b", neutral="0",
        output_statement="""
            if (prev_item != item) out[item-1] = ary[i];
            """)

out = a.copy()
knl(a, out)

a_host = a.get()
out_host = a_host[a_host > 300]

assert (out_host == out.get()[:len(out_host)]).all()

正在扫描的值是多个标志,指示每个数组元素是否大于300。这些标志由 input_expr . 此数组上的前缀和给出了大于300的数组项的运行计数。这个 output_statement 比较 prev_item (上一项的扫描结果,即索引)到 item (当前项目的扫描结果,即索引)。如果它们不同,即如果谓词在此位置得到满足,则该项将存储在计算索引处的输出中。

此示例没有使用PyOpenCL中也提供的以下高级功能:

  • 分段扫描

  • 访问中的上一项 input_expr (例如,比较)见 implementation 属于 pyopencl.algorithm.unique() 举个例子。

定制扫描内核

2013.1 新版功能.

调试辅助工具

class pyopencl.scan.GenericDebugScanKernel

与执行相同的功能和具有相同的接口 GenericScanKernel ,但使用非常简单的顺序扫描。在CPU平台上工作得最好,通过消除并行执行中可能出现的问题,帮助隔离扫描中的错误。

简单/传统界面

class pyopencl.scan.ExclusiveScanKernel(ctx, dtype, scan_expr, neutral, name_prefix='scan', options=[], preamble='', devices=None)

生成一个可以计算 prefix sum 使用任何关联操作 scan_expr . scan_expr 使用形式值“a”和“b”指示关联二进制运算的两个操作数。 中立的 是中性元素 scan_expr 服从 scan_expr(a, neutral) == a .

D型 指定要操作的数组的类型。 name_prefix 用于内核名称,以确保配置文件和日志中的可识别性。 选项 是生成时要使用的编译器选项的列表。 序言 指定在实际内核之前插入的代码字符串。 设备 可用于限制内核要在其上运行的设备集。(默认为上下文中的所有设备) ctx .

__call__(self, input_ary, output_ary=None, allocator=None, queue=None)
class pyopencl.scan.InclusiveScanKernel(ctx, dtype, scan_expr, neutral=None, name_prefix='scan', options=[], preamble='', devices=None)

作品像 ExclusiveScanKernel .

在 2013.1 版更改: 中立的 现在总是需要。

对于阵列 [1,2,3] ,包含扫描结果 [1,3,6] ,独占扫描结果 [0,1,3] .

下面是一个用法示例:

knl = InclusiveScanKernel(context, np.int32, "a+b")

n = 2**20-2**18+5
host_data = np.random.randint(0, 10, n).astype(np.int32)
dev_data = cl_array.to_device(queue, host_data)

knl(dev_data)
assert (dev_data.get() == np.cumsum(host_data, axis=0)).all()

谓词副本(“partition”,“unique”,…)

排序(基数排序)

构建许多可变大小的列表

双调排序