[Из песочницы] Behind the scene of TOP-1 supercomputer

Это история о том, как мы замедляли ускоряли расчеты на самом мощном суперкомпьютере в мире.


59e3cb7c31ae9527942627.jpeg


В апреле этого года наша команда приняла участие в финале Asia supercomputer challenge 2017, одним из заданий которого было ускорение программы для моделирования океанских волн Masnum-Wave на китайском суперкомпьютере Sunway TaihuLight.


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


Вычислительные узлы выставлены в форме двух овалов, между которыми стоит сетевое железо. В узлах используются процессоры Shenwei 26010. Каждый из них состоит из 4 гетерогенных процессорных групп, которые включают одно управляющее и 64 вычислительных ядра с тактовой частотой 1,45 ГГц и размером локального кэша 64 Кб.


59e51faed2e1c592022843.jpeg


Охлаждается все это с помощью водяной системы, расположенной в отдельном здании.


Из ПО в нашем распоряжении были компиляторы фортрана и си с «поддержкой» OpenACC 2 и athreads (аналог POSIX Threads) и планировщик задач, совмещающий в себе одновременно возможности обычного планировщика и mpirun.


Доступ к кластеру осуществлялся через особый VPN, плагины для работы с которым были доступны только для Windows и Mac OS. Все это добавляло особенного шарма при работе.


Заданием было ускорение Masnum-Wave на этом суперкомпьютере. Нам предоставили исходный код Masnum-Wave, несколько крохотных readme файлов с описанием основ работы на кластере и данные для замера ускорения.


Masnum-Wave — это программа для моделирования движения волн по всему земному шару. Она написана на Фортране с использованием MPI. В двух словах, она итеративно выполняет следующее: читает данные функций внешнего воздействия, синхронизирует граничные области между MPI-процессами, вычисляет продвижение волн и сохраняет результаты. Нам дали workload на 8 модельных месяцев с шагом по 7.5 минуты.


В первый же день мы нашли в интернете статью: «The Sunway TaihuLight supercomputer:
system and applications, Haohuan Fu» с описанием ускорения Masnum-Wave на архитектуре Sunway TaihuLight с помощью конвейерной обработки. Авторы статьи использовали всю мощь кластера (10649600 ядер), в нашем же распоряжении было 64 вычислительных группы (4160 ядер).


Masnum-Wave состоит из нескольких модулей:


59e518bc23229924305229.png


Мы впервые столкнулись с таким большим количеством кода, порожденного наукой. Так как код представляет собой смесь двух версий фортрана 90 и 77, это порой ломало работу их собственного компилятора. В большом количестве там встречаются чудесные конструкции goto, куски закомментированного кода и, конечно же, комментарии на китайском.


Сокращенный пример кода для наглядности:


do 1201 j=1,jl
        js=j
        j11=jp1(mr,j)
        j12=jp2(mr,j)
        j21=jm1(mr,j)
        j22=jm2(mr,j)

!****************************************************************

!      do 1202 ia=ix1,ix2
!      do 1202 ic=iy1,iy2

        eij=e(ks,js,ia,ic)
        !if (eij.lt.1.e-20) goto 1201
        if (eij.lt.1.e-20) cycle
        ea1=e(kp ,j11,ia,ic)
        ea2=e(kp ,j12,ia,ic)
! ...
        eij2=eij**2
        zua=2.*eij/al31
        ead1=sap/al11+sam/al21
        ead2=-2.*sap*sam/al31
        !      fcen=fcnss(k,ia,ic)*enh(ia,ic)
        fcen=fconst0(k,ia,ic)*enh(ia,ic)
        ad=cwks17*(eij2*ead1+ead2*eij)*fcen
        adp=ad/al13
        adm=ad/al23
        delad =cwks17*(eij*2.*ead1+ead2) *fcen
        deladp=cwks17*(eij2/al11-zua*sam)*fcen/al13
        deladm=cwks17*(eij2/al21-zua*sap)*fcen/al23

        !*      nonlinear transfer

        se(ks ,js )= se(ks ,js )-2.0*ad
        se(kp2,j11)= se(kp2,j11)+adp*wp11
        se(kp2,j12)= se(kp2,j12)+adp*wp12
        se(kp3,j11)= se(kp3,j11)+adp*wp21
        se(kp3,j12)= se(kp3,j12)+adp*wp22
        se(im ,j21)= se(im ,j21)+adm*wm11
        se(im ,j22)= se(im ,j22)+adm*wm12
        se(im1,j21)= se(im1,j21)+adm*wm21
        se(im1,j22)= se(im1,j22)+adm*wm22

!...

!        1202      continue
!        1212      continue
      1201      continue


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


