Halide与C布局的numpy数组

3

我开始使用Halide并从Python环境中使用它。在Python环境中,数据被传递为Numpy数组,实际上是别名到其他地方定义的C++数组。

然而,当我调用Halide函数时,出现错误:

约束违反:img.stride.0 (520) == 1 (1)
已中止(核心已转储)

可以通过将Numpy数组复制到Fortran布局数组来“解决”此问题:

img=np.copy(img,order="F")
res=np.copy(res,order="F")

使用img和res对我的输入和输出图像进行处理。但请注意,这涉及到额外的复制操作,这对于整体全局内存访问来说非常不好。

我该如何解决这个问题?我一直在考虑的一种方法是告诉Python我的数组具有Fortran布局,并正确地切换索引... 但是,我目前使用PyArray_SimpleNewFromData获取Python数组(而不实际复制数据),这会导致C样式数组。


如果你认为一次额外的复制操作“真的很糟糕的话,那么你就不应该使用Python/Numpy,因为这种开销基本上是与领域相关的。话虽如此,你是否已经对此进行了基准测试?如果是这样的话,那你肯定在一些相当琐碎的操作中使用了Halide。这就引出了一个问题,为什么你对性能如此关注呢? - Eelco Hoogendoorn
Eelco,通过以下解决方案(即更改步幅而不进行数据复制),512x512浮点图像的开销降至0.25毫秒,而之前大约为7.5毫秒。Halide正在改进数据本地化和减少带宽使用,因此我们应该尽量避免任何额外的图像复制。请注意,由于其转置操作,我预计np.copy会非常糟糕。也许我们应该使用Halide重新实现np.copy以提高其性能... - Klamer Schutte
2个回答

2
Halide原生支持行优先存储,但索引方式如下:im(col, row)... 对于习惯将图像看作矩阵或在C中使用2D数组的用户而言,这看起来非常像列优先存储。
因此,你可以选择改变你的索引以匹配Halide的概念,或者告诉Halide你的内存布局是相反的(stride(0)较大)。
这里有一个涵盖类似主题的教程: http://halide-lang.org/tutorials/tutorial_lesson_16_rgb_generate.html 对于二维输入和函数,简短的版本是:
image_param.set_stride(0, Expr()).set_stride(1, 1);
output_func.output_buffer().set_stride(0, Expr()).set_stride(1, 1);

第一个 set_stride 调用会解除第0维的步长限制,第二个则告诉 Halide 可以假设第1维的步长为1。如果这样做,你需要将 Halide Funcs 在第二个维度上进行向量化,因为那是内存中密集的维度:

f(i, j) = ...
f.vectorize(j, 4)

0
问题在于PyArray_SimpleNewFromData从数据中创建了一个C风格的ndarray,而在主机C++代码中,数组是Fortran风格的。一种解决方案是在创建完ndarrays后立即将其转换,可以通过以下代码完成:
def swap(img):
    (sh1,sh2)=img.shape
    (st1,st2)=img.strides
    img.shape=(sh2,sh1)
    img.strides=(st2,st1)

在 Halide 中,我们可以在零维度(x)中正常进行矢量化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接