Подтвердить что ты не робот

Многопроцессор Python с помощью PyCUDA

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

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

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

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

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

UPDATE Спасибо Калоян за руководство в плане многопроцессорной области; если бы 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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Вам нужна многопоточная реализация встроенной функции 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.