Я пытаюсь распараллелить мой последовательный код на C и разгрузить его на GPU NVIDIA с OpenACC (компилятор PGI)
Мой код написан как последовательный код. И часто вызывать очень длинные функции, как показано ниже.
int main()
{
// blah blah...
for(i=0; i<10; i++)
{
for(j=0; j<20; j++)
{
big_function(a,b,c);
}
}
// blah blah...
}
int big_function(a,b,c)
{
small_function_1(a);
small_function_2_with_data_dependencies(b);
}
В таком случае, big_function () можно распараллелить и запустить на GPU?
Я объявил весь цикл для парализованной области, используя #pragma acc kernels . как ниже.
#pragma acc routine
int big_function(int a, int b, int c);
#pragma acc routine
int small_function_1(int a);
#pragma acc routine
int small_function_2_with_data_dependencies(int b);
int main()
{
// blah blah...
#pragma acc data ~~~~
#pragma acc kernels
for(i=0; i<10; i++)
{
for(j=0; j<20; j++)
{
big_function(a,b,c);
}
}
// blah blah...
}
int big_function(a,b,c)
{
small_function_1(a);
small_function_2_with_data_dependencies(b);
}
Но скомпилированный файл занимает очень много времени, чтобы закончить. И результат был не верным.
Могу ли я использовать OpenACC для распараллеливания последовательного кода, который использует много вызовов функций?
Или я должен разбить и разделить big_function () на мелкие детали?
Вам нужно будет украсить каждую функцию по дереву вызовов с помощью acc routine
директива, как вы сделали в своем примере. Если вы ожидаете, что весь параллелизм будет исходить от циклов на верхнем уровне, то вы захотите, чтобы все подпрограммы были помечены как последовательные (seq
). Пока вы это сделали, компилятор должен быть в состоянии построить его для графического процессора. Вполне вероятно, что вы получите низкую производительность, поскольку такие большие деревья вызовов функций, как правило, содержат много состояний, которые разъедают ресурсы GPU, разделяемую память и регистры в частности. Вы, вероятно, обнаружите, что он будет работать намного лучше на графических процессорах, если вы будете перемещать параллелизм вниз по дереву вызовов, но это может отрицательно повлиять на производительность процессора и, возможно, увеличить использование памяти, поскольку вам придется экономить данные, которые были ранее доступно как состояние потока.
Если вы можете предоставить больше информации о реальном коде, я постараюсь помочь вам отладить проблемы с корректностью. Вы должны проверить обратную связь компилятора (-Minfo
) и убедитесь, что компилятор делает то, что вы думаете, он делает. Возможно, вы обнаружите, что его вызывает дерево вызовов. Вы также можете попробовать форумы PGI, так как они часто очень быстро реагируют на запросы помощи.
Это зависит от глубины вашего calltree. Как сказал Джеффларкин, acc routine
может помочь вам, но это только так далеко. В общем, эти подпрограммы должны быть встроены для создания большого ядра. GPU на самом деле не созданы для работы со сложными ядрами с тысячами строк кода — то есть, даже если он будет работать, будет сложно добиться его производительности.
Способ сделать это в более сложном случае — приватизировать ваш callgraph (который, я полагаю, является физической параметризацией некоторого моделирования) в i, j-доменах. То есть вместо того, чтобы вычислять все для одного столбца или точки поверхности, вы передаете данные более высокого измерения в свои подпрограммы, так что вы можете распараллеливать меньшие порции в i, j.
Sidenote: для Fortran 90+ я построил инструмент это делает распараллеливание для вас, но я боюсь, что он не поддерживает C ++. Возможно, это вдохновит вас на решение для предварительной обработки. В моем случае мне нужно было сохранить производительность процессора, что может сказаться на предложенном выше решении, но в вашем случае это может не применяться.