Научная статья на тему 'РАСПАРАЛЛЕЛИВАНИЕ РЕКУРРЕНТНЫХ ЦИКЛОВ С ПРЕДВАРИТЕЛЬНЫМ ВЫЧИСЛЕНИЕМ СУПЕРПОЗИЦИЙ'

РАСПАРАЛЛЕЛИВАНИЕ РЕКУРРЕНТНЫХ ЦИКЛОВ С ПРЕДВАРИТЕЛЬНЫМ ВЫЧИСЛЕНИЕМ СУПЕРПОЗИЦИЙ Текст научной статьи по специальности «Математика»

CC BY
80
13
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
РЕКУРРЕНТНЫЕ ЦИКЛЫ / ЧИСЛЕННЫЕ МЕТОДЫ / ПАРАЛЛЕЛЬНЫЕ ВЫЧИСЛЕНИЯ / ПРЕОБРАЗОВАНИЯ ПРОГРАММ / РЕКУРРЕНТНЫЕ ПОСЛЕДОВАТЕЛЬНОСТИ / RECURRENT LOOPS / NUMERICAL METHODS / PARALLEL COMPUTATION / PROGRAM TRANSFORMATIONS / RECURRENT SEQUENCES

Аннотация научной статьи по математике, автор научной работы — Штейнберг Олег Борисович

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

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

PARALLELIZATION OF RECURRENT LOOPS DUE TO THE PRELIMINARY COMPUTATION OF SUPERPOSITIONS

As a rule, sections of code that take a lot of time to compute are loops. Therefore, it is precisely on them that special efforts are directed when accelerating programs, in particular, through parallelization. The article describes the parallelization algorithm for loops calculating the elements of a recursively given sequence. The recurrent loops considered in the article cannot be directly parallelized. With the help of auxiliary transformations, they can sometimes be reduced to loops that allow parallel execution. Earlier, the author of the article published another algorithm for parallelizing loops that calculate the elements of a recursively given sequence. In modern processors, the execution time of arithmetic operations is an order of magnitude faster than reading the arguments of these operations from RAM. This article provides estimates of the complexity of accessing memory for the described algorithm. The parallel algorithm presented in the article is more efficient in accessing memory than the algorithm described by the author earlier.

Текст научной работы на тему «РАСПАРАЛЛЕЛИВАНИЕ РЕКУРРЕНТНЫХ ЦИКЛОВ С ПРЕДВАРИТЕЛЬНЫМ ВЫЧИСЛЕНИЕМ СУПЕРПОЗИЦИЙ»

УДК 510.57 DOI: 10.14529/mmp200305

РАСПАРАЛЛЕЛИВАНИЕ РЕКУРРЕНТНЫХ ЦИКЛОВ С ПРЕДВАРИТЕЛЬНЫМ ВЫЧИСЛЕНИЕМ СУПЕРПОЗИЦИЙ

О.Б. Штейнберг, Южный федеральный университет, г. Ростов-на-Дону, Российская Федерация

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

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

Ключевые слова: рекуррентные циклы; численные методы; параллельные вычисления; преобразования программ; рекуррентные последовательности.

Введение

Данная статья относится к теме преобразования программ [1-3]. Она посвящена новому преобразованию, способствующему распараллеливанию рекуррентных циклов. Некоторые работы, посвященные этой теме, имеют дело с циклами, вычисляющими сумму последовательности. Также встречаются работы посвященные распараллеливанию рекуррентных циклов с условными операторами [4]. Данная статья рассматривает новый алгоритм распараллеливания рекуррентных циклов, вычисляющих сами элементы рекуррентной последовательности.

Ранее автором был предложен другой алгоритм распараллеливания рекуррентных циклов [5-7], который является развитием алгоритма описанного в работе [8]. В работе [6] для него были найдены условия устойчивости. Описываемый в данной работе алгоритм отличается от предыдущего и по вычислительной сложности, и по обращениям к памяти, и по работе с памятью в процессе параллельного вычисления. В частности, при применении предыдущего алгоритма на архитектуре современных CPU возникали синхронизации при обращении к кэшу, замедляющие вычисления.

