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

Основное приложение GPU, целочисленные вычисления

Короче говоря, я сделал несколько прототипов интерактивного программного обеспечения. Теперь я использую pygame (python sdl wrapper), и все делается на CPU. Я начинаю переносить его на C сейчас и в то же время искать существующие возможности использовать некоторые возможности графического процессора для включения процессора из избыточных операций. Однако я не могу найти хороший "ориентир", какую точную технологию/инструменты я должен выбрать в своей ситуации. Я просто прочитал множество документов, он очень быстро истощает мои умственные способности. Я не уверен, что это вообще возможно, поэтому я озадачен. Здесь я сделал очень грубый эскиз моего типичного скелета приложения, который я разрабатываю, но учитывая, что он теперь использует GPU (заметьте, у меня почти нулевые практические знания о программировании GPU). Все еще важно то, что типы данных и функциональность должны быть точно сохранены. Вот он:
enter image description here

Таким образом, F (A, R, P) - это некоторая пользовательская функция, например замещение элемента, повторение и т.д. Функция, по-видимому, постоянна в времени жизни программы, формы прямоугольника обычно не равны с формой A, расчет места. Поэтому они просто генерируются с моими функциями. Примеры F: повторять строки и столбцы A; подставлять значения со значениями из таблиц замещения; составьте некоторые плитки в один массив; любая математическая функция для значений A и т.д. Как сказано, все это можно легко сделать на CPU, но приложение должно быть действительно гладким. BTW в чистом Python стал просто непригодным после добавления нескольких визуальных функций, основанных на массивах numpy. Cython помогает делать быстрые пользовательские функции, но тогда исходный код уже является своего рода салатом.

Вопрос:

  • Отражает ли эта схема некоторые (стандартные) технологии /dev.tools?

  • Является ли CUDA тем, что я ищу? Если да, некоторые ссылки/примеры , которые совпадают с с моей структурой приложения, были бы замечательными.

Я понимаю, это большой вопрос, поэтому я расскажу подробнее, если это поможет.


Обновить

Вот конкретный пример двух типичных вычислений для моего прототипа редактора растровых изображений. Поэтому редактор работает с индексами, а данные включают слои с соответствующими битовыми масками. Я могу определить размер слоев и масок того же размера, что и слои, и, скажем, все слои имеют одинаковый размер ( 1024 ^ 2 пикселя= 4 МБ для 32-битных значений). И моя палитра говорит: 1024 элемента (4 Килобита для 32-битного формата).
Подумайте, что я хочу сделать сейчас две вещи:

Шаг 1. Я хочу сгладить все слои в одном. Скажем, A1 - слой по умолчанию (фон), а слои "A2" и "A3" имеют маски "m2" и "m3". В python я бы написал:

from numpy import logical_not
...
Result = (A1 * logical_not(m2) + A2 * m2) * logical_not(m3) + A3 * m3

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

Шаг 2. Теперь у меня есть массив и вы хотите "раскрасить" его с помощью некоторой палитры, так что это будет моя таблица поиска. Как я вижу сейчас, существует проблема с одновременным чтением элемента таблицы поиска.  enter image description here

Но моя идея: возможно, можно просто дублировать палитру для всех блоков, чтобы каждый блок мог читать свою собственную палитру? Как это: enter image description here

4b9b3361

Ответ 1

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

