Я пытаюсь выполнить задачу нелинейной оптимизации полностью на графическом процессоре. Вычисление целевой функции и передача данных из GPU в CPU являются узкими местами. Чтобы решить эту проблему, я хочу
- сильно распараллелить вычисление цели и
- выполнить всю оптимизацию на GPU.
В частности, проблема заключается в следующем в псевдокоде:
x = x0 // initial guess of the vector of unknowns, typically of size ~10,000
for iteration = 1 : max_iter
D = compute_search_direction(x)
alpha = compute_step_along_direction(x)
x = x + D * alpha // update
end for loop
Функции compute_search_direction(x)
и compute_step_along_direction(x)
обе вызывают целевую функцию f0(x)
десятки раз за итерацию. Целевая функция представляет собой сложное ядро CUDA, в основном это прямое моделирование Блоха (= система уравнений, которая описывает динамику ядерных спинов в магнитном поле c). Выходные данные f0(x)
- это F (значение целевой функции, скалярное значение) и DF (якобиан, или вектор первых производных, с таким же размером, что и x, то есть ~ 10000). На GPU f0(x)
действительно быстрый, но передача x из CPU в GPU, а затем передача обратно F и DF из GPU в CPU занимает некоторое время (всего ~ 1 секунда). Поскольку эта функция вызывается десятки раз за одну итерацию, это приводит к довольно медленной общей оптимизации.
В идеале, я хотел бы иметь весь псевдокод выше на графическом процессоре. Единственное решение, которое я могу придумать сейчас, - это рекурсивные ядра. Приведенный выше псевдокод будет "внешним ядром", запущенным с числом потоков = 1 и числом блоков = 1 (т. Е. Это ядро на самом деле не параллельное ...). Затем это ядро будет вызывать целевую функцию (т. Е. «Внутреннее ядро», это массивно параллельное) каждый раз, когда ему нужно оценить целевую функцию и вектор первых производных. Поскольку запуск ядра выполняется асинхронно, я могу заставить графический процессор ждать, пока внутреннее ядро f0
не будет полностью оценено, чтобы перейти к следующей инструкции внешнего ядра (используя точку синхронизации).
В некотором смысле это действительно то же самое, что и обычное программирование CUDA, когда ЦПУ контролирует запуск ядра для оценки целевой функции f0
, за исключением того, что ЦП заменяется внешним ядром, которое не распараллелено (1 нить, 1 блок). Однако, поскольку все находится на графическом процессоре, задержки передачи данных больше нет.
Сейчас я проверяю идею на простом примере для проверки осуществимости. Однако это кажется довольно громоздким ... Мои вопросы:
- Имеет ли это какой-либо смысл для кого-либо еще?
- Есть ли более прямой способ достичь того же результата без добавлена сложность вложенных ядер?