#openacc
#openacc
Вопрос:
Оригинальный вопрос:
Я относительно новичок в OpenACC, но до сих пор мне удавалось с относительным успехом ускорить мой набор итеративных решателей Fortran (на основе CG), и я получаю ускорение около 7 на моей видеокарте Nvidia RTX GeForce. Все работает нормально, если предварительное кондиционирование не используется или используется диагональное предварительное кондиционирование. Но проблемы начинаются, если я хочу ускорить немного более сложные предварительные условия — inclomplete LDL ^ T является моим любимым.
Фрагмент кода, который выполняет неполную факторизацию LDL ^ T, выглядит следующим образом:
20 do i = 1, n ! browse through rows
21 sum1 = a % val(a % dia(i)) ! take diagonal entry
22 do j = a % row(i), a % dia(i)-1 ! browse only lower triangular part
23 k = a % col(j) ! fetch the column
24 sum1 = sum1 - f % val(f % dia(k)) * a % val(j) * a % val(j)
25 end do
26
27 ! Keep only the diagonal from LDL decomposition
28 f % val(f % dia(i)) = 1.0 / sum1
29 end do
Этот фрагмент кода по своей сути является последовательным. Просматривая строки, я использую факторизацию, выполненную в предыдущих строках, поэтому с самого начала ее нелегко распараллелить. Единственный способ, который мне удалось найти, чтобы скомпилировать его с помощью директивы OpenACC, и имея в виду его последовательный характер, это:
20 !$acc parallel loop seq amp;
21 !$accamp; present(a, a % row, a % col, a % dia, a % val) amp;
22 !$accamp; present(f, f % row, f % col, f % dia, f % val)
23 do i = 1, n ! browse through rows
24 sum1 = a % val(a % dia(i)) ! take diagonal entry
25 !$acc loop vector reduction( :sum1)
26 do j = a % row(i), a % dia(i)-1 ! browse only lower triangular part
27 k = a % col(j) ! fetch the column
28 sum1 = sum1 - f % val(f % dia(k)) * a % val(j) * a % val(j)
29 end do
30
31 ! Keep only the diagonal from LDL decomposition
32 f % val(f % dia(i)) = 1.0 / sum1
33 end do
Хотя эти директивы OpenACC сохраняют вычисления на графических процессорах и дают те же результаты, что и вариант без графического процессора, те из вас, кто более опытен, чем я, уже увидят, что здесь не так много параллелизма и ускорения. Внешний цикл ( i
, через строки) является последовательным, а внутренний цикл ( j
и k
) просматривает только несколько элементов.
В целом, графический вариант неполного LDL ^ T-CG-решателя с предварительной обработкой в несколько раз медленнее, чем версия без GPU.
У кого-нибудь есть идея, как обойти это? Я понимаю, что это может быть далеко не тривиально, но, возможно, по этому вопросу уже была проделана некоторая работа, о которой я не знаю, или, может быть, есть лучший способ использовать OpenACC.
Обновление через пару дней
Я сделал свою домашнюю работу, прочитал несколько статей на сайте Nvidia и за его пределами, и да, алгоритм факторизации действительно последовательный, и, похоже, вопрос о том, как это сделать на графических процессорах, остается открытым. В этом недавнем блоге: https://docs.nvidia.com/cuda/incomplete-lu-cholesky/index.html автор использует cuBLAS и cuSPARSE для CG, но все еще выполняет факторизацию на CPU. Среднее ускорение, о котором он сообщает, составляет около двух, что, смею сказать, немного разочаровывает.
Итак, я решил обойти проблему, раскрасив строки матрицы и выполнив повторную факторизацию цвет за цветом, вот так:
21 n_colors = 8
22
23 do color = 1, n_colors
24
25 c_low = (color-1) * n / n_colors 1 ! color's upper bound
26 c_upp = color * n / n_colors ! color's lower bound
27
28 do i = c_low, c_upp ! browse your color band
29 sum1 = a % val(a % dia(i)) ! take diagonal entry
30 do j = a % row(i), a % dia(i)-1 ! only lower traingular
31 k = a % col(j)
32 if(k >= c_low) then ! limit entries to your color
33 sum1 = sum1 - f % val(f % dia(k)) * a % val(j) * a % val(j)
34 end if
35 end do
36
37 ! This is only the diagonal from LDL decomposition
38 f % val(f % dia(i)) = 1.0 / sum1
39 end do
40 end do ! colors
В строке 32 я ограничиваю записи матрицы только цветом, принадлежащим его циклу. Очевидно, что, поскольку неполнота факторизованной матрицы более выражена (пренебрегается большим количеством записей), свойства сходимости хуже, но все же лучше, чем при простом диагональном предобуславливателе.
Я сделал следующее с OpenACC:
21 n_colors = 8
22
23 !$acc parallel loop num_gangs(8) tile(8) ! do each tile in its own color
24 !$accamp; present(a, a % row, a % col, a % dia, a % val) amp;
25 !$accamp; present(f, f % row, f % col, f % dia, f % val)
26 do color = 1, n_colors
27
28 c_low = (color-1) * n / n_colors 1 ! color's upper bound
29 c_upp = color * n / n_colors ! color's lower bound
30
31 !$acc loop seq ! inherently sequential
32 do i = c_low, c_upp ! browse your color band
33 sum1 = a % val(a % dia(i)) ! take diagonal entry
34 !$acc loop vector reduction( :sum1)
35 do j = a % row(i), a % dia(i)-1 ! only lower traingular
36 k = a % col(j)
37 if(k >= c_low) then ! limit entries to your color
38 sum1 = sum1 - f % val(f % dia(k)) * a % val(j) * a % val(j)
39 end if
40 end do
41
42 ! This is only the diagonal from LDL decomposition
43 f % val(f % dia(i)) = 1.0 / sum1
44 end do
45 end do ! colors
Результаты, которые я получаю от OpenACC variant, идентичны результатам только для CPU, просто производительность на графических процессорах все еще намного ниже. Тем не менее, кажется, что tile
директива работает так, как я ожидал, каждая группа, похоже, работает над своим собственным цветом, поскольку результаты, которые я получаю от графических процессоров, идентичны.
Есть какие-либо советы о том, как повысить производительность? Я делаю что-то совершенно глупое в приведенном выше коде? (Профилировщик показывает, что вычисления привязаны к GPU, поскольку вся система работает на GPU, но производительность действительно низкая.)
С наилучшими пожеланиями
Ответ №1:
Не уверен, что это поможет, но в этой статье описывается метод решения CG на графических процессорах. Он использует cuSPARSE и CUDA для реализаций, но вы могли бы получить идеи, которые можно было бы применить к вашему коду.
https://www.dcs.warwick.ac.uk/pmbs/pmbs14/PMBS14/Workshop_Schedule_files/8-CUDAHPCG.pdf