Потоковая компиляция opencl

[Обновление:] Теперь я создаю несколько процессов, и это работает довольно хорошо, хотя основная проблема с потоками все еще существует. [/]

Я пытаюсь запустить программу на С++ (g++ 4.6.1), которая компилирует кучу ядер opencl. Большая часть времени проводится внутри clBuildProgram. (Это генетическое программирование, и на самом деле запуск кода и оценка пригодности выполняются намного быстрее.) Я пытаюсь выполнить компиляцию этих ядер и пока безуспешно. На данный момент нет общих данных между потоками (кроме одинаковой платформы и ссылки на устройство), но он будет запускать только один поток за раз. Я могу запустить этот код как несколько процессов (просто запуская их в разных окнах терминала в Linux), и тогда он будет использовать несколько ядер, но не в рамках одного процесса. Я могу использовать несколько ядер с одним и тем же базовым кодом многопоточности (std::thread) только с базовой математикой, поэтому я думаю, что это как-то связано либо с компиляцией opencl, либо с некоторыми статическими данными, о которых я забыл. :) Любые идеи? Я сделал все возможное, чтобы сделать это потокобезопасным, поэтому я в тупике.

Я использую AMD SDK (opencl 1.1, около 13.06.2010) и 5830 или 5850 для его запуска. SDK и g++ не так актуальны, как могли бы быть. В последний раз, когда я устанавливал более новый дистрибутив Linux, чтобы получить более новый g++, мой код работал на половинной скорости (по крайней мере, компиляции opencl), поэтому я вернулся. (Только что проверил код этой установки, и он работает на половинной скорости, по-прежнему без различий в многопоточности.) Кроме того, когда я сказал, что он запускает только один поток за раз, он запускает их все, а затем чередует два, пока они не закончатся, затем выполните следующие два и т. д. И похоже, что все потоки выполняются до тех пор, пока код не перейдет к сборке программы. Я не использую функцию обратного вызова в clBuildProgram. Я понимаю, что здесь многое может пойти не так, и трудно сказать без кода. :)

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

Вот как я запускаю потоки:

    for (a = 0; a < num_threads; a++) {
        threads[a] = std::thread(std::ref(programs[a]));        
        threads[a].detach();
        sleep(1);    // giving the opencl init f()s time to complete
    }

Вот где он увяз (и это все передаваемые локальные переменные, хотя идентификатор устройства будет таким же):

    clBuildProgram(program, 1, & device, options, NULL, NULL);

Кажется, не имеет значения, имеет ли каждый поток уникальный контекст или command_queue. Я действительно подозревал, что это была проблема, поэтому я упоминаю об этом. :)

Обновление: для этого будет работать создание дочерних процессов с помощью fork().


person starship    schedule 27.01.2013    source источник
comment
Я думаю, будет полезно, если вы также предоставите поставщика OpenCL, который вы используете, и аппаратное обеспечение, на которое вы ориентируетесь, поскольку у каждого поставщика есть собственная реализация компилятора OpenCL.   -  person Oak    schedule 27.01.2013
comment
Опять же, я понимаю, что на этот вопрос сложно ответить. Я надеюсь, что по крайней мере кто-то может сказать, что они сделали это успешно. Скорее всего, это глупая ошибка, но я только что потратил неделю на то, чтобы сделать свои вещи потокобезопасными, и я ищу подтверждение того, что это возможно. :)   -  person starship    schedule 27.01.2013
comment
Рассматривали ли вы просто запуск каждой компиляции в дочернем процессе? Накладные расходы выше, но если количество потоков достаточно велико, производительность все же может повыситься. Кроме того, если выполнение и оценка по-прежнему занимают значительное количество времени, но чуть меньше, чем компиляция, можете ли вы запустить мутацию и перекомпилировать во время выполнения, а затем выбрать из этого перекомпилированного поколения на основе пригодности?   -  person Tim    schedule 27.01.2013
comment
Запуск дочерних процессов звучит как действительно хорошая идея. Я читаю об этом (новичок в этом :)). Я мог бы делать компиляции различных популяций в одном потоке и запускать их после компиляции в другом. Я выбираю только около 5-10%, чтобы выжить, поэтому мне нужно подождать, пока ядра закончат работу, чтобы мутировать. Но да, разветвление кажется хорошей идеей, пока я могу передавать данные обратно.   -  person starship    schedule 27.01.2013
comment
Форкинг работает! Спасибо, @Тим! Теперь мне просто нужно выяснить часть передачи данных.   -  person starship    schedule 28.01.2013
comment
Нет проблем, я рад, что это сработало.   -  person Tim    schedule 28.01.2013
comment
Возможно, драйвер графического процессора выполняет арбитраж и позволяет только потоку одновременно получать доступ к графическому процессору/компилировать ядро. Используя процессы, вы можете ослабить это, но тогда процесс будет ждать, чтобы получить право собственности на устройство/драйвер.   -  person ipapadop    schedule 29.01.2013
comment
@ipapadop, похоже, это то, что происходит с потоками, но он выполняет параллельную компиляцию с процессами. Опять же, я не уверен, что правильно его нарезаю. Я просто старался изо всех сил и потерпел неудачу. :)   -  person starship    schedule 01.02.2013


Ответы (1)


Возможно, вы захотите опубликовать что-нибудь на форуме поддержки AMD по этому поводу. Учитывая множество неудачных реализаций OpenGL в отношении согласованности потоков, которых требует спецификация, меня не удивит, что драйверы OpenCL все еще неоптимальны в этом смысле. Вместо этого они могли бы использовать идентификатор процесса для разделения данных, кто знает.

Если у вас есть рабочая многопроцессорная генерация, я предлагаю вам сохранить ее и сообщать результаты с помощью IPC. Либо вы можете использовать boost::ipc, у которого есть интересные способы использования сериализации (например, с boost::spirit для отражения структур данных). Или вы можете использовать posix-каналы или разделяемую память, или просто сбрасывать результаты компиляции в файлы и опрашивать каталог из вашего родительского процесса, используя boost::filesystem и итераторы каталогов...

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

person v.oddou    schedule 02.02.2013
comment
Попался на форумах. Теперь я работаю с процессами, в том числе могу обрабатывать ошибки сегментов, что является хорошей особенностью разветвления. Я использую каналы posix для связи. Последнее, что вы сказали об избежании необходимости создавать конвейерный сервер ... чтобы я не занимался обширным кодированием протокола (читай: отладкой) ... заставило меня усмехнуться. Я сделал это уже. :) Я еще не использовал boost из-за накладных расходов и сложности, хотя я знаю, что это полезно. Я просто реализую все сам в основном до сих пор. Это достаточно просто с примерами... и поучительно, так как все это для меня ново. - person starship; 11.02.2013