【发布时间】:2018-09-06 17:27:32
【问题描述】:
使用支持 GPU 编程的各种库,我发现我的算法在 GPU 与 CPU 上的性能更差。我相信这是由于两个设备之间的通信延迟造成的。
我的平台是 W10x64,在戴尔 XPS 15 笔记本电脑中配备 i7-7700HQ 和 GTX 1050。
如果我使用任何库,例如pytorch.cuda.FloatTensor 或 cupy.ndarray 触摸 GPU 阵列似乎需要大约 20~40us。这是一个 MWE:
import cupy as cu
ary = cu.empty((1))
const_one = cu.ones((1))
%timeit ary + const_one
> 18.5 µs ± 102 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
在 1 个元素上操作不是 GPU 的用途,这是一个人为的示例,用于显示两条数据的最短操作时间,两者都驻留在 GPU 上。
我相信 cuda 代码的结构是在 GPU 的能力范围内构建和消耗操作队列,因此这种延迟会随着时间或更大的内存块而消失?
这是 numpy 和 cupy 中相同算法的完整比较,它以双精度完成 128x128 光瞳上的相位误差,并使用它来创建点扩散函数。
我已尝试尽可能小心地减少主机-设备传输;对于 cupy,CPU 上只有数组大小的整数,因为我无法提前在 GPU 上获取它们。
初始设置:
precision = 'float32'
ary_size = 128
pad = ary_size // 2
cu0 = cu.zeros((1))
cu2 = cu.ones((1)) * 2
cu1 = cu.ones((1))
CUDA 执行
%%timeit
x = cu.linspace(-cu1, cu1, ary_size, dtype=precision)
y = cu.linspace(-cu1, cu1, ary_size, dtype=precision)
xx, yy = cu.meshgrid(x, y)
rho, phi = cu.sqrt(xx**cu2 + yy**cu2), cu.arctan2(yy, xx)
phase_err = rho ** cu2 * cu.cos(phi)
mask = rho > cu1
wv_ary = cu.exp(1j * cu2 * np.pi * phase_err)
wv_ary[mask] = cu0
padded = cu.pad(wv_ary, ((pad, pad), (pad, pad)), mode='constant', constant_values=0)
psf = fftshift(fft2(ifftshift(padded)))
intensity_psf = abs(psf)**cu2
> 4.73 ms ± 86.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Numpy 等价物:
%%timeit
x = np.linspace(-1, 1, ary_size, dtype=precision)
y = np.linspace(-1, 1, ary_size, dtype=precision)
xx, yy = np.meshgrid(x, y)
rho, phi = np.sqrt(xx**2 + yy**2), np.arctan2(yy, xx)
phase_err = rho ** 2 * np.cos(phi)
mask = rho > 1
wv_ary = np.exp(1j * 2 * np.pi * phase_err)
wv_ary[mask] = 0
padded = np.pad(wv_ary, ((pad, pad), (pad, pad)), mode='constant', constant_values=0)
psf = nfftshift(nfft2(nifftshift(padded)))
intensity_psf = abs(psf)**2
> 7.29 ms ± 63.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
因此,我使用 cuda 只能提高 35% 的性能。我知道我没有特别强大的 GPU,而且它的 fp64 比 fp32 性能差很多;但以 f32 精度重复并不会显着提高速度。
我也知道,如果我将尺寸更改为更大的值,例如512,CUDA 更好地展示了 GPU 性能,GPU 时间为 8.19 毫秒,CPU 时间为 144 毫秒。
因此,这种 GPU-CPU 协调延迟似乎是在小数组大小时杀死我的原因。这是我笔记本电脑的怪癖吗?很难找到有关 CPU-GPU 延迟的信息,但我看到一些报告显示 PCI-E 延迟小于 1us。如果是这种情况,那么我的 cuda 代码会快 20 倍左右,并且更实用。
【问题讨论】:
-
沟通不是唯一的过程。 CPU 需要将内存中的数据从 python 对象更改为与 GPU 兼容的其他对象。这不是怪癖,这是非常正常的。过去,我为我的 python 程序实现了一个 C 扩展来完成数百万个微小的工作。而且因为转换和沟通,确实慢了10倍……
-
这里应该没有数据转换。
x和y在 GPU 上初始化;我假设 cupy 和 pytorch (我都测试过,它们在这里非常相似,尽管 pytorch 无法完成最后几步,因为它没有 fft 或复杂的张量)在 CPU 上创建数组并将其发送到 GPU。但是,所有后续计算都应该只涉及 GPU 数据,没有 CPU 交互;它应该每次都返回一个指向 GPU 数组的指针,而不是其他太多,只会产生往返延迟。 -
我认为
numpy和cupy等也实现了兼容的浮点数据类型,所以缓冲区可以直接从主机传输到设备,无需转换。 -
在相同的笔记本型号上,我注意到安装崩溃固件更新后两个 GPU 的通信问题相同。第二次更新似乎减少了这一点,但它仍然可能在您遇到的延迟中发挥作用。