Изучив скудную документацию к китайским компиляторам, мы решили использовать OpenACC, так как это стандарт от Nvidia с примерами и спецификацией. К тому же, код из readme к athreads казался нам неоправданно сложным и просто не компилировался. Как же мы ошибались.


Одна из первых идей, которая приходит в голову при оптимизации кода на ускорителях — это использование локальной памяти. В OpenACC это можно сделать несколькими директивами, но результат всегда должны быть один: данные перед началом вычислений должны быть скопированы на локальную память.


Для проверки и выбора нужной директивы мы написали на фортране несколько тестовых программ, на которых убедились, что они работают и что можно таким образом получить ускорение. Далее мы расставили эти директивы в Masnum-Wave, указав им имена наиболее используемых переменных. После компиляции они стали появляться в логах, сопутствующие надписи на китайском не были подсвечены красным, и мы посчитали, что все скопировалось и работает.


59e518bde9819903986824.jpeg


Но оказалось, что не все так просто. Компилятор OpenACC не копировал массивы в Masnum-Wave, но работал исправно с тестовыми программами. Проведя пару дней с Google Translate мы поняли, что он не копирует объекты, которые определены в файлах, подключаемых через директивы препроцессора (include)!


Всю следующею неделю мы переносили код Masnum-Wave из подключаемых файлов (а их больше 30) в файлы с исходным кодом, при этом нужно было убедиться, что все определятся и линкуется в правильном порядке. Но, так как ни у кого из нас не было опыта работы с Фортраном, и все сводилось к «давайте жахнем и посмотрим, что получится», то без замены некоторых основных функций на костыльные версии тут тоже не обошлось.


И вот, когда все модули были перелопачены, и мы, в надежде получить свое мизерное ускорение, запустили свежескомилированный код, то получили очередную порцию разочарования! Директивы, написанные по всем канонам стандарта OpenACC 2.0, выдают ошибки в runtime. В этот момент в наши головы начала закрадываться идея, что этот чудесный суперкомпьютер поддерживает какой-то свой особый стандарт.


Тогда мы попросили у организаторов соревнований документацию, и с третьей попытки они нам ее предоставили.


59e518be493a2333345777.png


Пару часов с Google Translate подтвердили наши опасения: стандарт, который они поддерживают, называется OpenACC 0.5, и он кардинально отличается от OpenACC 2.0, поставляемого с pgi компилятором.


Например, основной нашей задумкой было переиспользование данных на ускорителе. Для выполнения этого в стандарте 2.0 необходимо обернуть параллельный код в блок data. Вот как это делается в примерах от Nvidia:


!$acc data copy(A, Anew)
 do while ( error .gt. tol .and. iter .lt. iter_max )
   error=0.0_fp_kind

!$omp parallel do shared(m, n, Anew, A) reduction( max:error )
!$acc kernels
   do j=1,m-2
do i=1,n-2
 Anew(i,j) = 0.25_fp_kind * ( A(i+1,j  ) + A(i-1,j  ) + &
A(i  ,j-1) + A(i  ,j+1) )
 error = max( error, abs(Anew(i,j)-A(i,j)) )
end do
   end do
!$acc end kernels
!$omp end parallel do

   if(mod(iter,100).eq.0 ) write(*,'(i5,f10.6)'), iter, error
   iter = iter +1

!$omp parallel do shared(m, n, Anew, A)
!$acc kernels
   do j=1,m-2
do i=1,n-2
 A(i,j) = Anew(i,j)
end do
   end do
!$acc end kernels
!$omp end parallel do

 end do
!$acc end data


Но на кластере этот код не скомпилируется, потому что в их стандарте эта операция делается через указание индекса для каждого блока данных:


#include 
#include 

#define NN 128

int A[NN],B[NN],C[NN];

int main()
{
    int i;
    for (i=0;i


Коря себя за выбор OpenACC, мы все же продолжили работу, так как времени оставалось всего пару дней. В последний день отборочного тура нам наконец-то удалось запустить наш «ускоренный» код. Мы получили замедление в 3.5 раза. Нам ничего не оставалось, кроме как написать в отчете к заданию все, что мы думаем о их реализации OpenACC в цензурной форме. Не смотря на это, мы получили множество позитивных эмоций. Когда еще придется удаленно отлаживать код на самом мощном компьютере в мире?


P.S.: В результате мы все же прошли в финальную часть конкурса и съездили в Китай.
Последним заданием финала была презентация с описанием решений. Лучшего результата добилась местная команда, которая написала свою библиотеку на Си c использованием athread т.к. OpenACC, по их словам, не работает.

© Habrahabr.ru