Циклы, как правило, являются самыми долго считаемыми участками кода в программах. Ввиду этого, именно на них направляются особые усилия для ускорения, в частности, через распараллеливание. Рекуррентные циклы непосредственно распараллелены быть не могут. Иногда, c помощью вспомогательных преобразований их можно привести к циклам, допускающим параллельное выполнение. Теме распараллеливающих преобразований программ посвящены работы [1,2]. Алгоритм, опи-

санный в данной работе, применим, в частности, к циклам с аффинной, дробно-аффинной или матричной рекуррентной зависимостью. Последовательности с аффинными рекуррентными зависимостями приводятся в [9].

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

В современных процессорах скорость выполнения арифметических операций оказывается на порядок быстрее, чем скорость считывания аргументов этих операций из оперативной памяти [11]. Это явление объясняет, например, почему известный алгоритм Штрассена перемножения матриц оказывается неэффективным на современных процессорах. В данной статье приводятся оценки сложности алгоритма по обращениям к памяти. По этому показателю представленный в данной статье алгоритм превосходит описанный ранее алгоритм [5-7] и алгоритм на котором он был основан [8].

Описываемое преобразование может быть реализовано, для автоматического выполнения, в оптимизирующих распараллеливающих компиляторах (например, [12-14]).

1. Алгоритм распараллеливания рекуррентных циклов с предварительным вычислением суперпозиций

Обозначим через Р количество вычислительных устройств (ВУ). Задача состоит в том, чтобы за как можно меньшее время вычислить последовательность Хг(г = (1, 2,..., N)) через отображения Сг по рекуррентной формуле Хг = Сг (Хг-1), при условии, что в наличии имеется Р ВУ.

Таким образом, в качестве распараллеливаемого будет рассматриваться программный цикл вида:

= 1; 1 <= Ы; 1++) Х[1] = С[1] (Х[1-1]).

Далее будем считать, что Р > 1. Исходя из постановки задачи, Хг нельзя вычислить, не зная Хг-1, а значит, несколько ВУ не смогут одновременно вычислять различные элементы искомого массива, если не выполнить каких-нибудь дополнительных действий. Как раз такими действиями может являться вычисление суперпозиций Сг о Сг+1.

Рассмотрим пример, демонстрирующий то, каким образом предварительно вычисленные суперпозиции способны помочь распараллеливанию.

Пример 1. Цикл, содержащий аффинную рекуррентную зависимость:

^г(1 = 1; 1 <= Ы; 1++) Х[1] = А[1] *Х[1-1]+В[1] .

В этом цикле никакие две итерации нельзя вычислить одновременно.

Заметим что Х1 = С1(Х0) = А1 * Х0 + В1, а Х2 = С2(Х1) = А2 * Х1 + В2. Следовательно, Х2 = С2(С1(Х0)) = А2*(А1 *Х0 + В1) + В2 = А2*А1 *Х0 + А2*В1 + В2.

Таким образом, если предварительно вычислить ААх = А2 * Ах и ББх = А2 * Бх, то можно будет одновременно получить Хх = Ах * Х0 + Бх и Х2 = ААх * Х0 + ББх.

Идея алгоритма

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

Рассмотрим идею алгоритма на простом примере. Предположим, что необходимо вычислить 100 элементов массива X (значит N=100) и для вычисления используется 2 ВУ (Р = 2). Изначально дано Х0. Исходя из постановки задачи, на первом шаге можно вычислить Хх = О\(Х0), вторым шагом можно вычислить Х2 = 02(Х1) и т.д. Теперь, предположим, что изначально имелось бы не только Х0, но и Х50. В таком случае, на первом шаге вторым ВУ можно было бы вычислять Х5х одновременно с Хх, на втором шаге Х52 параллельно с Х2 и т.д. Идея алгоритма состоит в том, чтобы предварительно вычислить элемент Х50, при помощи суперпозиции отображений G1, G2, . . . , ^0.

