<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>СТВОРЕННЯ І ДОСЛІДЖЕННЯ ПАРАЛЕЛЬНИХ СХЕМ АЛГОРИТМУ ДЖОНСОНА В ТЕХНОЛОГІЇ GPGPU</article-title>
      </title-group>
      <pub-date>
        <year>2016</year>
      </pub-date>
      <fpage>105</fpage>
      <lpage>112</lpage>
      <abstract>
        <p>Запропоновано застосування алгоритму Джонсона для знаходження найкоротших шляхів між усіма парами вершин зваженого орієнтованого графа. Виконано його формалізацію у термінах модифікованих систем алгоритмічних алгебр Глушкова. Обґрунтовано доцільність використання технології GPGPU для пришвидшення роботи алгоритму. Отримано низку схем паралельної версії алгоритму, оптимізовану для використання в технології GPGPU. Запропоновано підходи до реалізації отриманих схем з використанням архітектури обчислень NVIDIA CUDA. Виконано експериментальне дослідження підвищення продуктивності при проведенні обчислень на відеоадаптері. Ключові слова: NVidia CUDA, GPGPU, SSSP, APSP, Thrust, САА схема, алгоритм Джонсона. Предложено применение алгоритма Джонсона для нахождения кратчайших путей между всеми парами вершин взвешенного ориентированного графа. Выполнена его формализация в терминах модифицированных систем алгоритмических алгебр Глушкова. Обоснована целесообразность использования технологии GPGPU для ускорения работы алгоритма. Получен ряд схем параллельной версии алгоритма, оптимизированной под использование в технологии GPGPU. Предложено подходы к реализации полученных схем с использованием архитектуры вычислений NVIDIA CUDA. Выполнено экспериментальное исследование повышения производительности при проведении вычислений на видеоадаптере. Ключевые слова: NVidia CUDA, GPGPU, SSSP, APSP, Thrust, САА схема, алгоритм Джонсона. Johnson's all pairs shortest path algorithm application in an edge weighted, directed graph is considered. Its formalization in terms of Glushkov's modified systems of algorithmic algebras was made. The expediency of using GPGPU technology to accelerate the algorithm is proved. A number of schemas of parallel algorithm optimized for using in GPGPU were obtained. Suggested approach to the implementation of the schemes obtained using computing architecture NVIDIA CUDA. An experimental study of improved performance by using GPU for computations was made.</p>
      </abstract>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>-</title>
      <p>CUDA визначає розширення мови C/C++, яке дозволяє створювати код, що виконуватиметься на
відеоадаптері, визначаючи С функції-ядра (kernels). Кожен потік можна однозначно визначити за допомогою ряду
контекстних змінних.</p>
      <p>1. gridDim – векторна змінна, що зберігає розмірності сітки.
