Міністерство освіти і науки України
Національний університет “Львівська політехніка”
Кафедра ЕОМ
/
Звіт
З лабораторної роботи №3
з дисципліни: «Алгоритми та моделі обчислень»
на тему:
«використання потокового графу алгоритму при проектуванні паралельних обчислень.»
Львів 2022
Мета роботи: ознайомитися з використанням потокового графу алгоритму при паралельному програмуванні.
Завдання
Скласти програму (C/C++), яка дозволяє провести порівняння двох реалізацій обчислення виразу(звичайний послідовний код та код на основі інструкцій SSE2, що відображає потоковий граф алгоритму) за характеристикою часової складності для вхідних даних, що вводяться під час роботи програми.
Варіант №24/
Потоковий граф алгоритму
/
Код програми
#include <stdio.h>
#include <stdlib.h>
#include <intrin.h> // Windows
#include <math.h>
#include <time.h>
#define NANOSECONDS_PER_SECOND_NUMBER 1000000000
#define DATA_TYPE double
#define DATA_TYPE_PTR_ DATA_TYPE *
#define DATA_TYPE_PTR DATA_TYPE_PTR_ volatile
#define A 1.
#define B 2.
#define C 3.
#define D 4.
#define E 5.
#define F 6.
#define G 7.
#define H 8.
#define dAC dArr
#define dA dAC
#define dC (dAC + 1)
#define dBD (dArr + 2)
#define dB (dBD)
#define dD (dBD + 1)
#define dEG (dArr + 4)
#define dE dEG
#define dG (dEG + 1)
#define dFH (dArr + 6)
#define dF dFH
#define dH (dFH + 1)
#define dResult (dArr + 8)
#define dY dResult
#define REPEAT_COUNT 1000000
#define REPEATOR(count, code) \
for (unsigned int indexIteration = (count); indexIteration--;){ code; }
double getCurrentTime() {
clock_t time = clock();
if (time != (clock_t)-1) {
return ((double)time / (double)CLOCKS_PER_SEC);
}
return 0.;
}
void run_native(DATA_TYPE_PTR const dArr) {
*dY = (*dA - *dB) / (*dC - *dD) + (*dE + *dF) / (*dG + *dH);
}
void run_SSE2(DATA_TYPE_PTR const dArr) {
__m128d d__AC__;
__m128d d__BD__;
__m128d d__EG__;
__m128d d__FH__;
__m128d d__AC_BD__;
__m128d d__AC_BD__1;
__m128d d__EG_FH__1;
__m128d d__EG_FH__;
__m128d d__ACEGBDFH__;
d__AC__ = _mm_load_pd(dAC);
d__BD__ = _mm_load_pd(dBD);
d__EG__ = _mm_load_pd(dEG);
d__FH__ = _mm_load_pd(dFH);
// (a-b) (c-d)
d__AC_BD__ = _mm_sub_pd(d__AC__, d__BD__);
// (e+f) (g+h)
d__EG_FH__ = _mm_add_pd(d__EG__, d__FH__);
//(a-b)/(c-d)
d__AC_BD__1 = _mm_unpackhi_pd(d__AC_BD__, d__AC_BD__);
d__AC_BD__ = _mm_div_pd(d__AC_BD__, d__AC_BD__1);
//(e+f)/(g+h)
d__EG_FH__1 = _mm_unpackhi_pd(d__EG_FH__, d__EG_FH__);
d__EG_FH__ = _mm_div_pd(d__EG_FH__, d__EG_FH__1);
//(a-b)/(c-d)+(e+f)/(g+h)
d__ACEGBDFH__ = _mm_add_sd(d__AC_BD__, d__EG_FH__);
_mm_store_sd(dResult, d__ACEGBDFH__);
}
void printResult(char* const title, DATA_TYPE_PTR const dArr, unsigned int runTime) {
printf("%s:\r\n", title);
printf("y = (A - B) / (C - D) + (E + F) / (G + H)\r\n");
printf("A = %0.0f, B = %0.0f, C = %0.0f, D = %0.0f, E = %0.0f, F = %0.0f, G = %0.0f, H = %0.0f;\r\n\n", *dA, *dB, *dC, *dD, *dE, *dF, *dG, *dH);
printf("(%0.0f - %0.0f) / (%0.0f - %0.0f) + (%0.0f + %0.0f) / (%0.0f + %0.0f) = %0.3f\r\n", *dA, *dB, *dC, *dD, *dE, *dF, *dG, *dH, *dY);
printf("run time: %dns\r\n\r\n", runTime);
}
int main() {
DATA_TYPE_PTR const dArr = (DATA_TYPE_PTR_ const)_mm_malloc(63 * sizeof(DATA_TYPE), 16);
*dA = A;
*dB = B;
*dC = C;
*dD = D;
*dE = E;
*dF = F;
*dG = G;
*dH = H;
double startTime, endTime;
startTime = getCurrentTime();
REPEATOR(REPEAT_COUNT,
run_native(dArr);
)
endTime = getCurrentTime();
printResult((char*)"x86",
dArr,
(unsigned int)((endTime - startTime) * (NANOSECONDS_PER_SECOND_NUMBER / REPEAT_COUNT)));
*dY = 0;
startTime = getCurrentTime();
REPEATOR(REPEAT_COUNT,
run_SSE2(dArr);
)
endTime = getCurrentTime();
printResult((char*)"SSE2",
dArr,
(unsigned int)((endTime - startTime) * (NANOSECONDS_PER_SECOND_NUMBER / REPEAT_COUNT)));
_mm_free(dArr);
return 0;
}
Результат виконання
/
Висновок: виконавши дану лабораторну роботу, я ознайомився з командами SSE2, навчився розробляти програми на їх основі.