PyCUDA 2019.1中GPUArray正确复制方法及步长/顺序丢失问题问询
解决PyCUDA中GPUArray复制时丢失数组顺序/步长的问题
这个PyCUDA的长期bug确实挺让人头疼的——gpuarray.copy()默认会忽略原数组的步长(strides)和存储顺序(比如Fortran/F-order),强制转换成C-order后再复制,直接导致你示例里的断言失败。下面我一步步给你讲清楚解决方案、底层内存的情况,以及如何确保复制的正确性:
一、直接修复:手动保留步长与存储顺序
要正确复制带自定义步长或非C顺序的GPUArray,不能用原生的copy()方法,得手动创建一个和原数组参数完全一致的空GPUArray,再用设备到设备的内存拷贝完成数据转移。
示例代码如下:
import numpy as np import pycuda.autoinit from pycuda import gpuarray from pycuda.driver import memcpy_dtod # 创建F-order的numpy数组 np_array = np.array([[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]], order="F") gpu_array = gpuarray.to_gpu(np_array) # 手动创建与原GPUArray参数一致的空数组 gpu_array_copy = gpuarray.GPUArray( shape=gpu_array.shape, dtype=gpu_array.dtype, strides=gpu_array.strides, order="F" if gpu_array.is_f_contiguous() else "C" ) # 执行设备到设备的内存拷贝(直接按字节复制,不改变内存布局) memcpy_dtod(gpu_array_copy, gpu_array) # 现在断言会成功通过 assert(np.array_equal(gpu_array_copy.get(), np_array)) # 额外验证存储顺序是否一致 assert(gpu_array_copy.is_f_contiguous() == np.isfortran(np_array))
二、底层内存是否一致?
用上面的方法复制后,原GPUArray和新GPUArray的底层设备内存布局是完全一致的。原因很简单:memcpy_dtod是直接按字节拷贝设备内存,不会对数据做任何重排。
而原生gpuarray.copy()出问题的根源,是它会先把数组转换成连续的C-order布局再分配新内存,相当于做了一次“扁平化再重组”,彻底破坏了原有的步长和存储顺序。你可以通过以下代码验证内存一致性:
# 两个数组的内存总字节数完全相同 print(gpu_array.nbytes == gpu_array_copy.nbytes) # 输出True # 注意:内存指针(ptr)不同,因为是独立的内存块,但内容完全一致
三、通用的正确复制方法
如果需要处理任意顺序的数组,建议封装一个工具函数,自动判断原数组的属性并完成安全复制:
def safe_gpuarray_copy(gpu_arr): """安全复制GPUArray,保留原数组的形状、 dtype、步长和存储顺序""" new_arr = gpuarray.GPUArray( shape=gpu_arr.shape, dtype=gpu_arr.dtype, strides=gpu_arr.strides, order="F" if gpu_arr.is_f_contiguous() else "C" ) memcpy_dtod(new_arr, gpu_arr) return new_arr # 使用示例 gpu_array_copy = safe_gpuarray_copy(gpu_array) assert(np.array_equal(gpu_array_copy.get(), np_array)) assert(gpu_array_copy.is_f_contiguous() == gpu_array.is_f_contiguous())
另外补充个小技巧:如果只是需要把GPUArray的数据取回主机并保留顺序,可以直接用gpu_array.get(order='F')(针对F-order数组),这样主机端的numpy数组会直接保留原存储顺序,不需要额外处理。
内容的提问来源于stack exchange,提问作者Krupip
相关产品推荐
相关产品推荐