Описание алгоритма

Алгоритм состоит из трех этапов:

1. Вычисление (Р — 1)-ого отображения Qi = Gjо ■ ■ ■ о Gj+х о Gj (где I = 1, 2,... ,Р — 1; ] = ((I — 1) * X/ Р) + 1) через суперпозиции X/ Р исходных отображений (рис. 1).

Рис. 1. Иллюстрация первого этапа алгоритма

Псевдокод, соответствующий первому этапу алгоритма: for(i = 0; i < P-1; i++) { k = ((i+1)*N)/P; Q[i] = G[k] ;

for(j = 1; j < N/P; j++)

Q[i] = Superposition(Q[i], G[k-j])}.

2. Вычисление элементов XN/P,X2*N/P,..., X(P_i)*n/p посредством суперпозиций, вычисленных на первом этапе (рис. 2).

Рис. 2. Иллюстрация второго этапа алгоритма

Псевдокод, соответствующий второму этапу алгоритма: = 0; 1 < Г-1; 1++)

x[((i+l)*N)/p] = дитсм)/?]).

3. Вычисление элементов X всеми P ВУ начиная с X0 и элементов вычисленных на втором этапе алгоритма (рис. 3).

Псевдокод, соответствующий третьему этапу алгоритма:

for(i =0; i < P; i++) for(j = 0; j < N/P - 1; j++)

X[(i*N)/P + j + 1] = G[j](X[(i*N)/P + j]).

Заметим, что внешний цикл допускает параллельное выполнение.

Рис. 3. Иллюстрация третьего этапа алгоритма

Рассмотрим применение алгоритма к циклу с аффинной рекуррентной зависимостью при условии, что распараллеливание ведется на 2 ВУ.

Пример 2. Применим алгоритм к циклу с аффинной рекуррентной зависимостью (см. пример 1). На первом этапе будет получена суперпозиция, позволяющая вычислить элемент XN/2 через X0:

AA = A[N/2]*A[N/2-1]; BB = A[N/2]*B[N/2-1]+B[N/2] ; for(j = N/2-2; 0 < j; j--) { BB = AA*B[j]+BB; AA = AA*A [j]}.

Вторым этапом, с помощью суперпозиции, полученной на первом этапе, вычисляется элемент XN/2:

X[N/2] = AA*X [0]+BB.

На третьем этапе оба ВУ ведут вычисления своей половины элементов Xi:

for(j = 1; j < N/2; j++)//вычисления для первого ВУ

X[j] = A[j]*X [j-1] +B [j] ; for(j = N/2+1; j <= N; j++)//вычисления для второго ВУ X[j] = A[j]*X [j-1] +B [j] .

Стоит отметить, что вычислять суперпозиции на первом этапе, можно используя принцип сдваивания [4].

Пример 3. Рассмотрим два варианта получения суперпозиции, позволяющей вычислить X8 через X0:

1) G8 о (G7 ◦ (G6 о (G5 ◦ (G4 О (G3 О (G2 О Gl)))))),

2) (((G8 О G7) О (G6 О G5)) О ((G4 О G3) О (G2 ◦ Gl))).

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

2. Анализ сложности алгоритма

Проведем анализ сложности алгоритма для нескольких частных случаев отображений Сг.

Пример 4. В цикле примера 1 отображением Сг является Сг(Хг-1) = Аг * Хг-1 + Вг.

Пример 5. Цикл, содержащий дробно-аффинную рекуррентную зависимость:

^г(1 = 1; 1 <= Ы; 1++)

Х[1] = (А[1]*Х[1-1]+В[1]) / (С[1]*Х[1-1]+Б[1]).