Я бы сказал, что эта проблема будет решена только на GPU, если выполняются два условия:

  • Размер A[] оптимизирован, чтобы сделать время передачи несущественным (смотрите, http://blog.theincredibleholk.org/blog/2012/11/29/a-look-at-gpu-memory-transfer/).

  • Таблица поиска не слишком велика и/или значения поиска организованы таким образом, что кеш можно максимально использовать, в общем случае случайные поисковые запросы на графическом процессоре могут быть медленными, в идеале вы можете предварительно загрузить R[] в буфере общей памяти для каждого элемента буфера A[].

Если вы можете ответить на оба этих вопроса положительно, тогда и только потом подумайте о том, чтобы использовать GPU для своей проблемы, иначе эти 2 фактора превысят вычислительное ускорение, которое может предоставить вам GPU.

Еще одна вещь, на которую вы можете обратить внимание, - это как можно лучше перекрывать время передачи и вычислений, чтобы максимально скрыть медленные скорости передачи данных CPU- > GPU.

Что касается вашей функции F(A, R, P), вам необходимо убедиться, что вам не нужно знать значение F(A, R, P)[0], чтобы узнать, что такое значение F(A, R, P)[1], потому что если вы это сделаете, вам нужно переписать F(A, R, P), чтобы обойти эту проблему, используя некоторую методику параллелизации. Если у вас ограниченное число функций F(), это можно решить, написав параллельную версию каждой функции F() для использования графическим процессором, но если F() определяется пользователем, ваша проблема становится немного сложнее.

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

ИЗМЕНИТЬ

Прочитав ваше редактирование, я бы сказал "да". Палитра может входить в общую память (см. Размер общей памяти GPU очень мал - что я могу сделать с этим?), что очень быстро, если у вас есть несколько палитру, вы можете поместиться 16 КБ (размер разделяемой памяти на большинстве карт)/4 КБ на палитру = 4 палитры на блок потоков.

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

Ответ 2

Когда ваш код сильно параллелен (т.е. между этапами обработки есть небольшие или никакие зависимости от данных), вы можете перейти на CUDA (более мелкомасштабный контроль над синхронизацией) или OpenCL (очень похожий и переносимый API с OpenGL-интерфейсом для взаимодействия с GPU для обработки ядра). Большая часть работы по ускорению, которую мы делаем, происходит в OpenCL, которая отлично взаимодействует как с OpenGL, так и с DirectX, но у нас также есть одна и та же настройка, работающая с CUDA. Одна большая разница между CUDA и OpenCL заключается в том, что в CUDA вы можете скомпилировать ядра один раз и задерживать (и/или связывать) их в своем приложении, тогда как в OpenCL компилятор отлично справляется с стеком драйверов OpenCL, чтобы обеспечить компиляцию ядра, когда приложение запускается.

Один из вариантов, который часто упускается из виду при использовании Microsoft Visual Studio, - это С++ AMP, С++-синтаксический и интуитивно понятный api для тех, кто не хочет вникать в логические завивки и повороты API OpenCL/CUDA API, Большим преимуществом здесь является то, что код также работает, если у вас нет GPU в системе, но тогда у вас не так много вариантов настройки производительности. Тем не менее, во многих случаях это быстрый и эффективный способ написать доказательство вашего концептуального кода и повторное внедрение бит и частей в CUDA или OpenCL позже.

Блоки OpenMP и Thread Building - это только хорошие альтернативы, когда у вас проблемы с синхронизацией и множество зависимостей данных. Нативная потоковая обработка с использованием рабочих потоков также является жизнеспособным решением, но только если у вас есть хорошая идея о том, как настраивать точки синхронизации между различными процессами таким образом, чтобы потоки не изгоняли друг друга во время борьбы за приоритет. Это намного сложнее добиться, и инструменты, такие как Parallel Studio, являются обязательными. Но тогда и NVIDIA NSight, если вы пишете код графического процессора.

Приложение:

Разрабатывается новая платформа под названием Quasar (http://quasar.ugent.be/blog/), которая позволяет писать ваши математические проблемы в синтаксисе, который очень похож к Matlab, но с полной поддержкой интеграции c/С++/С# или java и кросс-компиляции (LLVM, CLANG) вашего "ядрового" кода с любой базовой аппаратной конфигурацией. Он генерирует файлы Ptx CUDA или запускается на openCL или даже на вашем CPU с использованием TBB или их смеси. Используя несколько прозвищ, вы можете украсить алгоритм так, чтобы базовый компилятор мог вывести типы (вы также можете явно использовать строгую типизацию), поэтому вы можете оставить материал тяжелого типа полностью до компилятора. Справедливости ради отметим, что на момент написания этой статьи система по-прежнему была w.i.p. и первые скомпилированные программы OpenCL просто проверяются, но наиболее важным преимуществом является быстрое прототипирование с почти одинаковой производительностью по сравнению с оптимизированным cuda. ​​

Ответ 3

В OpenCL/CUDA нет большой разницы, поэтому выберите, который лучше работает для вас. Просто помните, что CUDA ограничит вас графическими процессорами NVidia.

Если я правильно понимаю вашу проблему, ядро ​​(функция, выполняемая на графическом процессоре) должна быть простой. Он должен следовать этому псевдокоду:

kernel main(shared A, shared outA, const struct R, const struct P, const int maxOut, const int sizeA)
  int index := getIndex() // get offset in input array
  if(sizeA >= index) return // GPU often works better when n of threads is 2^n
  int outIndex := index*maxOut // to get offset in output array
  outA[outIndex] := F(A[index], R, P)
end

Функции F должны быть встроены, и вы можете использовать переключатель или, если для другой функции. Поскольку неизвестный размер вывода F, вы должны использовать больше памяти. Каждый экземпляр ядра должен знать позиции для правильной записи и чтения в памяти, поэтому должен быть некоторый максимальный размер (если их нет, то это все бесполезно, и вам нужно использовать CPU!). Если разные размеры являются разреженными, то я бы использовал что-то вроде вычисления этих разных размеров после того, как массив возвращался в ОЗУ и вычислял их немного с процессором, заполняя А некоторыми нулями или значениями индикации.

Размеры массивов - это, очевидно, длина (A) * maxOut = length (outA).

Я забыл упомянуть, что если выполнение F не является таким же в большинстве случаев (тот же исходный код), что и GPU будет его сериализовать. Многопроцессоры GPU имеют несколько ядер, подключенных к одному кэшу команд, поэтому ему придется сериализовать код, который не является одинаковым для всех ядер! OpenMP или потоки - лучший выбор для такого рода проблем!