Міністерство освіти і науки України
Національний університет «Львівська політехніка»
Кафедра ЕОМ
КУРСОВА РОБОТА
з дисципліни:
«Паралельні та розподілені обчислення»
на тему:
«Паралельне виконання операцій множення матриць»
Львів – 2013
Завдання
Розробити схему та описати процедуру перемноження матриці А (розмірністю N1*N2) на матрицю В (розмірністю N2*N3) на структурі з восьми процесорів. Для цієї структури визначити час виконання алгоритму, відсоток послідовної частини алгоритму та ефективність алгоритму.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
С
о
п
у
ш
И
н
с
ь
к
и
й
М
и
к
о
л
а
Б
о
3
1
4
2
5
8
8
6
7
7
N1 = 2Б = 240, N2 = 4П = 296, N3 = 2I =349
Таблиця 1. Часові параметри
Співвідношення часових параметрів
Пояснення
tu = tw
час виконання однієї операції множення
ts = 1/10 * tw
час виконання однієї операції сумування
tp = 1/8 * tw
час виконання однієї операції пересилання даних між процесорами
tz = 1/7 * tw
час виконання операції завантаження одних даних
tW
час виконання операції вивантаження одних даних
Таблиця 2. Кодування букв
3
6
2
5
10
13
14(17
11(12
п
и
о
Ш
к
м
л
й
219
91
250
157
47
43
212
146
DB
5B
FA
9D
2F
2B
D4
92
Таблиця 3. Матриця суміжності
1
2
3
4
5
6
7
8
1
0
1
0
1
1
0
1
1
2
0
0
0
1
1
0
1
1
3
1
1
0
1
1
0
1
0
4
1
0
0
0
1
1
0
1
5
0
0
1
0
0
1
1
1
6
0
0
1
0
1
0
1
1
7
1
1
0
1
0
1
0
0
8
1
0
0
1
0
0
1
0
z = 0909760 (номер залікової книжки)
Type = (i) mod 3 + 1 = (0 + 9 + 0 + 9 + 7 + 6 + 0) mod 3 = 31 mod 3 = 1
Type = 1 спільна пам’ять.
Анотація
В даній курсовій роботі розроблено алгоритм паралельного перемноження матриць на структурі з восьми процесорів. Завантаження даних відбувається з спільної пам’яті. Вхідні матриці мають розмірності А(240*296) та В(296*349).
Робота складається з розрахунку часових характеристик алгоритму, розробки функціональної схеми алгоритму та програмної реалізації. Відповідно до часових затрат паралельного алгоритму визначено його ефективність відносно послідовного.
Програмно, алгоритм реалізований на Java з консольним інтерфейсом. Паралелізація досягнута за допомогою потоків.
Зміст
Вступ 5
1. Теоретичний розділ 6
2. Розробка функціональної схеми 9
4. Розрахунковий розділ 12
5. Розробка програми 14
Висновки 17
Література 18
Вступ
Паралельні обчислювальні системи - комп'ютерні системи, що реалізовують тим або іншим способом паралельну обробку даних на багатьох обчислювальних вузлах для підвищення загальної швидкості розрахунку. Ідея розпаралелення обчислень базується на тому, що більшість завдань можуть бути розділені на набір менших завдань, які можуть бути вирішені одночасно. Зазвичай паралельні обчислення вимагають координації дій.
Паралельні алгоритми досить важливі з огляду на постійне вдосконалення багатопроцесорних систем і збільшення числа ядер у сучасних процесорах. Зазвичай простіше сконструювати комп'ютер з одним швидким процесором, ніж з багатьма повільними з тією ж продуктивністю. Однак збільшення продуктивності за рахунок вдосконалення одного процесора має фізичні обмеження, такі як досягнення максимальної щільності елементів та тепловиділення. Зазначені обмеження можна подолати лише шляхом переходу до багатопроцесорної архітектури, що виявляється ефективним навіть у малих обчислювальних системах. Складність послідовних алгоритмів виявляється в обсязі використаної пам'яті та часу (число тактів процесора), необхідних для виконання алгоритму.
Розподілені обчислення - спосіб розв'язання трудомістких обчислювальних завдань з використанням двох і більше комп'ютерів, об'єднаних в мережу. Розподілені обчислення є окремим випадком паралельних обчислень, тобто одночасного розв'язання різних частин одного обчислювального завдання декількома процесорами одного або кількох комп'ютерів. Тому необхідно, щоб завдання, що розв'язується було сегментоване — розділене на підзадачі, що можуть обчислюватися паралельно. При цьому для розподілених обчислень доводиться також враховувати можливу відмінність в обчислювальних ресурсах, які будуть доступні для розрахунку різних підзадач. Проте, не кожне завдання можна «розпаралелити» і прискорити його розв'язання за допомогою розподілених обчислень.
Теоретичний розділ
Паралельна система складається з певної кількості процесорів та модулів пам’яті. В даному випадку це структура з 8 процесорів та спільна пам’ять.
Множення матриці на матрицю або матриці на вектор є базовими мікроопераціями різних задач. Для їх реалізації використовують різні алгоритми та різні структури.
Для вирішення цієї задачі використовується алгоритм, при якому матриця А розбивається на 8 горизонтальних смуг, а матриця В – на 8 вертикальних, в такому разі матриця результату буде складатись з 8 горизонтальних смуг див. рис. 1.1.
Рис. 1.1 Розбиття матриць
Кожний процесор зчитує з пам’яті відповідну підматрицю А та підматрицю В. Після того як процесор помножив під матрицю А на підматрицю В, він обмінюється з іншим процесором підматрицею В. Підматриця А завжди знаходиться у відповідному процесорі, а підматриці В рухаються по всіх процесорах. Отже кожен процесор повинен помножити відповідну підматрицю А на всі підматриці В. В результаті всіх множень у пам’яті буде результуюча матриця. Однак обмін підматрицями В між процесорами відбувається не в довільному порядку. Схема можливого обміну відображена у графі див. рис. 1.2.
Проте обмін буде відбуватися по кільцю з процесорів (1,2,5,3,4,6,8,7,1), яке можна виділити з графу див. рис. 1.3.
Рис. 1.2 Граф можливиш шляхів обміну даними між процесорами
Рис. 1.3 Граф обміну даними між процесорами
Оскільки в нас спільна пам’ять то завантаження вхідних та вивантаження вихідних даних буде виконуватись послідовно. Це досить збільшує час виконання алгоритму, що є суттєвим мінусом даної реалізації. В такому випадку граф обміну даних між процесорами та пам’яттю набуде слідуючого вигляду див. рис. 1.4.
Рис. 1.4 Граф обміну даними між процесорами та пам’яттю
Збір даних буде проводитися також по кільцю за такою схемою (з умовою, що процесор в одночасно може або отримувати дані або пересилати) :
Р2(C2) => P5; P4(C4) => P3; P6(C6) => P8; P7(C7) => P1;
P5(C5, C2) => P4; P3(C4, C3) => P6; P8(C8, C6) => P7;
P4(C5, C2) => P3; P6(C4, C3) => P8; P7(C8, C6) => P1;
P3(C5, C2) => P6; P8(C4, C3) => P7;
P6(C5, C2) => P8; P7(C4, C3) => P1;
P8(C5, C2) => P7;
P8(C5, C2) => P1;
У першому такті пересилається одна підматриця Ві. У всіх решта – дві.
Розробка функціональної схеми
Рис. 2.1. Функціональна схема (частина 1)
Рис. 2.2. Функціональна схема (частина 2)
Таблиця 2.1. Опис елементів функціональної схеми
Елемент
Значення
Цей елемент представляє процедуру завантаження вхідних даних з пам’яті. В даному прикладі, завантажується підматриця А1 та підматриця В1.
Елемент представляє процедуру вивантаження результуючих даних в пам’ять. В даному прикладі, вивантажується результат множення підматриць А1 на В1.
Даний блок показує процедуру множення підматриць.
Цей елемент представляє процедуру відправки даних іншому процесору. В цьому прикладі, підматриця В1 відсилається на процесор з номером 2.
Цей елемент представляє процедуру отримання даних з іншого процесора. В цьому випадку, отримується підматриця В2 з процесора з номером 2.
Дана схема не показує істинні часові параметри виконання. Вона показує взаємозв’язки пов’язані з синхронізацією потоків, що моделюють процеси. Затримки показні на схемі пов’язані з розміром елементів схеми і не обов’язково виникають при виконанні програми. В таблиці 2.1 наведений опис елементів функціональної схеми.
Звернення процесорів до пам’яті відбувається послідовно. Кожен процесор може одночасно обмінюватись даними лише з двома іншими, з якими у нього встановленні зв’язки. У кожного процесора є по два буфери для зберігання вхідних під матриць та один буфер для тимчасового зберігання результату. Операція множення на всіх процесорах виконується паралельно.
В даній схемі після кожного множення, результат записується в пам'ять. Відповідно в пам’яті, з кожним кроком виконання алгоритму, в певній позиції буде з’являтись результуюча підматриця. Також кожен раз при переваванні копіюється значення під матриць. Такий підхід дещо впливає на час виконання алгоритму. Але незважаючи на цей мінус, цей метод дозволяє масштабувати обчислювальну систему. Тобто можна легко додати процесор в класі MultiThreadCalc.
Кожен з процесорів даної структури виконує по 8 операцій множення і відповідно 8 операцій вивантаження результату. Всі пересилання даних між процесорами відбуваються лише по зв’язках зображених на графі обміну даних див. рис. 1.3, що взяті з матриці суміжності на таблиця 3. Можна помітити, що пересилання на функціональній схемі в більшій мірі зосереджені на початку роботи алгоритму. Дальше кількість пересилань різко меншає, і якраз на тих завершальних етапах відбувається прискорення роботи.
Розрахунковий розділ
Дані завантажуються з спільної пам’яті.
Розміри матриць: N1 = 240; N2 = 296; N3 = 349
Розміри підматриць: A1-A8: [30, 296]B1-B7: [296, 43]; B8: [296, 48]
Розміри для розрахунків: N1M = 30; N3M = max(43, 48) = 48.
Час завантаження це сумарний час завантаження усіма процесорами по одній під матриці А та В. В один момент часу з пам’яті може читати лише один процесор.
Виразимо часові параметри через найменший :
– час виконання однієї операції сумування;
– час виконання однієї операції множення;
– час виконання однієї операції пересилання даних між процесорами;
– час виконання операції завантаження одних даних;
– час виконання операції вивантаження одних даних.
Час виконання виразу буде рівний
Оскільки за умовами в нас є спільна пам'ять, то звернення кожного процесору до пам'яті і завантаження даних буде виконуватися послідовно, тобто загальний час буде
Оскільки розміри під матриць Bі не є однаковими, час множення буде не однаковий і виникатимуть затримки під час обміну під матрицями Ві між процесорами. Тому при підрахунку загального часу виконання операцій множення та додавання будемо використовувати найбільші значення розмірів під матиць Ві.
Аналогічно максимальне значення другого виміру матриці Ві будемо використовувати для обчислення загального часу обміну даними між процесорами в тому числі і збору результату в першому процесорі.
Оскільки одночасно процесор може пересилати або приймати дані, тому час обміну даними між процесорами буде проводитися в два етапи.
Про те, чому саме коефіцієнт 13 використаний в даній формулі, розписано в кінці теоретичного розділу.
Обчислюємо загальний час виконання множення матриць на даному паралельному алгоритмі:
Для обчислення ефективності даного алгоритму, потрібно визначити час виконання множення даних матриць на одному процесорі. В цьому випадку, час завантаження та вивантаження даних не зміниться, пересилання і збір проводити не потрібно, тому даний час не враховується. Час виконання операцій множення та сумування обчислюється відповідно N1 * N2 * N3 * tU та N1 * (N2-1) * N3 * tS.
Ефективність визначається як відношення часу виконання алгоритму на однопроцесорній системі, до часу потрібного для виконання на багатопроцесорної системі, помноженого на кількість процесорів в ній.
Ефективність визначається як відношення часу виконання алгоритму на однопроцесорній системі, до часу потрібного для виконання на багатопроцесорної системі, помноженого на кількість процесорів в ній.
Розробка програми
Лістинг програми наведений у додатку А. Дана програма написана на мові Java. Програма має консольний інтерфейс. Паралелізація досягнута за допомогою використання потоків. Загальна схема роботи програми зображена на рис. 4.2.
Рис. 4.1. Вікно, після виконання програми.
В програмі спільний доступ до памяті було реалізовано за допомогою м’ютексу. Для виконання завдання, було написано клас MemoryDirector, що містить в собі методи читання з пам’яті та запису до неї під матриць то результатів поботи програми. Клас MemoryDirector також виконує генерацію початкових даних. Даний клас реалізовано згідно паттерну Singleton тому ми завжди отримуємо один і той самий об’єкт класу.
Структура зв’язку між процесорами формується класом MultiThreadCalc. Тобто в ньому створюються і запускаються на виконання потоки. Формуються зв’язки між процесорами. Він також забезпечує синхронізацію виконання з головним потоком.
Рис. 4.2. Загальна схема роботи програми
Кожен процес представлений класом Worker. Загальна схема роботи процесора зображена на рис. 4.3. Звязки між процесорами встановлено за допомою класу ConcurrentLinkedQueue. Тобто процесори зв’язані один з одним через ConcurrentLinkedQueue, яка забезпечує синхронізацію між потоками. При створенні об’єкту даного класу йому ткож передається семафор для синхронізації з говним потоком. Спочатку в кожен процесор завантажуються відповідні під матриці, далі вони перемножуються. Відповідна підматриця передається наступному процесору через ConcurrentLinkedQueue q_next, а результат записується в пам’ять через MemoryDirector. Далі подібна процедура отримання даних від попереднього процесора, перемноження під матриць, пересилання під матриці та збереження результату повторується доки всі процеси не відпрацюють множення 8 раз. По закінченні роботи кожен процесор зменшує спільний семафор, при закінченні всіх потоків, які преставляють процесори його значення буде рівне 0 і головний потік продовжить роботу.
Матриці реалізовано в зовнішньому пакеті JAMA, який знаходиться в файлі Jama-1.0.3.jar, який підключається до проекту, як зовнішня бібліотека. Пакем містить клас, який ревлізує матриці а також базові операції над матрицями зокрема множення.
Рис. 4.3. Загальна схема роботи процесора.
Підматриці реалізовано класом SubMatrix, який є класом-обгорткою і містить додаткові поля для ідентифікації під матриці в головній матриці і перевизначені методи виводу.
Розповсюдження вхідних даних виконується не напряму, а із врахуванням структури системи (з врахуванням фізичних зв’язків між вузлами системи). Для тестування достовірності результатів, біло створено клас SingleThreadCalc, в якому матриці множаться послідовно і записуються в пам’ять. Результат записуються в файл.
Висновки
Під час виконання курсової роботи я розробив паралельний алгоритм перемноження двох матриць на структурі з восьми процесорів та його програмну реалізацію. Також провів обчислення, і отримав часові характеристики даного паралельного алгоритму. Після чого порівняв його із звичайним послідовним алгоритмом, який у висновку виконується у 7,04 раз довше, ніж паралельний. Ефективність розпаралелення алгоритму виявилась рівною Е ≈ 0,88, що є досить непоганим показником, враховуючи особливість вивантаження даних у цьому алгоритмі.
Програмна реалізація моделює роботу багатопроцесорної системи за допомогою використання потоків.
Література
Організація паралельних обчислень: Навчальний посібник з дисципліни “Паралельні та розподілені обчислення” для студентів базового напрямку 6.0915 "Комп'ютерна інженерія" / Укладачі: Є. Ваврук, О. Лашко – Львів: Національний університет “Львівська політехніка”, 2007, 70 с.
Бройнль Т. Паралельне програмування: Початковий курс: Навчальний посібник. – К.:Вища школа.,1997.
Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. – СПб: БХВ-Петербург, 2002.
Воеводин В.В. Математические основы параллельных вычислений.- М.: Изд-во МГУ, 1991.
Воеводин В.В. Вычислительные основы линейной алгебры. – М.:Наука, 1977. – 304 с.
Ортега Дж. Введение в параллельные и векторные методы решения линейных систем. М.:Мир, 1991.
Программирование на параллельных вычислительных системах: Пер с англ./Под ред. Р.Бэбба.М.:Мир, 1991.
Векторизация программ: теория, методы, реализация: Пер. с англ. и нем. /Под ред. Г.Д.Чинина. - М:. Мир, 1991.
Корнеев В.В. Параллельные вычислительные системы. М.: Нолидж, 1999
С. Немнюгин, О.Стесик Параллельное программирование для многопроцессорных вычислительных систем. – СПб: БХВ-Петербург, 2002.
Питерсон Дж. Теория сетей Петри і моделирования систем: Пер. с англ. -М.: Мир, 1984. -264 с., ил.
ДОДАТОК А
Файл Kursec.java
package ua.edu.lp.sopushynskyy.pro.kursec;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import Jama.Matrix;
public class Kursec {
public static void main(String[] args) {
PrintWriter wr;
MultiThreadCalc MultiCalc = new MultiThreadCalc();
MultiCalc.startCalculation();
SingleThreadCalc SingleCalc = new SingleThreadCalc();
SingleCalc.startCalculation();
Matrix multiThreadResult = MemoryDirector.getMemory().getMResult();
Matrix singleThreadResult = MemoryDirector.getMemory().getSResult();
try {
wr = new PrintWriter(new File(Constants.S_RESULT_FILE_NAME));
singleThreadResult.print(wr, 4, 0);
wr = new PrintWriter(new File(Constants.M_RESULT_FILE_NAME));
multiThreadResult.print(wr, 4, 0);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Файл Constants.java
package ua.edu.lp.sopushynskyy.pro.kursec;
public class Constants {
public static String S_RESULT_FILE_NAME = "d:\\S_RESULT.TXT";
public static String M_RESULT_FILE_NAME = "d:\\M_RESULT.TXT";
public static final int PROC_COUNT = 8;
public static final int N1 = 240;
public static final int N2 = 296;
public static final int N3 = 349;
}
Файл SubMatrix.java
package ua.edu.lp.sopushynskyy.pro.kursec;
import Jama.Matrix;
public class SubMatrix {
private Matrix mx;
private String name;
private int num;
SubMatrix(Matrix mx, String name, int num) {
this.mx = mx;
this.name = name;
this.num = num;
}
public Matrix getMx() {
return mx;
}
public void setMx(Matrix mx) {
this.mx = mx;
}
public String toString() {
return name + getNum() + "(" + mx.getRowDimension() + ","
+ mx.getColumnDimension() + ")";
}
public int getNum() {
return num;
}
}
Файл MemoryDirector.java
package ua.edu.lp.sopushynskyy.pro.kursec;
import Jama.Matrix;
public class MemoryDirector {
private Matrix A = Matrix.random(Constants.N1, Constants.N2);
private Matrix B = Matrix.random(Constants.N2, Constants.N3);
private double m_result[][] = new double[Constants.N1][Constants.N3];
private double s_result[][] = new double[Constants.N1][Constants.N3];
private Object mutex = new Object();
private static MemoryDirector mem = new MemoryDirector();
private MemoryDirector() {
}
public static MemoryDirector getMemory() {
return mem;
}
public SubMatrix getSubmatrixB(int prcNum) {
int colInitial;
int colFinal;
synchronized (mutex) {
colInitial = prcNum * (Constants.N3 / Constants.PROC_COUNT);
colFinal = colInitial
+ (Constants.N3 / Constants.PROC_COUNT - 1)
+ ((prcNum == Constants.PROC_COUNT - 1) ? Constants.N3
% Constants.PROC_COUNT : 0);
return new SubMatrix(B.getMatrix(0, Constants.N2 - 1, colInitial,
colFinal), "B", prcNum);
}
}
synchronized public SubMatrix getSubmatrixA(int prcNum) {
int rowInitial;
int rowFinal;
synchronized (mutex) {
rowInitial = prcNum * (Constants.N1 / Constants.PROC_COUNT);
rowFinal = rowInitial
+ (Constants.N1 / Constants.PROC_COUNT - 1)
+ ((prcNum == Constants.PROC_COUNT - 1) ? Constants.N1
% Constants.PROC_COUNT : 0);
return new SubMatrix(A.getMatrix(rowInitial, rowFinal, 0,
Constants.N2 - 1), "A", prcNum);
}
}
public void saveParallelResult(Matrix mx, int subAnum, int subBnum) {
int colInitial;
int rowInitial;
int rowCount;
int colCount;
synchronized (mutex) {
rowInitial = subAnum * (Constants.N1 / Constants.PROC_COUNT);
colInitial = subBnum * (Constants.N3 / Constants.PROC_COUNT);
rowCount = (Constants.N1 / Constants.PROC_COUNT )
+ ((subAnum == Constants.PROC_COUNT - 1) ? Constants.N1
% Constants.PROC_COUNT : 0);
colCount = (Constants.N3 / Constants.PROC_COUNT )
+ ((subBnum == Constants.PROC_COUNT - 1) ? Constants.N3
% Constants.PROC_COUNT : 0);
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < colCount; j++) {
m_result[i + rowInitial][j + colInitial] = mx.getArray()[i][j];
}
}
}
}
public void saveSingleResult(Matrix mx) {
synchronized (mutex) {
for (int i = 0; i < Constants.N1; i++) {
for (int j = 0; j < Constants.N3; j++) {
s_result[i][j] = mx.getArray()[i][j];
}
}
}
}
public Matrix getA() {
return A;
}
public Matrix getB() {
return B;
}
public Matrix getMResult() {
return new Matrix(m_result);
}
public Matrix getSResult() {
return new Matrix(s_result);
}
}
Файл SingleThreadCalc.java
package ua.edu.lp.sopushynskyy.pro.kursec;
public class SingleThreadCalc {
public void startCalculation() {
System.out.println("Start single-thread calc");
long startTime = System.nanoTime();
MemoryDirector.getMemory().saveSingleResult(
MemoryDirector.getMemory().getA()
.times(MemoryDirector.getMemory().getB()));
long endTime = System.nanoTime();
System.out.println("Calc done. Time :"+ Double.toString((endTime - startTime)
/ 1000000000.0));
}
}
Файл MultiThreadCalc.java
package ua.edu.lp.sopushynskyy.pro.kursec;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
public class MultiThreadCalc {
//8 потоків - 8 процесорів
private Thread thread0;
private Thread thread1;
private Thread thread2;
private Thread thread3;
private Thread thread4;
private Thread thread5;
private Thread thread6;
private Thread thread7;
//8 черг представляють канали для обміну підматрицями між процесорами
private ConcurrentLinkedQueue<SubMatrix> q01 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q14 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q43 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q32 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q25 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q57 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q76 = new ConcurrentLinkedQueue <SubMatrix>();
private ConcurrentLinkedQueue<SubMatrix> q60 = new ConcurrentLinkedQueue <SubMatrix>();
CountDownLatch isDone = new CountDownLatch(Constants.PROC_COUNT);
private void buildProcStructure()
{
//формування кільця з процесорів з заданням
thread0 = new Thread(new Worker(0,q60,q01,isDone));
thread1 = new Thread(new Worker(1,q01,q14,isDone));
thread2 = new Thread(new Worker(4,q14,q43,isDone));
thread3 = new Thread(new Worker(3,q43,q32,isDone));
thread4 = new Thread(new Worker(2,q32,q25,isDone));
thread5 = new Thread(new Worker(5,q25,q57,isDone));
thread6 = new Thread(new Worker(7,q57,q76,isDone));
thread7 = new Thread(new Worker(6,q76,q60,isDone));
}
public void startCalculation()
{
System.out.println("Start multi-thread calc");
long startTime = System.nanoTime();
buildProcStructure();
thread0.start();
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread7.start();
try {
isDone.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.nanoTime();
System.out.println("Calc done. Time :" + Double.toString((endTime - startTime) / 1000000000.0));
}
}
Файл Worker.java
package ua.edu.lp.sopushynskyy.pro.kursec;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import Jama.Matrix;
public class Worker implements Runnable {
private ConcurrentLinkedQueue<SubMatrix> q_prev;
private ConcurrentLinkedQueue<SubMatrix> q_next;
private SubMatrix sub_B;
private SubMatrix sub_A;
private Matrix tmpResult;
private CountDownLatch isDone;
private int procNumber;
public Worker(int procNum, ConcurrentLinkedQueue<SubMatrix> q1,
ConcurrentLinkedQueue<SubMatrix> q2, CountDownLatch w) {
q_prev = q1;
q_next = q2;
isDone = w;
procNumber = procNum;
}
@Override
public void run() {
boolean flag = false;
// Завантаження підматриці А
sub_A = MemoryDirector.getMemory().getSubmatrixA(procNumber);
if (flag)
System.out.printf("thread: %2d message:\"loaded data: %s\"\n",
procNumber, sub_A);
// Завантаження підматриці B
sub_B = MemoryDirector.getMemory().getSubmatrixB(procNumber);
if (flag)
System.out.printf("thread: %2d message:\"loaded data: %s\"\n",
procNumber, sub_B);
// Обчислення першого множення підматриць
tmpResult = sub_A.getMx().times(sub_B.getMx());
if (flag)
System.out.printf("thread: %2d message:\"calculated %s*%s\"\n",
procNumber, sub_A.toString(), sub_B.toString());
// Збереження результатів
MemoryDirector.getMemory().saveParallelResult(tmpResult, sub_A.getNum(),
sub_B.getNum());
if (flag)
System.out.printf("thread: %2d message:\"result saved %s*%s\"\n",
procNumber, sub_A.toString(), sub_B.toString());
// Пересилання підматриці далі по кільцю
q_next.add(sub_B);
if (flag)
System.out.printf("thread: %2d message:\"sended data: %s\"\n",
procNumber, sub_B);
// Цикл буде пройдено 7 раз що залишились
for (int i = 0; i < Constants.PROC_COUNT - 1; i++) {
// очікування даних від попереднього процесора