В данном примере отображением является СДХ^) % % 1 %

C * Xi-1 + D

Заметим, что суперпозиция аффинных (дробно-аффинных) отображений будет аффинным (дробно-аффинным) отображением. Введем обозначения: Tsuperpositian -время, вычисления суперпозиции; Tfunction - время затрачиваемое на вычисление элемента последовательности с помощью отображения; Tread - время, затрачиваемое на чтение из одной ячейки памяти; Twrite - время, затрачиваемое на запись в одну ячейку памяти; Data - количество данных, используемых для описания отображения. Заметим что, отображение описывающее аффинную рекуррентную зависимость примера 1 состоит из двух коэффициентов A и B, что дает Data = 2, а дробно-аффинное примера 5 дает Data = 4.

Сложность алгоритма рассматривается с точки зрения вычислений и с точки зрения обращений к памяти.

2.1. Последовательное выполнение рекуррентного цикла

При последовательном выполнении цикла вычисляется N элементов массива X с использованием отображений Gi. Итого, вычислительная сложность алгоритма составляет N * Tfunction. Сложность по обращениям к памяти последовательного алгоритма состоит из записи N элементов X и чтения, используемых при вычислениях, данных. Итого, сложность алгоритма по обращениям к памяти равна N * Twrite + N * Data * Tread. Исходя из вышесказанного общая сложность алгоритма равна N * Tfunction + N * Twrite + N * Data * Tread.

2.2. Выполнение рекуррентного цикла с использованием рассматриваемого алгоритма

На первом этапе находится P — 1 отображение Qi (где i = 1,2,...,P — 1). При вычислении каждого отображения применяется N/P — 1 суперпозиция. То есть ((N/P — 1) * (P — 1)) * Tsuperposition. Все суперпозиции можно вычислять на P — 1 ВУ параллельно. Итого, время выполнения получается равным (N/P — 1) * Tsuperpositicn.

На втором этапе вычисляется всего P — 1 элемент массива X. Эти элементы вычисляются за время (P — 1) * Tfunction. Отметим что, вычисление этих же элементов последовательным алгоритмом занимает такое же время.

Третий этап алгоритма состоит в параллельном вычислении оставшихся элементов массива X. Это занимает ((N — (P — 1))/P)*Tfunction. Итого, вычислительная сложность составляет: ( N/P — 1) * Tsuperpositicn + ( P — 1) * Tfunction + (( N —(P — 1)) /P ) * Tfunction. Рассмотрим сложность алгоритма по обращениям к памяти.

На первом этапе алгоритма, считываются данные, необходимые для вычисления суперпозиций. Это занимает (P — 1) * (N/P) * Data * Tread. Если суперпозиции вычислять последовательно, то данные относящиеся к промежуточным суперпозициям (или промежуточные суперпозиции) могут быть перезаписаны. Откуда получается, что в итоге первого этапа потребуется записать лишь данные, соответствующие (P — 1)-ому отображению Qi (где i = 1, 2,..., P — 1), т.е. (P — 1) * Data * Twrite. На втором и третьем этапе производятся вычисления аналогичные последовательному вычислению цикла, соответственно, сложность по обращениям к памяти совпадает со сложностью при последовательном выполнении N * Twrite + N * Data * Tread. Итого, время выполнения алгоритма равно (P — 1) * (N/P) * Data * Tread + (P — 1) * Data * Twrite + N * Twrite + N * Data * Tread.

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

Заключение

В данной работе представлен новый алгоритм параллельного выполнения рекуррентных циклов. Приведена его общая оценка вычислительной сложности и сложности по обращениям к памяти, для случаев определенного типа рекуррентных зависимостей. Сложность по обращениям к памяти лучше известного параллельного алгоритма [8], в котором много промежуточных суперпозиций должно храниться в памяти.

Проводилось тестирование быстродействия алгоритма на двух компьютерах:

