Многопроцессорная обработка Python с PyCUDA - PullRequest
16 голосов
/ 06 мая 2011

У меня есть проблема, которую я хочу разделить между несколькими устройствами CUDA, но я подозреваю, что моя текущая архитектура системы сдерживает меня;

Я настроил класс GPU с функциями, которые выполняют операции на GPU (странно, что).Эти операции выполняются в стиле

for iteration in range(maxval):
    result[iteration]=gpuinstance.gpufunction(arguments,iteration)

Я представлял, что для N устройств будет N gpuinstances, но я не знаю достаточно о многопроцессорной обработке, чтобы увидеть самый простой способ применения этого, чтобы каждое устройствоназначается асинхронно, и, как ни странно, несколько примеров, с которыми я сталкивался, давали конкретные демонстрации сопоставления результатов после обработки.

Может кто-нибудь дать мне какие-либо указатели в этой области?

ОБНОВЛЕНИЕ Спасибо, Калоян, за ваше руководство в области многопроцессорности;если бы CUDA не был именно тем камнем преткновения, я бы пометил вас как ответившего.Сожалею.

Очевидно, что для игры с этой реализацией класс gpuinstance инициировал устройство CUDA с import pycuda.autoinit Но это не сработало, выдавая ошибки invalid context, как только каждый (правильно распределенный) поток встречал cudaкоманда.Затем я попытался инициализировать вручную в конструкторе __init__ класса с помощью ...

pycuda.driver.init()
self.mydev=pycuda.driver.Device(devid) #this is passed at instantiation of class
self.ctx=self.mydev.make_context()
self.ctx.push()    

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

(я также реализовал деструктор для очистки pop/detach)

Проблема в том, что invalid context исключения все еще появляются, как только поток пытается коснуться CUDA.

Есть идеи, ребята?И благодаря этому далеко.Автоматические отклики для людей, работающих «банан» в свой ответ!: P

Ответы [ 2 ]

18 голосов
/ 06 мая 2011

Вам нужно сначала выстроить все свои бананы на стороне CUDA, а затем подумать о лучшем способе сделать это на Python [бесстыдное повторение, я знаю].

CUDA multiМодель GPU довольно проста до 4.0 - у каждого графического процессора есть свой собственный контекст, и каждый контекст должен быть установлен отдельным потоком хоста.Итак, идея в псевдокоде такова:

  1. Приложение запускается, процесс использует API для определения количества используемых GPUS (остерегайтесь таких вещей, как режим вычисления в Linux)
  2. Приложение запускает новый хостпоток на GPU, передавая идентификатор GPU.Каждый поток неявно / явно вызывает эквивалент cuCtxCreate (), передавая идентификатор GPU, которому он был присвоен
  3. Прибыль!

В Python это может выглядеть примерно так:

import threading
from pycuda import driver

class gpuThread(threading.Thread):
    def __init__(self, gpuid):
        threading.Thread.__init__(self)
        self.ctx  = driver.Device(gpuid).make_context()
        self.device = self.ctx.get_device()

    def run(self):
        print "%s has device %s, api version %s"  \
             % (self.getName(), self.device.name(), self.ctx.get_api_version())
        # Profit!

    def join(self):
        self.ctx.detach()
        threading.Thread.join(self)

driver.init()
ngpus = driver.Device.count()
for i in range(ngpus):
    t = gpuThread(i)
    t.start()
    t.join()

Это предполагает, что безопасно просто установить контекст без какой-либо предварительной проверки устройства.В идеале вы должны проверить режим вычислений, чтобы убедиться в его безопасности, а затем использовать обработчик исключений, если устройство занято.Но, надеюсь, это дает основную идею.

3 голосов
/ 06 мая 2011

Что вам нужно, так это многопоточная реализация встроенной функции map. Здесь - это одна реализация.Что с небольшой модификацией в соответствии с вашими конкретными потребностями вы получите:

import threading

def cuda_map(args_list, gpu_instances):

    result = [None] * len(args_list)

    def task_wrapper(gpu_instance, task_indices):
        for i in task_indices:
            result[i] = gpu_instance.gpufunction(args_list[i])

    threads = [threading.Thread(
                    target=task_wrapper, 
                    args=(gpu_i, list(xrange(len(args_list)))[i::len(gpu_instances)])
              ) for i, gpu_i in enumerate(gpu_instances)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    return result

Это более или менее то же, что и у вас выше, с большой разницей в том, что вы не тратите время на ожиданиеза каждое отдельное завершение gpufunction.

...