从缓冲区复制的速度差异
The difference in the speed of copying from the buffer
我无法理解为什么在这些示例中复制速度差异如此之大。我从他们那里得到了几乎无关紧要的图像。 'faster' 变体
的计算时间也更快
没有帮助:
将'slow'变体的所有变量移到内核中,各种内存标志几乎不改变结果。
原来是内核的问题,到底是什么问题?
警告!我粘贴了整个文件
import pyopencl as cl
import numpy as np
from PIL import Image
import time
更快的变体。从缓冲区复制需要 ~0.15s
width = 800
height = 800
X = 0
Y = 0
R = 2
maxiter = 80000
xmin = X - R
xmax = X + R
ymin = Y - R
ymax = Y + R
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
r1 = np.linspace(xmin, xmax, width, dtype=np.float64)
r2 = np.linspace(ymin, ymax, height, dtype=np.float64)
q = r1 + r2[:, None] * 1j
q = np.ravel(q)
output = np.empty(width*height, dtype=np.uint8)
mf = cl.mem_flags
q_opencl = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=q)
output_opencl = cl.Buffer(ctx, mf.WRITE_ONLY, output.nbytes)
prg = cl.Program(ctx, """
__kernel void mandelbrot(__global double2 *q,
__global uchar *output, ushort const maxiter)
{
int gid = get_global_id(0);
double nreal, real = 0;
double imag = 0;
output[gid] = 0.0;
int curiter = 0;
for(curiter = 0; curiter < maxiter; curiter++) {
nreal = real*real - imag*imag + q[gid].x;
imag = 2* real*imag + q[gid].y;
real = nreal;
if (real*real + imag*imag > 4.0f){
break;
}
}
if (curiter < maxiter) {
output[gid] = curiter*64;
}
}
""").build()
prg.mandelbrot(queue, output.shape, None, q_opencl, output_opencl, np.uint16(maxiter))
t0 = time.time()
cl.enqueue_copy(queue, output, output_opencl).wait()
print(time.time()-t0, 'copy')
output = output.reshape((width, height))
较慢的变体。从缓冲区复制需要 ~0.78s
size = (800, 800)
X = 0
Y = 0
R = 2
maxiter = 80000
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
output = np.empty(size[0]*size[1], dtype=np.uint8)
mf = cl.mem_flags
output_cl = cl.Buffer(ctx, mf.WRITE_ONLY, output.nbytes)
prg = cl.Program(ctx, """
__kernel void mandelbrot(
__global uchar *out,
int width,
int height,
double real,
double imag,
double const radius,
int const maxiter) {
int id = get_global_id(0);
int i = id % width;
int j = id / width;
double window_radius = (width < height) ? width : height;
double x0 = real + radius * (2 * i - (float)width) / window_radius;
double y0 = imag - radius * (2 * j - (float)height) / window_radius;
double x = 0;
double y = 0;
int n = 0;
double x_temp = 0;
for(n = 0; n < maxiter; n++)
{
x_temp = x*x - y*y + x0;
y = 2 * x*y + y0;
x = x_temp;
if (x*x + y*y > 4.0f){
break;
}
}
if (n < maxiter) {
out[id] = n*64;
}
else {
out[id] = 0;
}
}
""").build()
prg.mandelbrot(queue, output.shape, None,
output_cl,
np.int32(size[0]),
np.int32(size[1]),
np.float64(X),
np.float64(Y),
np.float64(R),
np.int32(maxiter),
)
t0 = time.time()
cl.enqueue_copy(queue, output, output_cl).wait()
print(time.time() - t0, 'copy')
output = output.reshape((size[1], size[0]))
在第一个版本中,您将 80000 赋给 maxiter,它是无符号短整数。最大 65535。溢出并环绕到 14k ish 值。
第二个版本是 32 位的 int。 80k 正确通过。
14.5k / 80.0k 次迭代
0.15 / 0.78 秒
一致
I cannot understand the reason why the copy speed varies so much in these examples.
原因是:您没有测量复制命令的时间。
你说 "prg.mandelbrot() executes the kernel and do all the calculations" - 它不是那样做的。它 排队 内核。然后你排队复制命令,然后你调用wait()
。有些实现在入队后立即开始执行,但有些直到您调用 clFinish/clFlush/clWaitForEvents
才开始执行(最后一个是 PyOpenCL 的 Event.wait()
所做的 - 在您的代码中,cl.enqueue_copy()
returns 一个事件).
问题是您犯了尝试使用主机 CPU 时间测量 OpenCL (GPU) 时间的初学者错误。它永远行不通。您必须通过 OpenCL 事件分析来测量 GPU 时间。 Here's怎么做。
我无法理解为什么在这些示例中复制速度差异如此之大。我从他们那里得到了几乎无关紧要的图像。 'faster' 变体
的计算时间也更快没有帮助: 将'slow'变体的所有变量移到内核中,各种内存标志几乎不改变结果。
原来是内核的问题,到底是什么问题?
警告!我粘贴了整个文件
import pyopencl as cl
import numpy as np
from PIL import Image
import time
更快的变体。从缓冲区复制需要 ~0.15s
width = 800
height = 800
X = 0
Y = 0
R = 2
maxiter = 80000
xmin = X - R
xmax = X + R
ymin = Y - R
ymax = Y + R
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
r1 = np.linspace(xmin, xmax, width, dtype=np.float64)
r2 = np.linspace(ymin, ymax, height, dtype=np.float64)
q = r1 + r2[:, None] * 1j
q = np.ravel(q)
output = np.empty(width*height, dtype=np.uint8)
mf = cl.mem_flags
q_opencl = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=q)
output_opencl = cl.Buffer(ctx, mf.WRITE_ONLY, output.nbytes)
prg = cl.Program(ctx, """
__kernel void mandelbrot(__global double2 *q,
__global uchar *output, ushort const maxiter)
{
int gid = get_global_id(0);
double nreal, real = 0;
double imag = 0;
output[gid] = 0.0;
int curiter = 0;
for(curiter = 0; curiter < maxiter; curiter++) {
nreal = real*real - imag*imag + q[gid].x;
imag = 2* real*imag + q[gid].y;
real = nreal;
if (real*real + imag*imag > 4.0f){
break;
}
}
if (curiter < maxiter) {
output[gid] = curiter*64;
}
}
""").build()
prg.mandelbrot(queue, output.shape, None, q_opencl, output_opencl, np.uint16(maxiter))
t0 = time.time()
cl.enqueue_copy(queue, output, output_opencl).wait()
print(time.time()-t0, 'copy')
output = output.reshape((width, height))
较慢的变体。从缓冲区复制需要 ~0.78s
size = (800, 800)
X = 0
Y = 0
R = 2
maxiter = 80000
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
output = np.empty(size[0]*size[1], dtype=np.uint8)
mf = cl.mem_flags
output_cl = cl.Buffer(ctx, mf.WRITE_ONLY, output.nbytes)
prg = cl.Program(ctx, """
__kernel void mandelbrot(
__global uchar *out,
int width,
int height,
double real,
double imag,
double const radius,
int const maxiter) {
int id = get_global_id(0);
int i = id % width;
int j = id / width;
double window_radius = (width < height) ? width : height;
double x0 = real + radius * (2 * i - (float)width) / window_radius;
double y0 = imag - radius * (2 * j - (float)height) / window_radius;
double x = 0;
double y = 0;
int n = 0;
double x_temp = 0;
for(n = 0; n < maxiter; n++)
{
x_temp = x*x - y*y + x0;
y = 2 * x*y + y0;
x = x_temp;
if (x*x + y*y > 4.0f){
break;
}
}
if (n < maxiter) {
out[id] = n*64;
}
else {
out[id] = 0;
}
}
""").build()
prg.mandelbrot(queue, output.shape, None,
output_cl,
np.int32(size[0]),
np.int32(size[1]),
np.float64(X),
np.float64(Y),
np.float64(R),
np.int32(maxiter),
)
t0 = time.time()
cl.enqueue_copy(queue, output, output_cl).wait()
print(time.time() - t0, 'copy')
output = output.reshape((size[1], size[0]))
在第一个版本中,您将 80000 赋给 maxiter,它是无符号短整数。最大 65535。溢出并环绕到 14k ish 值。
第二个版本是 32 位的 int。 80k 正确通过。
14.5k / 80.0k 次迭代
0.15 / 0.78 秒
一致
I cannot understand the reason why the copy speed varies so much in these examples.
原因是:您没有测量复制命令的时间。
你说 "prg.mandelbrot() executes the kernel and do all the calculations" - 它不是那样做的。它 排队 内核。然后你排队复制命令,然后你调用wait()
。有些实现在入队后立即开始执行,但有些直到您调用 clFinish/clFlush/clWaitForEvents
才开始执行(最后一个是 PyOpenCL 的 Event.wait()
所做的 - 在您的代码中,cl.enqueue_copy()
returns 一个事件).
问题是您犯了尝试使用主机 CPU 时间测量 OpenCL (GPU) 时间的初学者错误。它永远行不通。您必须通过 OpenCL 事件分析来测量 GPU 时间。 Here's怎么做。