1) процессор Intel CoreTM i3-3227U 1.9GHz, оперативная память 4GB DDR3,

2) процессор Intel Core i7-3820 3.60GHz, оперативная память 8GB DDR3.

Алгоритм применялся к циклу с аффинной рекуррентной зависимостью, вычисляющему 67108864 элемента. Распараллеливания проводилось с использованием OpenMP 4.0. Результаты тестирования приведены в таблице.

Таблица

Результаты тестирования быстродействия алгоритма в случае аффинной рекуррентной зависимости

Процессор Последовательное выполнение (сек) Выполнение 2-мя потоками (сек) Выполнение 4-мя потоками (сек)

Intel i3 0,484124 0,310807 0,262585

Intel i7 0,383944 0,255872 0,210152

Работа поддержана грантом Правительства РФ № 075-15-2019-1928.

Литература

1. Allen, R. Optimizing Compilers for Modern Architectures / R. Allen, K. Kennedy. - San Francisco; San Diego; New York; Boston; London; Sidney; Tokyo: Morgan Kaufmann Publishers, 2002.

2. Wolfe, M. High Performance Compilers for Parallel Computing / M. Wolfe. - Redwood City: Addison-Wesley Publishing Company, 1996.

3. Steinberg, O.B. Circular Shift of Loop Body-Programme Transformation, Promoting Parallelism / O.B. Steinberg // Вестник ЮУрГУ. Серия: Математическое моделирование и программирование. - 2017. - Т. 10, № 3. - P. 120-132.

4. Штейнберг, Б.Я. Распараллеливание рекуррентных циклов с условными операторами / Б.Я. Штейнберг // Автоматика и телемеханика. - 1995. - № 6. - С. 176-184.

5. Штейнберг, О.Б. Распараллеливание рекуррентных циклов с нерегулярным вычислением суперпозиций / О.Б. Штейнберг // Известия ВУЗов. Северокавказский регион. Естественные науки. - 2009. - № 2. - С. 18-21.

6. Штейнберг, О.Б. Автоматическое распараллеливание рекуррентных циклов с проверкой устойчивости / О.Б. Штейнберг, С.Е. Суховерхов // Информационные технологии. -2010. - № 1. - С. 40-45.

7. Штейнберг, О.Б. Распараллеливание циклов, допускающих рекуррентные зависимости: дис. ... канд. физ.-мат. наук / О.Б. Штейнберг. - М., 2014.

8. Штейнберг, Б.Я. Математические методы распараллеливания рекуррентных программных циклов на суперкомпьютеры с параллельной памятью / Б.Я. Штейнберг. - Ростов-на-Дону: Изд-во Ростовского ун-та, 2004.

9. Graham, R.L. Concrete Mathematics: a Foundation for Computer Science / R.L. Graham, D.E. Knuth, O. Patashnik. - New York: Addison-Welsey, 1994.

10. Самарский, А.А. Введение в численные методы / А.А. Самарский. - М.: Наука, 1997.

11. Graham, S.L. Getting Up to Speed: the Future of Supercomputing / S.L. Graham, M. Snir, C.A. Patterson. - Washington: National Academies Press, 2005.

12. Оптимизирующая распараллеливающая система. - URL: http://ops.rsu.ru (дата обращения 1.07.2020).

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

13. Оптимизирующий компилятор Pluto. - URL: http://pluto-compiler.sourcefofge.net (дата обращения 1.07.2020).

14. Компилятор ROSE. - URL: http://rosecompiler.org/ (дата обращения 1.07.2020).

Олег Борисович Штейнберг, кандидат физико-математических наук, старший научный сотрудник, лаборатория «Вычислительная механика:», Институт математики, механики и компьютерных наук им. И.И. Воровича, Южный федеральный университет (г. Ростов-на-Дону, Российская Федерация), olegsteinb@gmail.com.

