Numba Cuda计算似乎比顺序运行慢.我犯了明显的错误吗?



有几个线程涵盖类似的主题,但不幸的是,这些似乎对我来说太复杂了,所以我想问一个类似的问题,希望有人能看看我的代码,特别是告诉我,如果我有什么错。

我现在正在学习numba cuda,从网上可以找到的简单例子开始。我从这里开始这个教程:

https://github.com/ContinuumIO/gtc2017-numba/blob/master/4%20-%20Writing%20CUDA%20Kernels.ipynb

显示了如何并行地对数组进行加法。他们用来计算时间的系统配置没有给出。对于代码复制,我使用Geforce GTX 1080 Ti和英特尔酷睿i7 8700K CPU。

我基本上复制了教程中的添加脚本,但也添加了顺序代码进行比较:

from numba import cuda
import numpy as np
import time
import math
@cuda.jit
def addition_kernel(x, y, out):
tx = cuda.threadIdx.x
ty = cuda.blockIdx.x
block_size = cuda.blockDim.x  
grid_size = cuda.gridDim.x  
start = tx+ ty * block_size
stride = block_size * grid_size
for i in range(start, x.shape[0], stride):
out[i] = y[i] + x[i]
def add(n, x, y):
for i in range(n):
y[i] = y[i] + x[i]

if __name__ =="__main__":
print(cuda.gpus[0])
print("")
n = 100000
x = np.arange(n).astype(np.float32)
y = 2 * x
out = np.empty_like(x)
x_device = cuda.to_device(x)
y_device = cuda.to_device(y)
out_device = cuda.device_array_like(x)

# Set the number of threads in a block
threadsperblock = 128
# Calculate the number of thread blocks in the grid
blockspergrid = 30#math.ceil(n[0] / threadsperblock)
# Now start the kernel
start = time.process_time()
cuda.synchronize()
addition_kernel[blockspergrid, threadsperblock](x_device, y_device, out_device)
cuda.synchronize()
end = time.process_time()
out_global_mem = out_device.copy_to_host()
print("parallel time: ", end - start)
start = time.process_time()
add(n,x,y)
end = time.process_time()
print("sequential time: ", end-start)

平行时间平均约0.14秒,而代码没有GPU内核只需要0.02秒。

这对我来说似乎很奇怪。我做错什么了吗?或者这个问题不是并行性的好例子?(我不认为你可以并行运行for循环)

什么是奇怪的是,我几乎没有注意到如果我不使用to_device()功能的差异。据我所知,这些应该是重要的,因为它们避免了每次迭代后CPU和GPU之间的通信。

addition_kernel在运行时编译当它被称为第一次,所以在中间你的测量时间!内核的编译是一个相当密集的操作。您可以强制编译提前完成(例如:

请注意,数组有点太小,所以您可以看到gpu上的很大改进。此外,与CPU版本的比较并不公平:您还应该使用Numba来实现CPU,或者至少使用Numpy(但不是解释的纯cpython循环)。

下面是一个例子:

import numba as nb
@cuda.jit('void(float32[::1], float32[::1], float32[::1])')
def addition_kernel(x, y, out):
tx = cuda.threadIdx.x
ty = cuda.blockIdx.x
block_size = cuda.blockDim.x  
grid_size = cuda.gridDim.x  
start = tx+ ty * block_size
stride = block_size * grid_size
for i in range(start, x.shape[0], stride):
out[i] = y[i] + x[i]
@nb.njit('void(int64, float32[::1], float32[::1])')
def add(n, x, y):
for i in range(n):
y[i] = y[i] + x[i]

最新更新