2. blockIdx – векторна змінна, що зберігає індекс (3-вимірний) блоку в сітці.
3. blockDim – векторна змінна, що зберігає розмірність блоку.
4. threadIdx – векторна змінна, що зберігає індекс (3-вимірний) потоку в блоці.
5. warpSize – змінна, що зберігає розмір warp групи [5].</p>
      <p>Наприклад, наступна функція-ядро додає 2 числа:
}
}
__global__ void add(int* a, int* b, int* c){</p>
      <p>*c = *a + *b;
int main(void){
... //ініціалізація змінних, виділення пам’яті на GPU
add&lt;&lt;&lt;1, 1&gt;&gt;&gt;(a, b, c); //запуск 1-потокової 1-блокової програми на GPU
return 0;
Система Алгоритмічних Алгебр</p>
      <p>Для формалізованого представлення алгоритмів функціонування абстрактної моделі ЕОМ В.М. Глушков
запропонував математичний апарат систем алгоритмічних (мікропрограмних) алгебр (САА).</p>
      <p>Фіксована САА являє собою двоосновну алгебраїчну систему, основами якої є множина операторів і
множина умов [6]. Операції САА поділяються на логічні і операторні. До логічних відносяться узагальнені
булеві операції і операція лівого множення оператора на умову (призначена для прогнозування
обчислювального процесу), а до операторних – основні конструкції структурного програмування (послідовне виконання,
циклічне виконання тощо).</p>
      <p>Теорема Глушкова: Для довільного алгоритму існує (в загальному випадку не єдина) САА, в якій цей
алгоритм може бути представлений регулярною схемою. Тобто, якщо визначити основи САА для конкретного
алгоритму, можна представити цей алгоритм у вигляді схеми і проводити подальші трансформації й оптимізації
вже не з алгоритмом, а з його САА схемою. Деякі основні операції алгоритмічної алгебри та їх аналоги у
процедурних мовах програмування наведено в табл. 1, 2:</p>
      <p>Таблиця 1. Сигнатура операцій САА
Сигнатура операцій алгоритмічної алгебри</p>
      <p>(САА)
Композиція: А * В (або A × B )
α-диз’юнкція α ( A ∨ B)</p>
      <p>α-ітерація α {A}
Обернена α-ітерація {A}α
Таблиця 2. Сигнатура операцій САА-М
Відповідні оператори мови Паскаль</p>
      <p>A;B;
if α then A else B;</p>
      <p>while α do A;
repeat A until not α
Синхронна диз’юнкція A ∨ B
Позначення операції
Бінарна операція паралельного виконання операторів А і В</p>
      <p>на підструктурах певної моделі
Унарна операція, що породжує оператори-фільтри</p>
      <p>Бінарна операція
синхронного застосування
операторів A i B
Алгоритм Джонсона. Ідея алгоритму полягає у багаторазовому застосуванні алгоритму Дейкстри до
кожної з вершин графу. Таким чином можна отримати найкоротші шляхи між будь-якими парами вершин.
Недоліком алгоритму Дейкстри є неможливість його застосування для графів з від’ємними вагами ребер. Якщо ж
в графі є ребра з від’ємними вагами (але відсутні цикли негативних ваг), можна спробувати звести задачу до
випадку невід’ємних ваг, замінивши функцію ваги w на нову функцію ваги wˆ таку, щоб.</p>
      <p>1. Найкоротші шляхи не змінилися: для будь-якої пари вершин u, v ∈V найкоротший шлях з u в v з
точки зору функції w є також найкоротшим шляхом з точки зору wˆ і навпаки.</p>
      <p>2. Ваги всіх ребер з точки зору функції wˆ є невід’ємними [2].
Введемо наступні позначення:</p>
      <p>G(V , E) – вхідний граф;
V – початкова розмірність графа;
isEdge(i, j) – предикат, значенням якого буде істина, якщо існує ребро з вершини i в j ;
dij – довжина найкоротшого знайденого шляху з i в j ;
wij – довжина ребра з вершини i в j ;
{hi}, i = 0,V +1 – множина найкоротших відстаней від доданої вершини до всіх вершин графа;
setWay(i, j, val) – встановлення значення (val) шляху dij ;
setEdge(i, j, val) – встановлення значення (val) ребра з вершини i в j ;
add (Q,i) – додавання елементу i в колекцію Q;
erase(Q,i) – видалення елементу i з колекції Q;
eraseAll(Q, u) – видалення всіх елементів колекції u з колекції Q;
size(Q) – отримання кількості елементів у колекції Q.
Тому
першим
кроком</p>
      <p>алгоритму
V ′ = V ∪{s} ,
а
s
–
деяка
нова
вершина.</p>
      <p>є
При
побудова
цьому
графа</p>
      <p>G′ = (V ′, E′) ,
мають
бути
створені
ребра
{(s,v) : v ∈V ∪ wsv = 0} . Оператор extendGraph розширює заданий граф додатковою вершиною (з порядковим
індексом V), додаючи ребра вагою 0 від доданої вершини до усіх існуючих:</p>
      <p>extendGrap h=i&lt;V {setEdge (V , i,0)} .
Для формалізації алгоритмів введемо такі оператори:
оператор init проводить ініціалізацію перед виконанням алгоритмів Дейкстри та Беллмана – Форда,
встановлюючи найкоротші відстані від початкової вершини ( q ) до усіх інших вершин, окрім q , як ∞ :
init(q)=i&lt;V {i=q (setWay(q,i,0) ∨setWay(q,i,∞)) × (+ + i)} ; 
оператор RelaxEdge проводить релаксацію ребра з вершини u в v. Умовою релаксації є мінімум шляху
від початкової вершини q до вершин, що розглядаються:
 </p>
      <p>RelaxEdge (q,u,v)=isEdge(u,v)(duv &gt;dqu +wuv (setWay(q,v ,d qu + wuv ) ∨ E) ∨ E) ; 
оператор RelaxEdges проводить релаксацію всіх ребер у графі:</p>
      <p>RelaxEdges (q)=u&lt;V {v&lt;V {RelaxEdge (q,u,v ) × (+ + v)} × (+ + u)}.</p>
      <p>Алгоритм Беллмана – Форда. Для коректного виконання алгоритму вхідний граф не може мати цикли
негативної ваги. Для перевірки наявності такого циклу використовується алгоритм Беллмана – Форда для
вершини s . Масив найкоротших шляхів від вершини s до усіх інших вершин, отриманий як результат виконання
алгоритму Беллмана – Форда (який буде містити елементи, відмінні від 0 у випадку негативних ваг ребер),
дозволяє перейти до вагової функції wˆ .</p>
      <p>Згідно алгоритму Беллмана – Форда, після (V − 1) циклів релаксації усіх ребер встановлені шляхи duv є
найкоротшими. Тому виконання умови релаксації після (V − 1) циклів релаксації усіх ребер означає наявність в
графі цикла негативних ваг [2], що і перевіряє умова cnc:</p>
      <p>u&lt;V v&lt;V
cnc(q) = ∧ ∧ (isEdge(u, v) ∧ dqv &lt; dqu + wuv ).</p>
      <p>
        u=1 v=1
де
(
        <xref ref-type="bibr" rid="ref1">1</xref>
        )
(
        <xref ref-type="bibr" rid="ref2">2</xref>
        ) 
(
        <xref ref-type="bibr" rid="ref3">3</xref>
        )
(
        <xref ref-type="bibr" rid="ref4">4</xref>
        )
(
        <xref ref-type="bibr" rid="ref5">5</xref>
        )
Таким чином, для алгоритма Беллмана – Форда з початковою вершинoю q можна сформувати наступну
схему з використанням апарату систем алгоритмічних алгебр Глушкова (САА):
      </p>
      <p>BellmanFor d (q) = init (q)×i&lt;V −1 {Re laxEdges (q) × (+ + i)} × cnc(q).</p>
      <p>Результатом виконання алгоритму Беллмана – Форда є булеве значення, що вказує на наявність циклів
негативної ваги у графі. Також алгоритм визначає найкоротші шляхи від доданої вершини “q” до усіх існуючих
вершин графа (задає значення {hi} ).</p>
      <p>Якщо у графі немає циклів негативної ваги, можна трансформувати ваги його ребер:</p>
      <p>UpdateWeights=i&lt;V +1{ j&lt;V +1 {isEdge(i, j) (wij = (wij + hi − h j ) ∨ E) × (+ + j)}× (+ + i)}.</p>
      <p>Алгоритм Дейкстри. Алгоритм Дейкстри є по суті послідовним і знаходить найкоротші шляхи від
заданої вершини до всіх інших вершин у графі за O(V logV + E) час. В алгоритмі Дейкстри виділяють 2 типи
вершин: встановлені та невстановлені. Встановленими вершинами називають ті, для яких визначений шлях від
початкової вершини і всі вихідні ребра яких релаксуються (або вже релаксовані). На початку алгоритму до
масиву встановлених вершин додається початкова вершина, і релаксуються ребра, що з неї виходять. На
наступній ітерації визначається вершина U, довжина до якої від початкової вершини є найменшою серед
невстановлених вершин. Ця вершина переноситься в масив встановлених вершин і її вихідні ребра релаксуються. Ітерації
повторюються, поки всі вершини не будуть перенесені в масив встановлених вершин [2].</p>
      <p>Введемо наступні додаткові позначення для формалізації послідовної версії алгоритму Дейкстри:
Q – множина нерозглянутих (невстановлених) вершин;</p>
      <p>ExtractMin(Q, q,u) – оператор знаходження вершини u такої, що не була розглянута раніше та відстань
від початкової вершини до якої ( dqu ) є найменшою:
Алгоритм Дейкстри ( D(q) ) можна формалізувати наступним чином:</p>
      <p>ExtractMin (Q, q,u) = (mv = d q,Q0 ) × (mi = 0)×i∈Q {(v = Qi )×mv&lt;dqv (mv = d qv ×</p>
      <p>×(mi = i) × (+ + i) ∨ E) × (v = Qi ) × erase(Q, mi)}.</p>
      <p>SubD1(Q) = ExtractMin(Q, q,u)×v&lt;V {RelaxEdge(q,u,v) × (+ + v)},</p>
      <p>
        D(q) = init(q)×Q≠∅ {SubD1(Q)}.
(
        <xref ref-type="bibr" rid="ref6">6</xref>
        )
(
        <xref ref-type="bibr" rid="ref7">7</xref>
        )
(
        <xref ref-type="bibr" rid="ref8">8</xref>
        )
(
        <xref ref-type="bibr" rid="ref9">9</xref>
        )
(
        <xref ref-type="bibr" rid="ref10">10</xref>
        )
(11)
Результат виконання алгоритму Дейкстри дає масив найкоротших шляхів від однієї вершини графу до
всіх інших в рамках нової вагової функції wˆ . Щоб перейти до початкової вагової функції w, потрібно виконати
операцію зворотньої трансформації ваг:
      </p>
      <p>to(D, i)= j&lt;V {setWay (i, j, D j + h j − hi ) × (+ + j)} ,
де {D j}, j = 0,V – множина найкоротших шляхів з вершини i до всіх інших вершин графу (отриманих
алгоритмом Дейкстри).</p>
      <p>Використовуючи позначення, можна сформувати послідовну регулярну схему алгоритму Джонсона:
α = BellmanFord (V ) ,</p>
      <p>SubJohns 1(V )=i&lt;V {to(D(i), i) × (+ + i)} ,
SubJohns 2(V )=α (UpdateWeig hts × SubJohns1(V ) ∨ E) ,</p>
      <p>Johnson = extendGrap h(V ) × SubJohns 2(V ) .</p>
      <p>З точки зору оптимізації і прискорення алгоритму, основний акцент має ставитися на прискорення
алгоритму Дейкстри, оскільки саме в цьому виконуються основні обчислення.
Створення паралельних схем</p>
      <p>Класичний підхід до розпаралелювання алгоритмів APSP заключається в паралельному виконанні
багатьох ітерацій алгоритму SSSP. Такий підхід є доцільним для виконання обчислень на CPU. При використанні
технології GPGPU та моделі CUDA велику роль у підвищенні продуктивності має рівень різноманітності гілок
виконання потоків. За моделлю SIMT різні потоки можуть мати різні шляхи виконання інструкцій. Втім, на
апаратному рівні різні шляхи виконання виконуються послідовно, навіть якщо певна умова розгалуження
виконується лише для одного потоку. Розробники CPU архітектур доклали багато зусиль для створення
компенсаційних заходів, зокрема, спекулятивного виконання та передбачення переходів. На GPU таких оптимізацій не
було виконано, тому використання розгалужень, шляхи виконання яких сильно відрізняються, призводить до
суттєвої втрати продуктивності [7].</p>
      <p>В роботі запропоновано паралельні схеми, що ґрунтуються на паралельному виконанні обчислень
всередині ітерацій SSSP. Це дає можливість звести рівень розгалуження гілок до мінімуму.</p>
      <p>Паралельні схеми алгоритму Дейкстри. Алгоритм Дейкстри складається з двох циклів – зовнішній на
кожній ітерації обирає вершину для релаксації всіх ребер, що виходять з неї; внутрішній цикл – власне цикл
релаксації ребер. Паралелізація зовнішнього циклу означає, що на кожній ітерації обирається більш ніж одна
вершина, ребра якої можуть бути релаксовані без впливу на коректність результатів алгоритму. Паралелізація
внутрішнього циклу означає, що вихідні ребра обраної вершини релаксуються одночасно.</p>
      <p>Паралелізація зовнішнього циклу. На кожній ітерації алгоритм Дейкстри виконує пошук вершини u
такої, що не була розглянута раніше та відстань від початкової вершини до якої ( dqu ) є найменшою. Чим
більша кількість обраних вершин на кожній ітерації – тим більший рівень паралелізму.</p>
      <p>Таким чином, якщо при послідовній версії обиралася одна вершина u (з умови мінімальності dqu ), то в
паралельній версії u – множина вершин v, для яких справджується нерівність:
dqv ≤ dqu + Δ ,
(12)
(13)
(14)
(15)</p>
      <p>PExtractMin(Q, q,u) = ExtractLimit(Q, q, m) ×
size(Q)</p>
      <p>•
× ∨ (</p>
      <p>i=1 dqi&lt;dqm+Δ (add (u,l) ∨ E) × BQ (size(Q)))× eraseAll(Q,u).
thrust::device_vector&lt;int&gt; Q(V);
INIT&lt;&lt;&lt;blocks, threads&gt;&gt;&gt;(Q, M, C, q);
while(Q.size() != 0)
{
}</p>
      <p>RELAX_WITH_MASK&lt;&lt;&lt;blocks, threads&gt;&gt;&gt;(G, M, C);
threshold = ExtractMin(Q, C);</p>
      <p>
        UPDATE_MASK&lt;&lt;&lt;blocks, threads&gt;&gt;&gt;(Q, C, M, threshold);
де Δ – довжина найменшого ребра в графі [8]. Такий варіант було обрано для економії пам’яті та
обчислювальної потужності за рахунок малого зниження продуктивності. Для збільшення швидкості виконання алгоритму
(за разунок додаткової пам’яті) можна замінити Δ на Δv , де Δv – довжина найменшого ребра, що виходить з
вершини v. Така заміна дасть можливість корегувати умову «прийнятності» для кожної окремої вершини, що
дозволить збільшити рівень паралелізму. Тому оператор (
        <xref ref-type="bibr" rid="ref8">8</xref>
        ) набуває наступного вигляду:
      </p>
      <p>ExtractMin (Q, q,u) = ExtractLim it(Q, q, m)×i&lt;size(Q) {dql &lt;dqm +Δ (add (u,l) ∨ E) ×</p>
      <p>×(+ + i)}× eraseAll(Q,u).</p>
      <p>
        ExtractLimit – оператор знаходження вершини, відстань від початкової вершини до якої є найменшою
(аналог (
        <xref ref-type="bibr" rid="ref8">8</xref>
        ) для послідовної схеми алгоритму, але без видалення знайденої вершини з Q ). Таким чином, в схемі
оператора (13) обробку кожної вершини в Q можна проводити паралельнo, синхронізуючи потоки бар’єром
BQ (i) , який затримує виконання потоків, поки їх не набереться кількість Q (внутрішня організація бар’єра
Таким чином, алгоритм Дейкстри, розпаралелений по зовнішньому циклу, на кожній ітерації бере в
роботу більш ніж одну вершину та паралельно для кожної з них відбувається релаксація всіх ребер, що з неї
виходять. Після цього процесу гілки синхронізуються бар’єром, алгоритм знаходить новий набір вершин для
обробки і починається наступна ітерація. Формалізувати цю схему можна наступним чином:
size(u)
      </p>
      <p>•
PSubD1(u) = l∨=1 (v&lt;V (RelaxEdge (q,ul ,v) × (+ + v)} × Bu (size(u))),</p>
      <p>Dijkstra(q) = init(q)×Q≠∅ {PExtractMin(Q, q,u) × PSubD1(u)}.</p>
      <p>Як видно з формалізованої схеми, кількість потоків дорівнює кількості вершин у графі. Структура
алгоритму наведена далі:
На кожній ітерації релаксація вихідних ребер відбувається не для всіх вершин, а з певною маскою:
RELAX_WITH_MASK(G, M, C)
{
tid = threadx.Id;
if(M[tid] == true)
{
&lt;для всіх ребер [edgeId] що виходять з вершини tid виконати&gt;
atomicMin(C[edgeId], G[edgeId] + C[tid]);
Як показано вище, вершини переносяться у масив встановлених, якщо відстань від початкової вершини
до них є меншою за поріг:</p>
      <p>UPDATE_MASK(Q, C, M, threshold)
{
tid = thread.Id;
if(C[Q[tid]] &lt;= threshold)
{</p>
      <p>M[Q[tid]] = true;</p>
      <p>Q[tid] = -1; // після виконання елементи ‘-1’ будуть видалені
Розробникам застосувань під GPU потрібно вирішувати не лише алгоритмічні задачі, але й задачі
вирішення конфліктів банків, оптимізації розгалужень тощо. При досить складних задачах кроки оптимізації є
неочевидними та погіршують читабельність та зрозумілість коду. Розробники CUDA створили бібліотеку
шаблонів на С++ для CUDA GPU застосувань, аналогічну до STL. Thrust вирішує низку проблем, пов’язаних з
оптимізацією застосувань під конкретні архітектури, або ж використання різних підходів для пристроїв, що
підтримують різні версії CUDA. Розробники можуть повністю концентруватися на вирішенні високорівневих задач,
делегуючи Thrust вибір обчислювальної реалізації.</p>
      <p>Основними контейнерами Thrust є вектори: host_vector та device_vector. Вміст host_vector зберігається в
загальній пам’яті комп’ютера, а вміст device_vector зберігається в пам’яті GPU. Контейнери реалізують низку
стандартних операцій та дають можливість глибокого розширення [10].</p>
      <p>З формальної схеми видно, що у векторі Q зберігаються індекси невстановлених вершин. Алгоритм
Дейкстри на кожній ітерації потребує знаходження індексу вершини, відстань від початкової вершини до якої є
найменшою. За допомогою Thrust задача вирішується наступним шляхом:
{
}
}
};
struct compare_unsettled_value
{
const int* data;
compare_unsettled_value(int* ptr) { data = ptr; }
__host__ __device__
bool operator()(int lhs, int rhs){</p>
      <p>return this-&gt;data[lhs] &lt; this-&gt;data[rhs];
ExtractMin(Q, C) //Q – device_vector&lt;int&gt;</p>
      <p>thrust::detail::normal_iterator&lt;device_ptr&lt;int&gt;&gt; minIndex = thrust::min_element(
thrust::device, Q.begin(), Q.end(), compare_unsettled_value(C);</p>
      <p>...</p>
      <p>
        У вищенаведеному блоці коду використовується бібліотечний метод min_element, що виконує згортку
масиву (заданого межами Q.begin() – Q.end()) операцією знаходження мінімуму. Оскільки в масиві зберігаються
не значення відстані, а індекси, вводиться структура compare_unsettled_value, яка за заданими індексами
проводить порівняння відстаней відповідних вершин. Всі обчислення проводяться на GPU, про що свідчить явно
задана політика thrust::device [10].
Паралелізація внутрішнього циклу. Як було сказано вище, паралелізація внутрішнього циклу означає
одночасну релаксацію всіх ребер обраної вершини. Таким чином, оператор (13) залишається такою ж, як і в
послідовній версії ((
        <xref ref-type="bibr" rid="ref8">8</xref>
        )), але змінюється логіка роботи з оператором (
        <xref ref-type="bibr" rid="ref3">3</xref>
        ): наразі кожен потік представляє собою
ребро, яке може бути релаксовано незалежно. Після паралельної обробки відбувається бар’єрна синхронізація
по V потокам, після якої обробка поточної вершини вважається закінченою і алгоритм обирає вершину для
наступної ітерації.
      </p>
      <p>V
•
PSubD2(u) = ∨ (RelaxEdge(q,u,v) × Bv (v)),</p>
      <p>
        v=1
Dijkstra (q) = init (q)×Q≠∅ {ExtractMin (Q,u) × PSubD 2(u)}.
(16)
Паралелізація алгоритму Беллмана – Форда. Оскільки алгоритм Беллмана – Форда виконується в
алгоритмі Джонсона лише раз, навіть при ідеальній паралельній версії великого виграшу отримати не вдасться.
Втім, алгоритм може опрацьовувати кожну вершину паралельно, тому оператор (
        <xref ref-type="bibr" rid="ref4">4</xref>
        ) у паралельному виконанні
має наступний вигляд:
      </p>
      <p>V
•
RelaxEdges (q) = u∨=1(u&lt;V {RelaxEdge (q,u, v) × (+ + v) × Bv (u)}).
(17)
Схема всього алгоритму залишається без змін.
Аналіз отриманих результатів</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          1.
          <string-name>
            <surname>Pogorilyy</surname>
            <given-names>S.D.</given-names>
          </string-name>
          &amp;
          <string-name>
            <surname>Bilous</surname>
            <given-names>R.V.</given-names>
          </string-name>
          (
          <year>2010</year>
          )
          <article-title>Genetic algorithm for solving routing problem in networks</article-title>
          .
          <source>In 7-th international scientific programming conference UKRPROG'2010. Kyiv</source>
          . p.p.
          <fpage>171</fpage>
          -
          <lpage>177</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          2.
          <string-name>
            <surname>Cormen T. H</surname>
          </string-name>
          . et al. (
          <year>2013</year>
          )
          <article-title>Introduction to algorithms</article-title>
          , 3rd Ed. Cambridge: MIT Press.
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          3.
          <string-name>
            <surname>NVidia</surname>
          </string-name>
          (
          <year>2015</year>
          ). CUDA
          <string-name>
            <given-names>C</given-names>
            <surname>Programming</surname>
          </string-name>
          <article-title>Guide</article-title>
          [Online]
          <year>September 2015</year>
          . Available from: http://docs.nvidia.com/cuda/pdf/CUDA_C_
          <article-title>Programming_Guide</article-title>
          .pdf [Accessed:
          <fpage>22</fpage>
          -nd
          <source>Dec</source>
          <year>2015</year>
          ].
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          4.
          <string-name>
            <surname>Boreskov</surname>
            <given-names>A.V.</given-names>
          </string-name>
          &amp;
          <string-name>
            <surname>Kharlamov</surname>
            <given-names>A.A</given-names>
          </string-name>
          (
          <year>2010</year>
          )
          <article-title>CUDA technology fundamentals</article-title>
          . Moscow: DMC Press.
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          5.
          <string-name>
            <surname>Pogorilyy S.D.</surname>
          </string-name>
          ,
          <string-name>
            <surname>Vitel</surname>
            <given-names>D.</given-names>
          </string-name>
          <string-name>
            <surname>Yu</surname>
          </string-name>
          . &amp;
          <string-name>
            <surname>Vereschinsky</surname>
            <given-names>O.A.</given-names>
          </string-name>
          (
          <year>2012</year>
          )
          <article-title>Modern video adapter architectures</article-title>
          .
          <source>GPGPU technology. Part 1. Data registration, storage and processing</source>
          .
          <volume>14</volume>
          (
          <issue>4</issue>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          6.
          <string-name>
            <surname>Anisimov</surname>
            <given-names>A.V.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Pogorilyy</surname>
            <given-names>S.D.</given-names>
          </string-name>
          &amp;
          <string-name>
            <surname>Vitel</surname>
            <given-names>D.Yu.</given-names>
          </string-name>
          (
          <year>2013</year>
          )
          <article-title>About the Issue of Algorithms formalized Design for Parallel Computer Architectures</article-title>
          .
          <source>Applied and Computational Mathematics</source>
          .
          <volume>12</volume>
          (
          <issue>2</issue>
          ). p.p.
          <fpage>140</fpage>
          -
          <lpage>151</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          7.
          <string-name>
            <surname>Tianyi</surname>
            ,
            <given-names>D. H.</given-names>
          </string-name>
          &amp;
          <string-name>
            <surname>Abdelrahman</surname>
            ,
            <given-names>T. S.</given-names>
          </string-name>
          (
          <year>2011</year>
          ).
          <article-title>Reducing Branch Divergence in GPU Programs</article-title>
          .
          <source>Proceedings of the Fourth Workshop on General Purpose Processing on Graphics Processing Units</source>
          . New York, NY, USA.
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          8.
          <string-name>
            <surname>Crauser</surname>
            ,
            <given-names>A.</given-names>
          </string-name>
          et al (
          <year>1998</year>
          ).
          <article-title>A parallelization of Dijkstra's shortest path algorithm</article-title>
          . Mathematical Foundations of Computer Science, P.
          <fpage>722</fpage>
          -
          <lpage>731</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          9.
          <string-name>
            <surname>Pogorilyy S.D.</surname>
          </string-name>
          ,
          <string-name>
            <surname>Maryanovsky</surname>
            <given-names>V.A.</given-names>
          </string-name>
          ,
          <string-name>
            <given-names>Boyko</given-names>
            <surname>Yu</surname>
          </string-name>
          .V. &amp;
          <string-name>
            <surname>Vereshinsky</surname>
            <given-names>O.A.</given-names>
          </string-name>
          (
          <year>2009</year>
          )
          <article-title>Research of Danzig algorithm parallel schemes for computing systems with shared memory</article-title>
          .
          <source>Mathematical Machines and Systems. 4.</source>
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          10.
          <string-name>
            <surname>Bell</surname>
            ,
            <given-names>N.</given-names>
          </string-name>
          &amp;
          <string-name>
            <surname>Hoberock</surname>
            ,
            <given-names>J.</given-names>
          </string-name>
          (
          <year>2011</year>
          ).
          <article-title>Thrust: A Productivity-Oriented Library for</article-title>
          CUDA [Online]
          <year>2011</year>
          . Available from: https://thrust.googlecode.com/files/Thrust%
          <fpage>20</fpage>
          -
          <lpage>%</lpage>
          20A%
          <fpage>20Productivity</fpage>
          -
          <source>Oriented%20Library%20for%20CUDA.pdf [Accessed: 22-nd Dec</source>
          <year>2015</year>
          ].
        </mixed-citation>
      </ref>
      <ref id="ref11">
        <mixed-citation>
          <string-name>
            <given-names>Індекс</given-names>
            <surname>Гірша: Google</surname>
          </string-name>
          Scholar -
          <volume>5</volume>
          , Scopus -
          <volume>2</volume>
          .
        </mixed-citation>
      </ref>
      <ref id="ref12">
        <mixed-citation>http://orcid.org/0000-0001-9667-8729.</mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>