Поступила в редакцию 7 марта 2020 г.

MSC 68W10 DOI: 10.14529/mmp200305

PARALLELIZATION OF RECURRENT LOOPS DUE

TO THE PRELIMINARY COMPUTATION OF SUPERPOSITIONS

0.B. Steinberg, South Federal University, Rostov-on-Don, Russian Federation, olegsteinb@gmail.com

As a rule, sections of code that take a lot of time to compute are loops. Therefore, it is precisely on them that special efforts are directed when accelerating programs, in particular, through parallelization.

The article describes the parallelization algorithm for loops calculating the elements of a recursively given sequence. The recurrent loops considered in the article cannot be directly parallelized. With the help of auxiliary transformations, they can sometimes be reduced to loops that allow parallel execution. Earlier, the author of the article published another algorithm for parallelizing loops that calculate the elements of a recursively given sequence.

In modern processors, the execution time of arithmetic operations is an order of magnitude faster than reading the arguments of these operations from RAM. This article provides estimates of the complexity of accessing memory for the described algorithm. The parallel algorithm presented in the article is more efficient in accessing memory than the algorithm described by the author earlier.

Keywords: recurrent loops; numerical methods; parallel computation; program transformations; recurrent sequences.

References

1. Allen R., Kennedy K. Optimizing Compilers for Modern Architectures. San Francisco, San Diego, New York, Boston, London, Sidney, Tokyo, Morgan Kaufmann Publishers, 2002.

2. Wolfe M. High Performance Compilers for Parallel Computing. Redwood City, Addison-Wesley Publishing Company, 1996.

3. Steinberg O.B. Circular Shift of Loop Body-Programme Transformation, Promoting Parallelism. Bulletin of the South Ural State University. Series: Mathematical Modelling, Programming and Computer Software, 2017, vol. 10, no. 3, pp. 120-132. DOI: 10.14529/mmp170310

4. Steinberg B.Y. [Parallelizing Recurrence Loops with Conditional Statements]. Automation and Telemechanics, 1995, no. 6, pp. 176-184. (in Russian)

5. Steinberg O.B. [Parallelizing Recurrent Program Loops with Irregular Superposition Computation]. University News. North-Caucasian Region. Natural Sciences Series, 2009, no. 2, pp. 18-21. (in Russian)

6. Steinberg O.B., Sukhoverkhov S.E. [Recurrent Program Loops with Stability Check]. Information Technologies, 2010, no. 1, pp. 40-45. (in Russian)

7. Steinberg O.B. Parallelizing Loops Allowing Recurrent Dependences. PhD Thesis. Moscow, Institute for System Programming of the Russian Academy of Sciences, 2014. (in Russian)

8. Steinberg B.J. Matematicheskie metody rasparallelivaniya rekurrentnykh programnykh tsiklov na superkompyutery s parallelVnoy pamyatyu [Parallelizing Recurrent Program Loops with Irregular Superposition Computation]. Rostov-on-Don, Rostov University Publishing House, 2004. (in Russian)

9. Graham R.L., Knuth D.E., Patashnik O. Concrete Mathematics: a Foundation for Computer Science. N.Y., Addison-Welsey, 1994.

10. Samarskiy A.A. Vvedenie v chislennye metody [Introduction to Numerical Methods]. Moscow, Nauka, 1997. (in Russian)

11. Graham S.L., Snir M., Patterson C.A. Getting up to Speed: the Future of Supercomputing. Washington, National Academies Press, 2005.

12. Optimizing Parallelizing System. Available at: http://ops.rsu.ru (accessed 1.07.2020).

13. Optimizing Compiler Pluto. Available at: http://pluto-compiler.sourcefofge.net (accessed 1.07.2020).

14. ROSE Compiler. Available at: http://rosecompiler.org/ (accessed 1.07.2020).

Received March 7, 2020

i Надоели баннеры? Вы всегда можете отключить рекламу.