Почему несколько процессов, обращающихся к одному графическому процессору, повышают производительность? - PullRequest
0 голосов
/ 02 октября 2019

Я использую PyOpenCl в сочетании с Python 3.7.

При вызове одного и того же ядра с несколькими процессами, каждый из которых имеет свой собственный контекст, указывающий на одно и то же устройство с графическим процессором, я получаю улучшения производительности, которые масштабируются почти линейно с числомпроцессов.

Я могу себе представить, что выполнение параллельных процессов делает возможным несколько перекрывающихся передач, когда ядро ​​процесса A выполняется, а процесс B отправляет данные на графическую карту. Но это не должно быть причиной такого повышения производительности.

В приложении вы найдете пример кода, где я реализовал фиктивное приложение, в котором декодируются некоторые данные.

При установке n_processes = 1 Iпримерно 12 Мбит / с, а при установке n_processes = 4 я получаю 45 Мбит / с.

Я использую одну видеокарту AMD Radeon VII.

Есть кто-нибудь хорошее объяснение этогоявление?

Обновление: я профилировал скрипт, используя CodeXL. Похоже, что между исполнениями ядра тратится много времени, и его могут использовать несколько процессов.

enter image description here

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()
...