【问题标题】:Python -- cuda latency?Python——cuda 延迟?
【发布时间】:2018-09-06 17:27:32
【问题描述】:

使用支持 GPU 编程的各种库,我发现我的算法在 GPU 与 CPU 上的性能更差。我相信这是由于两个设备之间的通信延迟造成的。

我的平台是 W10x64,在戴尔 XPS 15 笔记本电脑中配备 i7-7700HQ 和 GTX 1050。

如果我使用任何库,例如pytorch.cuda.FloatTensorcupy.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倍……
  • 这里应该没有数据转换。 xy 在 GPU 上初始化;我假设 cupy 和 pytorch (我都测试过,它们在这里非常相似,尽管 pytorch 无法完成最后几步,因为它没有 fft 或复杂的张量)在 CPU 上创建数组并将其发送到 GPU。但是,所有后续计算都应该只涉及 GPU 数据,没有 CPU 交互;它应该每次都返回一个指向 GPU 数组的指针,而不是其他太多,只会产生往返延迟。
  • 我认为numpycupy等也实现了兼容的浮点数据类型,所以缓冲区可以直接从主机传输到设备,无需转换。
  • 在相同的笔记本型号上,我注意到安装崩溃固件更新后两个 GPU 的通信问题相同。第二次更新似乎减少了这一点,但它仍然可能在您遇到的延迟中发挥作用。

标签: python cuda latency


【解决方案1】:

您的所有操作似乎都受内存限制,可能除了您的 GPU 上的双倍 exp 和 atan。根据GeForce website,您的 GPU 的内存带宽似乎是 112GB/s。根据ark.intel.com,您的 CPU 可能有大约 37GB/s 的带宽。那是一个x4。

请注意,小型数据集确实适合 CPU 的 L2 缓存,因此您可以假设写入后的读取位于缓存中(比 DRAM 快一个数量级)。那可以玩x2。

最后,在 GPU 上启动此类操作时,问题的大小不足以让 GPU 隐藏延迟,因此您无法获得全部带宽:读取的成本比延迟更接近比它的吞吐量。如果您填充一半的读取总线,您将获得一半的带宽。

所有这些都可以通过 NV prof 验证或不分析您的代码。然后,您应该会看到各个内核的时序和延迟。

【讨论】:

    猜你喜欢
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 2015-03-10
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 2011-03-26
    • 2011-09-23
    相关资源
    最近更新 更多