Я использую PyOpenCl в сочетании с Python 3.7.
При вызове одного и того же ядра с несколькими процессами, каждый из которых имеет свой собственный контекст, указывающий на одно и то же устройство с графическим процессором, я получаю улучшения производительности, которые масштабируются почти линейно с числомпроцессов.
Я могу себе представить, что выполнение параллельных процессов делает возможным несколько перекрывающихся передач, когда ядро процесса A выполняется, а процесс B отправляет данные на графическую карту. Но это не должно быть причиной такого повышения производительности.
В приложении вы найдете пример кода, где я реализовал фиктивное приложение, в котором декодируются некоторые данные.
При установке n_processes = 1 Iпримерно 12 Мбит / с, а при установке n_processes = 4 я получаю 45 Мбит / с.
Я использую одну видеокарту AMD Radeon VII.
Есть кто-нибудь хорошее объяснение этогоявление?
Обновление: я профилировал скрипт, используя CodeXL. Похоже, что между исполнениями ядра тратится много времени, и его могут использовать несколько процессов.
import logging
import multiprocessing as mp
import pyopencl as cl
import pyopencl.array as cl_array
from mako.template import Template
import numpy as np
import time
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(process)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s')
kernelsource = """
float boxplus(float a,float b)
{
float boxp=log((1+exp(a+b))/(exp(a)+exp(b)));
return boxp;
}
void kernel test(global const float* in,
global const int* permutation_vector,
global float* out)
{
int gid = get_global_id(0);
int p = gid; // permutation index
float t = 0.0;
for(int k=1; k<10;k++){
p = permutation_vector[p];
t= boxplus(in[p],in[gid]);
}
out[gid] = t;
}
"""
class MyProcess(mp.Process):
def __init__(self, q):
super().__init__()
self.q = q
def run(self) -> None:
platform = cl.get_platforms()
my_gpu_devices = [platform[0].get_devices(device_type=cl.device_type.GPU)[0]]
ctx = cl.Context(devices=my_gpu_devices)
queue = cl.CommandQueue(ctx)
tpl = Template(kernelsource)
rendered_tp = tpl.render()
prg = cl.Program(ctx, str(rendered_tp)).build()
size = 100000 # shape of random input array
dtype = np.float64
output_buffer = cl_array.empty(queue, size, dtype=dtype)
input_buffer = cl_array.empty(queue, size, dtype=dtype)
permutation = np.random.permutation(size)
permutation_buffer = cl_array.to_device(queue, permutation.astype(np.int))
def decode(data_in):
input_buffer.set(data_in)
for i in range(10):
prg.test(queue, input_buffer.shape, None,
input_buffer.data,
permutation_buffer.data,
output_buffer.data)
queue.finish()
return output_buffer.get()
counter = 1
while True:
data_in = np.random.normal(size=size).astype(dtype)
data_out = decode(data_in)
if counter % 100 == 0:
self.q.put(size * 100)
counter = 1
else:
counter += 1
def run_test_multi_cpu_single_gpu():
q = mp.Queue()
n_processes = 4
for i in range(n_processes):
MyProcess(q).start()
t0 = time.time()
symbols_sum = q.get()
i = 0
while True:
i += 1
print('{} Mbit/sec'.format(1 / 1e6 * symbols_sum / (time.time() - t0 + 1e-15)))
symbols = q.get()
symbols_sum += symbols
if __name__ == '__main__':
run_test_multi_cpu_single_gpu()