Ҳамаи мисолҳои дар боло овардашуда мушкилоти дарёфти роҳи кӯтоҳтарин байни ду қуллаи графикро ифода мекунанд (маршрут ё роҳи байни ду ҷой, як қатор амалҳо ё мураккабии гирифтани варақ аз ин ё он ҷо). Ин синфи мушкилоти роҳи кӯтоҳтарин SSSP (Single Source Shortest Path) номида мешавад ва алгоритми асосии ҳалли ин масъалаҳо мебошад, ки мураккабии ҳисоббарории O(n^2)
дорад.
Аммо, баъзан мо бояд ҳамаи роҳҳои кӯтоҳтаринро дар байни ҳама қуллаҳо пайдо кунем. Мисоли зеринро дида бароед: шумо барои шумо харитаи ҳаракатҳои мунтазами байни хона , кор ва театр эҷод мекунед. Дар ин сенария шумо бо 6 масир хотима хоҳед ёфт: work ⭢ home
, home ⭢ work
, work ⭢ theatre
, theatre ⭢ work
, home ⭢ theatre
ва theatre ⭢ home
(масалан, масирҳои баръакс аз сабаби роҳҳои яктарафа метавонанд гуногун бошанд) .
A(k, n) = n! / (n - m)! // where n - is a number of elements, // k - is a number of elements in permutation (in our case k = 2)
Ин ба мо 12 хатсайр барои 4 ҷой ва 90 хатсайр барои 10 ҷой медиҳад (ки таъсирбахш аст). Эзоҳ… ин бе назардошти нуқтаҳои мобайнии байни ҷойҳо аст, яъне барои аз хона ба кор рафтан шумо бояд 4 кӯчаро убур кунед, аз дарё сайр кунед ва аз купрук гузаред. Агар мо тасаввур кунем, баъзе масирҳо метавонанд нуқтаҳои мобайнии умумӣ дошта бошанд… хуб… дар натиҷа мо як графикаи хеле калон бо қуллаҳои зиёд дорем, ки дар он ҳар як қулла ё ҷой ё нуқтаи мобайнии муҳимро ифода мекунад. Синфи масъалаҳо, ки дар он мо бояд ҳамаи роҳҳои кӯтоҳтарини байни ҳамаи ҷуфтҳои қуллаҳои графикро пайдо кунем, APSP (All Pairs Shortest Paths) номида мешавад ва алгоритми асосии ҳалли ин масъалаҳо мебошад, ки дорои O(n^3)
мебошад. O(n^3)
мураккабии хисоббарорй.
Алгоритми Флойд-Уоршалл ҳамаи роҳҳои кӯтоҳтаринро байни ҳар як ҷуфт қуллаҳои графикӣ пайдо мекунад. Алгоритмҳоро Роберт Флойд дар [1] нашр кардааст (барои тафсилоти бештар ба бахши “Истифодабарандагон” нигаред). Дар ҳамон сол, Питер Ингерман дар [2] татбиқи муосири алгоритмро дар шакли се ҳалқаҳои for
гузошташуда тавсиф кард:
algorithm FloydWarshall(W) do for k = 0 to N - 1 do for i = 0 to N - 1 do for j = 0 to N - 1 do W[i, j] = min(W[i, j], W[i, k] + W[k, j]) end for end for end for end algorithm // where W - is a weight matrix of N x N size, // min() - is a function which returns lesser of it's arguments
Агар шумо ҳеҷ гоҳ барои кор бо графике, ки дар шакли матритса нишон дода шудааст, тағир наёфта бошед, пас фаҳмидани он ки алгоритми дар боло зикршуда чӣ кор мекунад, душвор буда метавонад. Пас, барои боварӣ ҳосил кардан, ки мо дар як саҳифа ҳастем, биёед бубинем, ки чӣ гуна графикро дар шакли матритса муаррифӣ кардан мумкин аст ва чаро чунин муаррифӣ барои ҳалли масъалаи роҳи кӯтоҳтарин муфид аст.
Дар расми зер графики 5 қулла нишон дода шудааст. Дар тарафи чап, график дар шакли визуалӣ оварда шудааст, ки аз доираҳо ва тирҳо сохта шудааст, ки дар он ҳар як доира қулла ва тирча канори самтро ифода мекунад. Рақами дохили доира ба рақами қулла ва рақами болои канор ба вазни канор мувофиқат мекунад. Дар тарафи рост, ҳамон график дар шакли матритсаи вазн оварда шудааст. Матритсаи вазн як шакли мебошад, ки дар он ҳар як ячейкаи матритса дорои "вазн" - масофаи байни қуллаи i
(сатр) ва қуллаи j
(сутун) мебошад. Матритсаи вазн маълумотро дар бораи "роҳ" байни қуллаҳо (рӯйхати қуллаҳое, ки тавассути онҳо шумо аз i
то j
ба даст меоред) дар бар намегирад – танҳо вазн (масофа) байни ин қуллаҳо.
Дар матритсаи вазн мо мебинем, ки арзишҳои чашмакҳо ба вазнҳои байни қуллаҳои баробаранд. Аз ин рӯ, агар мо роҳҳоро аз қуллаи 0
(сатри 0
) тафтиш кунем, мебинем, ки ... танҳо як роҳ вуҷуд дорад - аз 0
то 1
. Аммо, дар тасвири визуалӣ мо метавонем роҳҳоро аз қуллаи 0
то қуллаҳои 2
ва 3
(тавассути қуллаи 1
) равшан бубинем. Сабаби ин оддӣ аст - дар ҳолати аввал, матритсаи вазн танҳо масофаи байни қуллаҳои ҳамсояро дар бар мегирад. Аммо, танҳо ин маълумот барои пайдо кардани боқимонда кофӣ аст.
Биёед бубинем, ки он чӣ гуна кор мекунад. Ба чашмаки W[0, 1]
диққат диҳед. Қимати он нишон медиҳад, ки роҳ аз қуллаи 0
то қуллаи 1
бо вазни баробар ба 1
мавҷуд аст (дар кӯтоҳ мо метавонем чунин нависед: 0 ⭢ 1: 1
). Бо ин дониш, мо ҳоло метавонем ҳамаи ячейкаҳои сатри 1
скан кунем (ки ҳамаи вазнҳои ҳамаи роҳҳоро аз қуллаи 1
дар бар мегирад) ва бандари ин маълумотро ба сатри 0
интиқол дода, вазнро ба 1
зиёд кунем (қимати W[0, 1]
).
Бо истифода аз ҳамон қадамҳо мо метавонем роҳҳоро аз қуллаи 0
тавассути қуллаҳои дигар пайдо кунем. Ҳангоми ҷустуҷӯ, шояд рӯй диҳад, ки зиёда аз як роҳе вуҷуд дорад, ки ба як қулла мебарад ва муҳимтар аз он, вазнҳои ин роҳҳо метавонанд гуногун бошанд. Намунаи чунин вазъият дар расми зер тасвир шудааст, ки ҷустуҷӯ аз қуллаи 0
то қуллаи 2
боз як роҳи дигарро ба қуллаи 3
-и вазни камтар ошкор кард.
Мо ду роҳ дорем: роҳи аслӣ – 0 ⭢ 3: 4
ва роҳи наве, ки мо ҳоло кашф кардем – 0 ⭢ 2 ⭢ 3: 3
(дар хотир доред, ки матритсаи вазн роҳҳоро дар бар намегирад, бинобар ин мо намедонем, ки кадоме аз онҳост. қуллаҳо ба роҳи аслӣ дохил карда шудаанд). Ин лаҳзаест, ки мо тасмим мегирем, ки кӯтоҳтаринро нигоҳ дорем ва 3
ба чашмаки W[0, 3]
нависед.
algorithm FloydWarshall(W) do for k = 0 to N - 1 do for i = 0 to N - 1 do for j = 0 to N - 1 do W[i, j] = min(W[i, j], W[i, k] + W[k, j]) end for end for end for end algorithm
Давраи аз ҳама берунӣ for
k
дар болои ҳамаи қуллаҳои график такрор мешавад ва дар ҳар як итератсия, тағирёбандаи k
қуллаеро ифода мекунад , ки мо роҳҳои ҷустуҷӯи тавассути . Давраи дохилӣ for
on i
инчунин дар болои ҳамаи қуллаҳои график такрор мешавад ва дар ҳар як такрор, i
қуллаеро нишон медиҳад , ки мо роҳҳоро аз . Ва ниҳоят, як давраи ботинӣ for
j
дар болои ҳамаи қуллаҳои график ва дар ҳар як итератсия такрор мешавад, j
қуллаеро нишон медиҳад , ки мо роҳро ҷустуҷӯ мекунем. Дар якҷоягӣ он ба мо чунин медиҳад: дар ҳар як такрори k
мо роҳҳоро аз ҳама қуллаҳои i
ба ҳамаи қуллаҳои j
тавассути қуллаи k
ҷустуҷӯ мекунем. Дар дохили давра мо роҳи i ⭢ j
(нишондодашуда бо W[i, j]
) бо роҳи i ⭢ k ⭢ j
(бо маблағи W[I, k]
ва W[k, j]
ифода карда мешавад) муқоиса мекунем ва кӯтоҳтаринро менависем. як баргашт ба W[i, j]
.
Рамзи сарчашма ва графикҳои таҷрибавӣ дар GitHub дастрасанд. Графикҳои таҷрибавиро дар феҳристи Data/Sparse-Graphs.zip
пайдо кардан мумкин аст. Ҳама нишондиҳандаҳои ин мақола дар файли амалӣ карда мешаванд.
NO_EDGE = (int.MaxValue / 2) - 1
.
Дар бораи №1. Вақте ки мо дар бораи матритсаҳо сухан меронем, мо ҳуҷайраҳоро аз рӯи “сатрҳо” ва “сутунҳо” муайян мекунем. Аз ин рӯ, тасаввур кардани матритса дар шакли «мураббаъ» ё «росткунҷа» табиист (Расми 4а).
i = row * row_size + col; // where row - cell row index, // col - cell column index, // row_size - number of cells in a row.
Массиви хаттии матритсаи вазн шарти пешакии иҷрои самараноки алгоритми Флойд-Уоршалл мебошад. Сабаби ин содда нест ва шарҳи муфассал сазовори як мақолаи алоҳида ... ё чанд паём аст. Бо вуҷуди ин, айни замон бояд қайд кард, ки чунин муаррифӣ ба таври назаррас беҳтар мекунад, ки дар асл ба кори алгоритм таъсири калон мерасонад.
- Эзоҳҳои муаллиф
Дар бораи № 2. Агар шумо ба алгоритми псевдокоди бодиққат назар андозед, шумо ягон санҷиши марбут ба мавҷудияти роҳ байни ду қулларо намеёбед. Ба ҷои ин, псевдокод танҳо функсияи min()
ро истифода мебарад. Сабаб оддӣ аст - дар аввал, агар байни қуллаҳо роҳ мавҷуд набошад, арзиши ячейка ба infinity
муқаррар карда мешавад ва дар ҳама забонҳо, ба истиснои JavaScript, ҳама арзишҳо аз infinity
камтаранд. Дар сурати мавҷуд будани ададҳо, он метавонад васвасаи истифодаи int.MaxValue
ҳамчун арзиши "ҳеҷ роҳ" бошад. Аммо ин боиси зиёд шудани ададҳои бутун мегардад, дар ҳолатҳое, ки қиматҳои ҳам i ⭢ k
ва k ⭢ j
роҳҳо ба int.MaxValue
баробар мешаванд. Аз ин рӯ, мо арзишеро истифода мебарем, ки як камтар аз нисфи int.MaxValue
аст.
- Хонандаи боандеша
Ин дар ҳақиқат имконпазир аст, аммо мутаассифона он ба ҷазои назарраси иҷроиш оварда мерасонад. Хулоса, CPU омори натиҷаҳои арзёбии филиалҳоро нигоҳ медорад, масалан. вақте ки баъзе аз изҳороти if
true
ё false
арзёбӣ мешаванд. Он ин оморро барои иҷро кардани рамзи "шохаи аз ҷиҳати оморӣ пешбинишуда" пеш аз баҳодиҳии if
воқеӣ истифода мебарад (ин номида мешавад) ва аз ин рӯ кодро самараноктар иҷро мекунад. Аммо, вақте ки пешгӯии CPU нодуруст аст, он дар муқоиса бо пешгӯии дуруст ва иҷрои бечунучаро талафоти назаррасро ба бор меорад, зеро CPU бояд шохаи дурустро қатъ ва ҳисоб кунад.
Азбаски дар ҳар як такрори k
мо як қисми зиёди матритсаи вазнро навсозӣ мекунем, омори филиали CPU бефоида мешавад, зеро намунаи рамз вуҷуд надорад, ҳама шохаҳо танҳо ба маълумот асос ёфтаанд. Ҳамин тавр, чунин санҷиш боиси миқдори зиёди мегардад.
Уҳ, ба назар чунин мерасад, ки мо қисми назариявиро анҷом додем - биёед алгоритмро амалӣ кунем (мо ин татбиқро ҳамчун Baseline
нишон медиҳем):
public void Baseline(int[] matrix, int sz) { for (var k = 0; k < sz; ++k) { for (var i = 0; i < sz; ++i) { for (var j = 0; j < sz; ++j) { var distance = matrix[i * sz + k] + matrix[k * sz + j]; if (matrix[i * sz + j] > distance) { matrix[i * sz + j] = distance; } } } } }
Рамзи дар боло зикршуда қариб як нусхаи псевдокоди қаблан зикршуда бо истиснои ягона аст - ба ҷои Math.Min()
мо if
навсозии ячейка танҳо ҳангоми зарурат истифода мебарем.
- Хонандаи боандеша
Сабаб оддӣ аст. Дар лаҳзаи навиштани JIT рамзи тақрибан баробарро ҳам барои татбиқи if
ва Math.Min
мебарорад. Шумо метавонед онро ба таври муфассал дар тафтиш кунед, аммо дар ин ҷо порчаҳои сохторҳои асосии ҳалқа мавҷуданд:
// // == Assembly code of implementation of innermost loop for of j using if. // 53: movsxd r14, r14d // // compare matrix[i * sz + j] and distance (Condition) // 56: cmp [rdx+r14*4+0x10], ebx 5b: jle short 64 // // if matrix[i * sz + j] greater than distance write distance to matrix // 5d: movsxd rbp, ebp 60: mov [rdx+rbp*4+0x10], ebx 64: // end of loop // // == Assembly code of implementation of innermost loop for of j using Math.Min. // 4f: movsxd rbp, ebp 52: mov r14d, [rdx+rbp*4+0x10] // // compare matrix[i * sz + j] and distance (Condition) // 57: cmp r14d, ebx. // // if matrix[i * sz + j] less than distance write value to matrix // 5a: jle short 5e // // otherwise write distance to matrix // 5c: jmp short 61 5e: mov ebx, r14d 61: mov [rdx+rbp*4+0x10], ebx 65: // end of loop
Шумо метавонед бубинед, новобаста аз он ки мо if
ё Math.Min
истифода мебарем, то ҳол санҷиши шартӣ мавҷуд аст. Аммо, дар if
мавҷуд набудани навиштани нолозим.
var max = v * (v - 1)) / 2; // where v - is a number of vertexes in a graph.
Биёед санг кунем!
Усул | Андоза | Маънои | Хатогӣ | StdDev |
---|---|---|---|---|
Асосӣ | 300 | 27,525 мс | 0,1937 мс | 0,1617 мс |
Асосӣ | 600 | 217,897 мс | 1,6415 мс | 1,5355 мс |
Асосӣ | 1200 | 1,763,335 мс | 7,4561 мс | 6,2262 мс |
Асосӣ | 2400 | 14,533,335 мс | 63,3518 мс | 52,9016 мс |
Асосӣ | 4800 | 119,768,219 мс | 181,5514 мс | 160,9406 мс |
Аз натиҷаҳое, ки мо мебинем, вақти ҳисобкунӣ дар муқоиса бо андозаи график хеле зиёд мешавад - барои графики 300 қуллаи он 27 миллисония, барои графики 2400 қуллаи - 14,5 сония ва барои графики 4800 - 119 сония, ки қариб 2 дақиқа !
Бо вуҷуди ин, мо дар таҷрибаҳои худ графикҳои мутаҳаррикро истифода мебарем, ки хусусияти аҷибе доранд – агар аз қуллаи 1
то қуллаи 2
роҳ мавҷуд бошад, пас аз қуллаи 2
то қуллаи 1
роҳ вуҷуд надорад. Барои мо, ин маънои онро дорад, ки бисёр қуллаҳои ҳамсоя вуҷуд доранд, ки агар аз i
то k
роҳ набошад, мо метавонем онро гузаронем (мо ин татбиқро ҳамчун SpartialOptimisation
ишора мекунем).
public void SpartialOptimisation(int[] matrix, int sz) { for (var k = 0; k < sz; ++k) { for (var i = 0; i < sz; ++i) { if (matrix[i * sz + k] == NO_EDGE) { continue; } for (var j = 0; j < sz; ++j) { var distance = matrix[i * sz + k] + matrix[k * sz + j]; if (matrix[i * sz + j] > distance) { matrix[i * sz + j] = distance; } } } } }
Инҳоянд натиҷаҳои татбиқи қаблӣ ( Baseline
) ва ҷорӣ ( SpartialOptimisation
) дар як маҷмӯи графикҳо (натиҷаҳои зудтарин бо ғафс нишон дода шудаанд):
Усул | Андоза | Маънои | Хатогӣ | StdDev | Таносуб |
---|---|---|---|---|---|
Асосӣ | 300 | 27,525 мс | 0,1937 мс | 0,1617 мс | 1.00 |
Optimization Spartial | 300 | 12,399 мс | 0,0943 мс | 0,0882 мс | 0,45 |
Асосӣ | 600 | 217,897 мс | 1,6415 мс | 1,5355 мс | 1.00 |
Optimization Spartial | 600 | 99,122 мс | 0,8230 мс | 0,7698 мс | 0,45 |
Асосӣ | 1200 | 1,763,335 мс | 7,4561 мс | 6,2262 мс | 1.00 |
Optimization Spartial | 1200 | 766,675 мс | 6,1147 мс | 5,7197 мс | 0,43 |
Асосӣ | 2400 | 14,533,335 мс | 63,3518 мс | 52,9016 мс | 1.00 |
Optimization Spartial | 2400 | 6,507,878 мс | 28,2317 мс | 26,4079 мс | 0,45 |
Асосӣ | 4800 | 119,768,219 мс | 181,5514 мс | 160,9406 мс | 1.00 |
Optimization Spartial | 4800 | 55,590,374 мс | 414,6051 мс | 387,8218 мс | 0,46 |
Компютери ман бо протсессори Inter Core i7-7700 CPU 3.60GHz (Kaby Lake)
бо 8 протсессори мантиқӣ ( ) ё 4 ядро бо технологияи муҷаҳҳаз шудааст. Доштани зиёда аз як ядро ба монанди доштани "дастҳои эҳтиётии" бештар аст, ки мо метавонем онҳоро ба кор гузорем. Пас, биёед бубинем, ки кадом қисми корро байни коргарони сершумор ба таври муассир тақсим кардан мумкин аст ва сипас онро параллел созед.
Биёед аз for of k
оғоз кунем. Дар ҳар як ҳалқаи такрорӣ роҳҳоро аз ҳар як қулла ба ҳар як қулла тавассути қуллаи k
ҳисоб мекунад. Роҳҳои нав ва навшуда дар матритсаи вазн нигоҳ дошта мешаванд. Ба такрорҳо нигоҳ карда, шумо метавонед мушоҳида кунед - онҳо метавонанд бо ҳама гуна тартиб иҷро карда шаванд: 0, 1, 2, 3 ё 3, 1, 2, 0 бидуни таъсир ба натиҷа. Бо вуҷуди ин, онҳо бояд ба таври пайдарпай иҷро карда шаванд, вагарна баъзе аз такрорҳо наметавонанд роҳҳои нав ё навшударо истифода баранд, зеро онҳо ҳанӯз дар матритсаи вазн навишта намешаванд. Чунин номутобиқатӣ бешубҳа натиҷаҳоро хароб хоҳад кард. Пас, мо бояд ҷустуҷӯро давом диҳем.
Номзади навбатӣ for of i
loop аст. Дар ҳар як даври такрорӣ роҳро аз қуллаи i
то қуллаи k
(ячейка: W[i, k]
), роҳро аз қуллаи k
то қуллаи j
(ячейка: W[k, j
]) мехонад ва сипас тафтиш мекунад, ки роҳи маълум аз i
ба j
(ячейка: W[i, j]
) аз i ⭢ k ⭢ j
роҳ кӯтоҳтар аст (маблағи: W[i, k]
+ W[k, j]
). Агар шумо ба намунаи дастрасӣ бодиққат назар кунед, шумо метавонед пайхас кунед - дар ҳар як итератсия i
цикли маълумот аз k
сатр ва навсозии i
сатрро мехонад, ки аслан маънои онро дорад - такрорҳо мустақиланд ва на танҳо бо ҳама гуна тартиб, балки дар баробари ҳам иҷро кардан мумкин аст!
Ин умедбахш менамояд, аз ин рӯ биёед онро амалӣ кунем (мо ин татбиқро ҳамчун SpartialParallelOptimisations
ишора мекунем).
for of j
давриро низ мувозӣ кардан мумкин аст. Аммо, параллелизатсияи давраи ботинӣ дар ин ҳолат хеле бесамар аст. Шумо метавонед онро худатон бо ворид кардани чанд тағйироти оддӣ дар тафтиш кунед.
- Эзоҳҳои муаллиф
public void SpartialParallelOptimisations(int[] matrix, int sz) { for (var k = 0; k < sz; ++k) { Parallel.For(0, sz, i => { if (matrix[i * sz + k] == NO_EDGE) { return; } for (var j = 0; j < sz; ++j) { var distance = matrix[i * sz + k] + matrix[k * sz + j]; if (matrix[i * sz + j] > distance) { matrix[i * sz + j] = distance; } } }); } }
Инҳоянд натиҷаҳои татбиқи Baseline
, SpartialOptimisation
ва SpartialParallelOptimisations
дар як маҷмӯи графикҳо (параллелизатсия бо истифода аз синфи анҷом дода мешавад):
Усул | Андоза | Маънои | Хатогӣ | StdDev | Таносуб |
---|---|---|---|---|---|
Асосӣ | 300 | 27,525 мс | 0,1937 мс | 0,1617 мс | 1.00 |
Optimization Spartial | 300 | 12,399 мс | 0,0943 мс | 0,0882 мс | 0,45 |
Оптимизатсияи SpartialParallel | 300 | 6,252 мс | 0,0211 мс | 0,0187 мс | 0,23 |
Асосӣ | 600 | 217,897 мс | 1,6415 мс | 1,5355 мс | 1.00 |
Optimization Spartial | 600 | 99,122 мс | 0,8230 мс | 0,7698 мс | 0,45 |
Оптимизатсияи SpartialParallel | 600 | 35,825 мс | 0,1003 мс | 0,0837 мс | 0,16 |
Асосӣ | 1200 | 1,763,335 мс | 7,4561 мс | 6,2262 мс | 1.00 |
Optimization Spartial | 1200 | 766,675 мс | 6,1147 мс | 5,7197 мс | 0,43 |
Оптимизатсияи SpartialParallel | 1200 | 248,801 мс | 0,6040 мс | 0,5043 мс | 0,14 |
Асосӣ | 2400 | 14,533,335 мс | 63,3518 мс | 52,9016 мс | 1.00 |
Optimization Spartial | 2400 | 6,507,878 мс | 28,2317 мс | 26,4079 мс | 0,45 |
Оптимизатсияи SpartialParallel | 2400 | 2,076,403 мс | 20,8320 мс | 19,4863 мс | 0,14 |
Асосӣ | 4800 | 119,768,219 мс | 181,5514 мс | 160,9406 мс | 1.00 |
Optimization Spartial | 4800 | 55,590,374 мс | 414,6051 мс | 387,8218 мс | 0,46 |
Оптимизатсияи SpartialParallel | 4800 | 15,614,506 мс | 115,6996 мс | 102,5647 мс | 0,13 |
Аз натиҷаҳо мо мебинем, ки параллелизатсияи даври for of i
вақти ҳисобкуниро нисбат ба татбиқи қаблӣ ( SpartialOptimisation
) 2-4 маротиба кам кардааст! Ин хеле таъсирбахш аст, аммо дар хотир доштан муҳим аст, ки параллелизатсияи ҳисобҳои холис тамоми захираҳои ҳисоббарории мавҷударо истеъмол мекунад, ки метавонад ба гуруснагии захираҳои барномаҳои дигар дар система оварда расонад.
var a = new [] { 5, 7, 8, 1 }; var b = new [] { 4, 2, 2, 6 }; var c = new [] { 0, 0, 0, 0 }; for (var i = 0; i < 4; ++i) c[i] = a[i] + b[i];
Бо роҳи аз ҳад зиёд соддакардашуда , иҷрои итератсияи 0
и for
даври дар боло зикршуда дар сатҳи CPU метавонад ба таври зерин тасвир карда шавад:
Ин аст он чизе ки рӯй медиҳад. CPU арзишҳои массивҳои a
ва b
аз хотира ба регистрҳои CPU бор мекунад (як реестр маҳз як арзиш дошта метавонад). Сипас CPU амалиёти иловаи скаляриро дар ин реестрҳо иҷро мекунад ва натиҷаро дубора ба хотираи асосӣ - рост ба массиви c
менависад. Ин раванд чор маротиба пеш аз ба охир расидани давра такрор карда мешавад.
Дар ин ҷо чӣ рӯй дода истодааст. CPU арзишҳои массивҳои a
ва b
аз хотира ба регистрҳои CPU бор мекунад (аммо ин дафъа як реестри вектор метавонад ду арзиши массивро нигоҳ дорад). Сипас CPU амалиёти иловаи векториро дар ин реестрҳо иҷро мекунад ва натиҷаро ба хотираи асосӣ - рост ба массиви c
менависад. Азбаски мо якбора дар ду арзиш амал мекунем, ин раванд ба ҷои чаҳор маротиба ду маротиба такрор мешавад.
public void SpartialVectorOptimisations(int[] matrix, int sz) { for (var k = 0; k < sz; ++k) { for (var i = 0; i < sz; ++i) { if (matrix[i * sz + k] == NO_EDGE) { continue; } var ik_vec = new Vector<int>(matrix[i * sz + k]); var j = 0; for (; j < sz - Vector<int>.Count; j += Vector<int>.Count) { var ij_vec = new Vector<int>(matrix, i * sz + j); var ikj_vec = new Vector<int>(matrix, k * sz + j) + ik_vec; var lt_vec = Vector.LessThan(ij_vec, ikj_vec); if (lt_vec == new Vector<int>(-1)) { continue; } var r_vec = Vector.ConditionalSelect(lt_vec, ij_vec, ikj_vec); r_vec.CopyTo(matrix, i * sz + j); } for (; j < sz; ++j) { var distance = matrix[i * sz + k] + matrix[k * sz + j]; if (matrix[i * sz + j] > distance) { matrix[i * sz + j] = distance; } } } } }
Рамзи векторӣ метавонад каме аҷиб ба назар расад, бинобар ин биёед онро зина ба зина гузарем.
Мо вектори i ⭢ k
роҳро эҷод мекунем: var ik_vec = new Vector<int>(matrix[i * sz + k])
. Дар натиҷа, агар вектор чаҳор қимати навъи int
дошта бошад ва вазни роҳи i ⭢ k
ба 5 баробар бошад, мо вектори чор 5-ро эҷод мекунем – [5, 5, 5, 5]. Минбаъд, дар ҳар як такрор, мо ҳамзамон роҳҳоро аз қуллаи i
то қуллаҳои j, j + 1, ..., j + Vector<int>.Count
ҳисоб мекунем.
Хосияти Vector<int>.Count
шумораи унсурҳои навъи int
ро, ки ба регистрҳои векторӣ мувофиқанд, бармегардонад. Андозаи регистрҳои векторӣ аз модели CPU вобаста аст, бинобар ин ин амвол метавонад арзишҳои гуногунро дар CPU-ҳои гуногун баргардонад.
- Эзоҳи муаллиф
ij_vec
ва ikj_vec
.ij_vec
ва ikj_vec
муқоиса кунед ва арзишҳои хурдтаринро ба вектори нави r_vec
интихоб кунед.r_vec
ба матритсаи вазн баргардонед.
Дар ҳоле ки №1 ва №3 хеле соддаанд, №2 шарҳро талаб мекунад. Тавре ки қаблан зикр гардид, бо векторҳо мо дар як вақт арзишҳои сершуморро идора мекунем. Аз ин рӯ, натиҷаи баъзе амалиётҳо ягона буда наметавонад, яъне амалиёти муқоисавии Vector.LessThan(ij_vec, ikj_vec)
наметавонад true
ё false
баргардонад, зеро он арзишҳои сершуморро муқоиса мекунад. Ба ҷои ин, он вектори наверо бар мегардонад, ки дорои натиҷаҳои муқоиса байни арзишҳои мувофиқ аз векторҳои ij_vec
ва ikj_vec
( -1
, агар арзиши ij_vec
камтар аз арзиши ikj_vec
ва 0
бошад, агар дар акси ҳол). Вектори баргардонидашуда (худ) аслан муфид нест, аммо мо метавонем амалиёти вектории Vector.ConditionalSelect(lt_vec, ij_vec, ikj_vec)
барои истихроҷи арзишҳои зарурӣ аз векторҳои ij_vec
ва ikj_vec
ба вектори нав - r_vec
истифода барем. Ин амалиёт вектори наверо бармегардонад, ки дар он арзишҳо ба хурдтар аз ду қимати мувофиқи векторҳои воридотӣ баробаранд, яъне агар арзиши вектори lt_vec
ба -1
баробар бошад, пас амалиёт қиматро аз ij_vec
интихоб мекунад, вагарна аз ikj_vec
арзиш интихоб мекунад.
Ба ғайр аз №2 , як қисми дигар вуҷуд дорад, ки шарҳро талаб мекунад - ҳалқаи дуюми векторнашуда. Он вақте лозим аст, ки андозаи матритсаи вазн ҳосили арзиши Vector<int>.Count
набошад. Дар ин ҳолат, ҳалқаи асосӣ наметавонад ҳамаи арзишҳоро коркард кунад (зеро шумо қисми векторро бор карда наметавонед) ва дуюм, ғайривекторӣ, давра такрори боқимондаро ҳисоб мекунад.
Усул | сз | Маънои | Хатогӣ | StdDev | Таносуб |
---|---|---|---|---|---|
Асосӣ | 300 | 27,525 мс | 0,1937 мс | 0,1617 мс | 1.00 |
Optimization Spartial | 300 | 12,399 мс | 0,0943 мс | 0,0882 мс | 0,45 |
Оптимизатсияи SpartialParallel | 300 | 6,252 мс | 0,0211 мс | 0,0187 мс | 0,23 |
Optimizations SpartialVector | 300 | 3,056 мс | 0,0301 мс | 0,0281 мс | 0,11 |
Асосӣ | 600 | 217,897 мс | 1,6415 мс | 1,5355 мс | 1.00 |
Optimization Spartial | 600 | 99,122 мс | 0,8230 мс | 0,7698 мс | 0,45 |
Оптимизатсияи SpartialParallel | 600 | 35,825 мс | 0,1003 мс | 0,0837 мс | 0,16 |
Optimizations SpartialVector | 600 | 24,378 мс | 0,4320 мс | 0,4041 мс | 0,11 |
Асосӣ | 1200 | 1,763,335 мс | 7,4561 мс | 6,2262 мс | 1.00 |
Optimization Spartial | 1200 | 766,675 мс | 6,1147 мс | 5,7197 мс | 0,43 |
Оптимизатсияи SpartialParallel | 1200 | 248,801 мс | 0,6040 мс | 0,5043 мс | 0,14 |
Optimizations SpartialVector | 1200 | 185,628 мс | 2,1240 мс | 1,9868 мс | 0,11 |
Асосӣ | 2400 | 14,533,335 мс | 63,3518 мс | 52,9016 мс | 1.00 |
Optimization Spartial | 2400 | 6,507,878 мс | 28,2317 мс | 26,4079 мс | 0,45 |
Оптимизатсияи SpartialParallel | 2400 | 2,076,403 мс | 20,8320 мс | 19,4863 мс | 0,14 |
Optimizations SpartialVector | 2400 | 2,568,676 мс | 31,7359 мс | 29,6858 мс | 0,18 |
Асосӣ | 4800 | 119,768,219 мс | 181,5514 мс | 160,9406 мс | 1.00 |
Optimization Spartial | 4800 | 55,590,374 мс | 414,6051 мс | 387,8218 мс | 0,46 |
Оптимизатсияи SpartialParallel | 4800 | 15,614,506 мс | 115,6996 мс | 102,5647 мс | 0,13 |
Optimizations SpartialVector | 4800 | 18,257,991 мс | 84,5978 мс | 79,1329 мс | 0,15 |
Аз натиҷа маълум аст, ки векторизатсия вақти ҳисобкуниро ба таври назаррас коҳиш додааст - аз 3 то 4 маротиба дар муқоиса бо версияи параллелнашуда ( SpartialOptimisation
). Лаҳзаи ҷолиб дар ин ҷо ин аст, ки версияи векторӣ инчунин аз версияи параллелӣ ( SpartialParallelOptimisations
) дар графикҳои хурдтар (камтар аз 2400 қуллаҳо) бартарӣ дорад.
Агар шумо ба татбиқи амалии векторизатсия таваҷҷӯҳ дошта бошед, ман ба шумо тавсия медиҳам, ки силсилаи паёмҳои хонед, ки дар он Array.Sort
ро вектор кардааст (ин натиҷаҳо баъдтар дар навсозӣ барои ҷамъоварии партов дар пайдо шуданд).
Иҷрои охирин кӯшишҳои параллелизатсия ва векторизатсияро муттаҳид мекунад ва … ростқавлона он фардият надорад 🙂 Зеро… хуб, мо танҳо як ҷузъи Parallel.For
-ро аз SpartialParallelOptimisations
бо ҳалқаи векторишудаи SpartialVectorOptimisations
иваз кардем:
public void SpartialParallelVectorOptimisations(int[] matrix, int sz) { for (var k = 0; k < sz; ++k) { Parallel.For(0, sz, i => { if (matrix[i * sz + k] == NO_EDGE) { return; } var ik_vec = new Vector<int>(matrix[i * sz + k]); var j = 0; for (; j < sz - Vector<int>.Count; j += Vector<int>.Count) { var ij_vec = new Vector<int>(matrix, i * sz + j); var ikj_vec = new Vector<int>(matrix, k * sz + j) + ik_vec; var lt_vec = Vector.LessThan(ij_vec, ikj_vec); if (lt_vec == new Vector<int>(-1)) { continue; } var r_vec = Vector.ConditionalSelect(lt_vec, ij_vec, ikj_vec); r_vec.CopyTo(matrix, i * sz + j); } for (; j < sz; ++j) { var distance = matrix[i * sz + k] + matrix[k * sz + j]; if (matrix[i * sz + j] > distance) { matrix[i * sz + j] = distance; } } }); } }
Ҳамаи натиҷаҳои ин мақола дар зер оварда шудаанд. Тавре ки интизор мерафт, истифодаи ҳамзамон параллелизм ва векторизатсия натиҷаҳои беҳтаринро нишон дода, вақти ҳисобкуниро то 25 маротиба (барои графикҳои 1200 қулла) нисбат ба татбиқи аввалия кам кард.
Усул | сз | Маънои | Хатогӣ | StdDev | Таносуб |
---|---|---|---|---|---|
Асосӣ | 300 | 27,525 мс | 0,1937 мс | 0,1617 мс | 1.00 |
Optimization Spartial | 300 | 12,399 мс | 0,0943 мс | 0,0882 мс | 0,45 |
Оптимизатсияи SpartialParallel | 300 | 6,252 мс | 0,0211 мс | 0,0187 мс | 0,23 |
Optimizations SpartialVector | 300 | 3,056 мс | 0,0301 мс | 0,0281 мс | 0,11 |
Оптимизатсияи SpartialParallelVector | 300 | 3,008 мс | 0,0075 мс | 0,0066 мс | 0,11 |
Асосӣ | 600 | 217,897 мс | 1,6415 мс | 1,5355 мс | 1.00 |
Optimization Spartial | 600 | 99,122 мс | 0,8230 мс | 0,7698 мс | 0,45 |
Оптимизатсияи SpartialParallel | 600 | 35,825 мс | 0,1003 мс | 0,0837 мс | 0,16 |
Optimizations SpartialVector | 600 | 24,378 мс | 0,4320 мс | 0,4041 мс | 0,11 |
Оптимизатсияи SpartialParallelVector | 600 | 13,425 мс | 0,0319 мс | 0,0283 мс | 0,06 |
Асосӣ | 1200 | 1,763,335 мс | 7,4561 мс | 6,2262 мс | 1.00 |
Optimization Spartial | 1200 | 766,675 мс | 6,1147 мс | 5,7197 мс | 0,43 |
Оптимизатсияи SpartialParallel | 1200 | 248,801 мс | 0,6040 мс | 0,5043 мс | 0,14 |
Optimizations SpartialVector | 1200 | 185,628 мс | 2,1240 мс | 1,9868 мс | 0,11 |
Оптимизатсияи SpartialParallelVector | 1200 | 76,770 мс | 0,3021 мс | 0,2522 мс | 0,04 |
Асосӣ | 2400 | 14,533,335 мс | 63,3518 мс | 52,9016 мс | 1.00 |
Optimization Spartial | 2400 | 6,507,878 мс | 28,2317 мс | 26,4079 мс | 0,45 |
Оптимизатсияи SpartialParallel | 2400 | 2,076,403 мс | 20,8320 мс | 19,4863 мс | 0,14 |
Optimizations SpartialVector | 2400 | 2,568,676 мс | 31,7359 мс | 29,6858 мс | 0,18 |
Оптимизатсияи SpartialParallelVector | 2400 | 1,281,877 мс | 25,1127 мс | 64,8239 мс | 0,09 |
Асосӣ | 4800 | 119,768,219 мс | 181,5514 мс | 160,9406 мс | 1.00 |
Optimization Spartial | 4800 | 55,590,374 мс | 414,6051 мс | 387,8218 мс | 0,46 |
Оптимизатсияи SpartialParallel | 4800 | 15,614,506 мс | 115,6996 мс | 102,5647 мс | 0,13 |
Optimizations SpartialVector | 4800 | 18,257,991 мс | 84,5978 мс | 79,1329 мс | 0,15 |
Оптимизатсияи SpartialParallelVector | 4800 | 12,785,824 мс | 98,6947 мс | 87,4903 мс | 0,11 |