
- •Isbn 978-601-217-247-8
- •Есептеу машиналары
- •§1.1.1 Параллель виртуалды машиналар
- •1 Сурет. Vm/sp, vм/ха, vn/еsа машиналары
- •2 Сурет. Виртуалды машина жүйесі
- •3 Сурет. VMware терезесі
- •4 Сурет. Бір компьютерде бірнеше операциялық жүйе
- •5 Сурет. Вм консолынің мысалы
- •6 Сурет. Виртуалды машиналар жүйесінің архитектурасы
- •§1.1.2 Виртуалды машиналардың түрлері
- •§1.1.3 Виртуаландыру - ақпараттық жүйелерді реттеудің басты жолы
- •§1.2.1 Компьютердің аппараттық құрылымын жетілдіру
- •7 Сурет. Бір операцияны бес тактіде орындайтын тізбекті құрылғының
- •8 Сурет. Бір операцияны әрбірі бес тактіде орындайтын екі бірдей тізбекті
- •10 Сурет. Конвейерлік құрылғы өнімділігінің кіріс деректер
- •11 Сурет. Illiac IV матрицалық жүйесінің жобасы
- •§1.2.2 Компьютерді басқарудың интеллектуалдығын жоғарылату
- •12 Сурет. Ортақ жадылы параллель компьютерлер
- •13 Сурет. Таратылған жадылы параллель компьютерлер
- •14 Сурет. Ортақ шиналы мультипроцессорлық жүйе.
- •15 Сурет. Матрицалық коммутаторлардағы
- •16 Сурет. Омега - желі мультипроцессорлық жүйесі.
- •17 Сурет. Мультикомпьютерлерлік жүйелер байланыс топологияларымен: а – сызықша; б – дөңгелек; в – жұлдызша
- •18 Сурет. Процессорлардың байланыс топологияларының нұсқалары
- •19 Сурет. Сm* есептеу жүйесінің сызбасы
- •20 Сурет. Bbn Butterfly есептеу жүйесінің сызбасы
- •§1.2.3 Функционалды құрылғылар жүйесі
- •§1.3.1 Параллель компьютерлер және жүйелер классификациясы
- •21 Сурет. М. Флин классификациясының sisd және simd кластары
- •22 Сурет. М. Флин классификациясының misd және mimd кластары
- •23 Сурет. Mimd класына р. Хокнидың қосымша
- •§1.3.2 Векторлы-конвейерлік компьютелер
- •24 Сурет. Cray c90 компьютерінің жалпы сүлбесі
- •25 Сурет. Cray c90 компьютері жадысының бөлінуі
- •26 Сурет. Cray c90 компьютерінде векторлық операциялардың орындалуы
- •27 Сурет. Cray c90 компьютерінде векторлық операциялардың ілінісуі
- •§1.3.3 Ортақ жадылы параллель компьютерлер
- •28 Сурет. Hewlett Packard Superdome компьютері
- •29 Сурет. Hewlett Packard Superdome компьютерінің
- •§1.3.4 Таратылған жадылы есептеу жүйелері
- •30 Сурет. Cray t3e компьютерінің коммуникациялық торы
- •31 Сурет. Cray т3d/t3e компьютерлеріндегі барьерлі синхрондау
- •32 Сурет. Есептеу кластерінің жалпы схемасы
- •33 Сурет. Мвс-1000м суперкомпьютерінің құрылымы
- •34 Сурет. Коммуникациялық ортаның латенттілігі және өткізу қабілеті
- •§1.3.5 Метакомпьютинг
- •§2.1.1 Үлкен есептер және үлкен компьютерлер
- •35 Сурет. Сандық эксперименттің этаптары
- •§ 2.1.2 Алгоритм графы және параллель есептеулер
- •§ 2.1.3 Шексіз параллелділік концепциясы
- •§ 2.1.4 Ішкі параллельділік
- •37 Сурет. Матрицаларды көбейту графы
- •38 Сурет. Үшбұрышты жүйелерге арналған графтар
- •39 Сурет. Блокты-екідиагоналды жүйеге арналған Макрограф
- •40 Сурет. Блокты-екідиагоналды жүйеге арналған граф
- •41 Сурет. Жалпыланған пралллель форманың ярустары
- •42 Сурет. Графтағы микро және макропараллельділік
- •§2.2.1 Дәстүрлі тізбекті тілдерді пайдалану.
- •§2.2.2 OpenMp бағдарламалау технологиясы
- •44 Сурет. ОреnМр: бағдарламаның орындалу процесі
- •§2.2.3 Хабарлама жіберу негізіндегі бағдарламалау жүйелері. Mpi бағдарламалау жүйесі
- •Int mpi_Comm_rank(mpi_Comm comm, int *rank)
- •Int mpi_Send(void *buf, int count, mpi_Datatype datatype, int dest, int msgtag, mpi_Comm comm)
- •Integer count, datatype, dest, msgtag, comm, request, ierr
- •Int mpi_Isend(void *buf, int count, mpi_Datatype datatype, int dest, int msgtag, mpi_Comm comm, mpi_Request *request)
- •Int mpi_Irecv(void *buf, int count, mpi_Datatype datatype, int source, int msgtag, mpi_Comm comm, mpi_Request *request)
- •Integer count, datatype, source, msgtag, comm, request, ierr
- •Int main(argc,argv)
- •Int argc;
- •Include 'mpif.H’
- •Integer ierr, rank, size, prev, next, reqs(4), buf(2)
- •Integer stats(mpi_status_size, 4)
- •Int mpi_Waitany( int count, mpi_Request *requests, int *index, mpi_Status *status)
- •Integer count, requests(*), index, status(mpi_status_size), ierr
- •Int mpi_Waitsome( int incount, mpi_Request *requests, int *outcount, int *indexes, mpi_Status *statuses)
- •Integer incount, requests(*), outcount, indexes(*), ierr,
- •Int mpi_Test(mpi_Request *request, int *flag, mpi_Status *status)
- •Integer request, ierr, status(mpi_status_size)
- •Int mpi_Testall( int count, mpi_Request *requests, int *flag, mpi_Status *statuses)
- •Integer count, requests(*), statuses(mpi_status_size,*), ierr
- •Int mpi_Testany(int count, mpi_Request *requests, int *index, int *flag, mpi_Status *status)
- •Integer count, requests(*), index, status(mpi_status_size), ierr
- •Int mpi_Testsome( int incount, mpi_Request *requests, int *outcount, int *indexes, mpi_Status *statuses)
- •Integer incount, requests(*), outcount, indexes(*), ierr,
- •Int mpi_Iprobe( int source, int msgtag, mpi_Comm comm, int *flag, mpi_Status *status)
- •Include 'mpif.H’
- •Integer ierr, rank, size, n, nl, I, j
- •Integer irr, status(mpi_status_size), req(maxproc*2)
- •If(ir .Ne. Rank)
- •Int mpi_Send_init( void *buf, int count, mpi_Datatype datatype, int dest, int msgtag, mpi_Comm comm, mpi_Request *request)
- •Integer count, datatype, dest, msgtag, comm, request, ierr
- •Int mpi_Recv_init( void *buf, int count, mpi_Datatype datatype, int source, int msgtag, mpi_Comm comm, mpi_Request *request)
- •Integer count, datatype, source, msgtag, comm, request, ierr
- •Integer сомм, ierr
- •Include 'mpif.H’
- •Integer ibuf(maxproc)
- •Integer req(2*maxproc), statuses(mpi_status_size, maxproc)
- •Integer count, datatype, root, comm, ierr
- •Integer scount, stype, rcount, rtype, root, comm, ierr
- •Integer scount, stype, rcounts(*), displs(*), rtype, root, comm, ierr
- •Integer scount, stype, rcount, rtype, root, comm, ierr
- •Int mpi_Bcast(void *buf, int count, mpi_Datatype datatype, int source, mpi_Comm comm)
- •Int mpi_Gather( void *sbuf, int scount, mpi_Datatype stype, void *rbuf, int rcount, mpi_Datatype rtype, int dest, mpi_Comm comm)
- •Int mpi_Scatter(void *sbuf, int scount, mpi_Datatype stype, void *rbuf, int rcount, mpi_Datatype rtype, int source, mpi_Comm comm)
- •Int main(argc,argv)
- •Int argc;
- •Int numtasks, rank, sendcount, recvcount, source;
- •Int mpi_Barrier (mpi_Comm comm)
- •§ 2.2.4 Бағдарламалаудың басқа тілдері және жүйелері.
- •Параллель есептеуде қолданылатын қысқаша қазақша-орысша терминологиялық сөздік
- •Параллель есептеуде қолданылатын қысқаша орысша-қазақша терминологиялық сөздік
- •Және орта айнымалылары
- •Mpi функциялары
44 Сурет. ОреnМр: бағдарламаның орындалу процесі
Бастапқы уақыт мезетінде жіп-мастер (нить-мастер) немесе "басты" жіп туындайды және ол бағдарламаның орындалуын стартты нүктеден бастайды. Сонымен қатар ол тек өзі ғана бағдарламаның барлық тізбекті облыстарын орындайды. Параллельділікті қолдау үшін мұнда FORK/JOIN схемасы пайдаланылады. Параллель облысқа енген кезде жіп-мастер қосымша жіптерді туындатады (FORK операциясы орындалады). Одан кейін әрбір қосымша жіп өзінің бірегей нөмірін алады, ал жіп-мастердің нөмірі әрқашан 0. Барлық қосымша туындаған жіптер пареллель облысқа сәйкес келетін бір кодты ғана орындайды. Параллель облыстан шығар мезетте басты жіп қалған жіптердің аяқталуын күтеді және бағдарламаның ары қарай орындалуын өзі ғана жалғастырады (JOIN операциясы орындалады).
Параллель облыста бағдарламаның барлық айнымалылары екі класқа бөлінеді: жалпы (SHARED) және жергілікті (PRIVATE). Жалпы айнымалылар бағдарлама үшін әрқашанда бір экземплярда ғана болып, барлық жіптерге бір атпен ғана қолжетімді болады. Жергілікті айнымалыларды хабарлау әр жіп үшін әрбір айнымалының өз экземплярын туындатады. Жіптің өзінің жергілікті айнымалысының мәнін өзгертуі, басқа жіптердегі дәл осы жергілікті айнымалының мәнінің өзгеруіне ешқандай әсері жоқ. Негізінде, бағдарлама облыстарының соңғы қарастырылған түсініктері және айнымалылар кластары ОреnМР шеңберінде параллель бағдарламалар жазудың жалпы идеяларын анықтайды: бағдарлама текстінің кейбір фрагменттері параллель облыстар болып хабарланады; тек осы облыстар ғана жіптер жиынымен орындалады, ал олар жалпы айнымалылармен де жергілікті айнымалылармен де жұмыс істей алады.
ОреnМР негізгі конструкциясы мен базалық құрылымын қарастырайық [2]. Ондағы барлық директивалар комментарилерде орналасады және келесі комбинациялардың бірінен басталады: !$омр, с$омр немесе *$омр (еске сала кетсек '!', 'с' или '*' символдарының бірінен басталатын жол, Fortran тілінің ережелері бойынша комментарий болып есептеледі).
Бұдан әрі нақты директиваларды суреттеу кезінде жазуды қысқарту үшін, кейбір кездерде біз бұл префикстерді көрсетпейтін боламыз, әрине шын бағдарламаларда олар әрқашан қатысуы қажет.
ОреnМР-ға қатысты барлық функциялар және қоршаған орта айнымалылары омр_ префиксынан басталады.
Параллель облыстарды бейнелеу. Бағдарламадағы параллель облыстарды анықтау үшін директивалардың келесі жұбы пайдаланылады
!$ОМР PARALLEL
<бағдарламаның параллель коды>
!$ОМР END PARALLEL
Берілген екі директиваның арасына орналасқан параллель кодты орындау үшін, жіп-мастер қосымша omp_num_threads - 1 жіп туғызады, мұндағы omp_num_threads – орта айнымалысы. Оның мәнін пайдаланушы бағдарламаны жүктеудің алдында береді және өзгерте алады. Жіп-мастердің нөмірі әрқашан 0 болатынын жоғарыда айтқанбыз. Барлық жіптер берілген директивалар арасындағы кодты орындайды.
end parallel директивасынан кейін автоматты түрде барлық жіптердің айқын емес синхрондалуы жүреді. Қалай барлық жіптер осы нүктеге жетісімен жіп-мастер бағдараманың келесі бөлігін орындауды жалғастырады, ал қалған жіптер жойылады.
Параллель секциялар бірінің ішінде бірі болуы мүмкін. Арнайы айтылмаса, олар бір жіппен орындалады. Мұндай қабатталып орналасқан секцияларды өңдеуге қажетті стратегияны omp_nested айнымалысы анықтайды, ал олардың мәндерін omp_set_nested функциясының көмегімен өзгертуге болады.
Параллель секцияларда жіптердің санын өзгертуге болады. Егер OMP_DYNAMIC айнымалысының мәні 1-ге тең болса, онда OMP_SET_NUM_THREADS функциясының көмегімен пайдаланушы omp_num_threads айнымалысының мәнін өзгерте алады, олай болса параллель секцияға кірген кездегі туындаған жіптер санын да өзгерте аламыз. OMP_DYNAMIC айнымалысының мәні OMP_SET_DYNAMIC функциясымен бақыланады.
Параллель секция кодының параллель орындалуын және жіптер туындауының қажеттілігін, пайдаланушы if қосымша опциясы көмегімен келесі директивада анықтай алады:
!$ОМР PARALLEL IF(<шарт>)
Егер <шарт> орындалмаса, онда директива қосылмайды, бағдарламаны өңдеу бұрынғы режимде жалғасады.
Жоғарыда, туындаған жіптердің бәрі бір ғана кодты орындайды деп айтылған болатын. Енді олардың арасында жұмысты қалай тиімді түрде бөлуге болады деген сұрақты талқылау керек.
ОрепМР шеңберінде жұмысты бөліп беру үшін келесі төрт нұсқаны пайдалануға болады:
төменгі деңгейдегі бағдарламалау;
циклдарды параллель орындау үшін do директивасы;
бағдарламаның тәуелсіз фрагменттерін параллель орындау үшін sections директивасы;
код участогын бір рет қана орындау үшін single директивасы.
Төменгі деңгейдегі бағдарламалау OMP_GET_THREAD_NUM және OMP_GET_NUM_THREADS функцияларының көмегімен жұмысты таратуды көздейді, олар сәйкесінше жіп нөмірін және туындаған жіптердің жалпы санын қайтарады. Мысалы, егер келесі түрдегі фрагментті жазсақ:
IF(OMP_GET_THREAD_NUM() .EQ. 3) THEN
<нөмірі 3 жіп үшін жеке код >
ELSE
<барлық қалған жіптер үшін код >
ENDIF
онда if. . .else директивалары арасындағы бағдарлама бөлігі тек 3-ші нөмірлі жіппен ғана, ал else. . .endif арасындағы бөлігі – қалған барлығымен орындалады. Бұрынғыша, бастапқы код барлық жіптер үшін бірдей болып қалады. Алайда, ондағы басқарудың нақты берілуі түрлі жіптер үшін әртүрлі жүреді, себебі omp_get_thread_num () функциясы 3 мәнін тек 3-ші нөмірлі жіпке ғана қайтарады.
Егер параллель секцияда цикл операторы кездессе, онда жалпы ережеге сәйкес ол барлық жіптермен орындалады, яғни әрбір жіп бұл циклдің барлық итерацияларын орындайды.
Жіптер арасындағы цикл итерацияларын тарату үшін do директивасын пайдалану керек:
!$ОМР DO [опция [[,] опция]...]
<dо-цикл>
[!$ОМР END DO]
Бұл директива артынан ілескен do операторы кіретін блокқа қатысты.
schedule – опциясы жіптер арасында цикл итерацияларын таратудың нақты әдістерін анықтайды.
static [, m] — итерацияның блокты-циклдық таратылуы. Бірінші жіп m итерациядан тұратын бірінші блокты орындайды, екінші жіп – екінші блокты және т.с.с. соңғы блокқа дейін, одан кейін таратылу қайтадан бірінші жіптен басталады. Егер m–нің мәні көрсетілмесе, онда итерациялардың барлық жиыны, өлшемдері шамамен бірдей болатын үздіксіз бөліктерге (кесектерге) бөлінеді, және олар жіптер арасында таратылады.
dynamic [,m] - итерациялардың динамикалық таратылуы. Алғашында барлық жіптер m итерациядан тұратын үлестерін (порции) алады, өз жұмыстарын аяқтағаннан кейін әрбір жіп тағы да m итерациядан тұратын келесі үлесін алады. Егер m–нің мәні көрсетілмесе, ол бірге тең деп қабылданады.
guided [,m] - итерациялардың өлшемдері азаятын блоктармен динамикалық таратылуы. Алғашында белгіленген блоктардың өлшемдері жеткілікті үлкен етіп алынады, ал бағдарламаның жұмыс істеу процесінде ол үнемі біртіндеп азая береді. Итерация блогының минимальді өлшемі m. Алғашқы белгіленген блоктың өлшемі оның іске асырылуына байланысты. Егер m–нің мәні көрсетілмесе, ол бірге тең деп қабылданады.
runtime – цикл итерацияларының таратылу әдісі бағдарламаның жұмыс істеу кезінде omp_schedule айнымалысының мәніне байланысты таңдалады. Таңдалған әдіс schedule опциясынан кейін жақша ішінде көрсетіледі, мысалы:
!$ОМР DO SCHEDULE (DYNAMIC, 10)
Бұл мысалда, итерациялардың әрбірі 10 итерациядан тұратын блоктар түрінде динамикалық таратылуы пайдаланылады.
Параллель циклдың соңында, параллель жұмыс істейтін жіптердің айқын емес барьерлік синхрондауы іске асады (происходит): олардың ары қарай орындалуы, берілген нүктеге олардың барлығы жеткен кезде ғана жалғасады. Егер мұндай бөгелудің (кідірістің) қажеттілігі болмаса, онда end do nowait аяқтаушы директивасы цикл соңына жеткен жіптерге (қалған жіптермен синхрондалу жасалынбайды) орындауды жалғастыра беруге мүмкіндік береді. Егер end do директивасы айқын түрде көрсетілмесе, онда параллель циклдың соңында синхрондау бәрібір орындалады.
Параллель циклдарды ұйымдастыруға бірнеше табиғи шектеулер қойылады. Дербес жағдайда, дұрыс құрылған бағдарлама қандай жіп параллель циклдің қай итерациясын орындап жатқанына байланысты болмауы керек деп есептеледі. Параллель циклдан қосымша шығуды пайдалануға болмайды. schedule опциясында көрсетілген итерация блогының өлшемі цикл шеңберінде өзгермеуі керек.
Келесі мысалды қарастырайық. Ол бағдарламаның паралель секциясында орналасқан деп есептейік.
!$ОМР DO SCHEDULE (STATIC, 2)
DO i = 1, n
DO j = 1, m
A(i, j) = (B(i, j - 1) + B(i - 1, j))/2.0
END DO
END DO
!$OMP END DO
Бұл мысалда сыртқы цикл параллель деп хабарланған, және мұнда итерацияның блокты-циклдік таратылуы пайдаланылатын болады (блокта екі итерациядан). Ішкі циклға қатысты ешқандай нұсқаулар жоқ, сондықтан ол әрбір жіппен тізбекті орындалатын болады.
Тәуелсіз фрагменттер деңгейіндегі параллельділік ОреnМР-да sections...end sections қос директиваларының көмегімен, және осы жұптың ішінде орналасқан section директивасының бірнеше санымен беріледі. Мысалы:
!$ОМР SECTIONS
!$ОМР SECTION
<фрагмент 1>
!$ОМР SECTION
<фрагмент 2>
!$ОМР SECTION
<фрагмент З>
!$ОМР END SECTIONS
Бұл мысалда, ондағы үш фрагментті параллель орындауға болатындығы көрсетілген. Әрбір фрагмент қандай да бір жіппен тек бір рет қана орындалады. Егер жіптер саны секциялар санынан көп болса, онда қай жіптер қандай секциялар үшін екенін және қандай жіптер қолданылмайтынын авторлардың өздері шешеді. Конструкцияның соңында жіптер жұмысын айқын емес синхрондау қарастырылады. Егер бұндай синхрондаудың қажеттілігі болмаса, онда END SECTIONS nowait директивасын пайдалануға болады.
do және sections директиваларында firstprivate және lastprivate опцияларын өз айнымалыларының тізімімен пайдалануға болады. Бұл опциялар берілген конструкцияларға кірер алдында, жергілікті айнамалыларды инициалдауды басқарады, сонымен қатар параллель цикл және секция өңделуі аяқталғаннан кейінгі айнымалылардың мәндерін анықтайды.
Егер параллель секцияда кодтың қандай да бір бөлігі тек бір рет қана орындалуы керек болса, онда оны single...end single директиваларының арасына орналастыру керек. Мұндай қажеттілік көбіне жалпы айнымалылармен жұмыс істегенде жиі пайда болады. Айтылған участок бір жіппен ғана орындалады. Егер соңында end single nowait директивасы көрсетілмесе, онда барлық жіптердің айқын емес синхрондалуы орындалады.
ОреnМР базалық түсініктерінің бірі – айнымалылар кластары. Параллель облыста пайдаланылатын барлық айнымалылары не жалпы немесе жергілікті болуы мүмкін. Жалпы айнымалылар shared директивасымен сипатталады, ал жергілікті – PRIVATE директивасымен. Әрбір жалпы айнымалы бағдарлама үшін әрқашанда бір экземплярда ғана болып, әрбір жіпке бір атпен ғана қолжетімді болады. Әрбір жергілікті айнымалы үшін әрбір жіпте, осы жіпке ғана қолжетімді болатын осы айнымалының жеке экземпляры бар болады.
Келесі фрагмент параллель секцияда орналасқан деп есептейік:
I = OMP_GET_THREAD_NUM()
PRINT *, I
Егер I айнымалысы берілген параллель секцияда жергілікті деп хабарланса, онда шығысында 0 ден omp_num_threads – 1-ге дейінгі сандар жиыны алынады. Жалпы айтқанда сандар еркін ретпен алынады, бірақ әрбір сан тек бір рет қана кездеседі. Егер I айнымалысы берілген параллель секцияда жалпы деп хабарланса, онда шығысында 0 ден omp_num_threads – 1 аралығында жататын omp_num_threads сандар тізбегі алынады. Бұл жағдайда, тізбекте қанша және қандай сандар болатыны туралы алдын-ала ештеңе айтуға болмайды. Дербес жағдайда omp_num_threads бірдей сандарының I0 жиыны да болуы мүмкін. Шынында да, I0 процесінен басқа қалған барлық процесті қандай да бір ретпен бірінші оператор орындады деп есептейік. Одан кейін, қандай да бір себепке байланысты олардың орындалуы тоқтатылсын. Осы мезетте I0 нөмірлі процесс I-дің осы мәнін қабылдайды, ал бұл айнымалы жалпы болуы салдарынан, әрбір жіп бірдей мәндерді шығарады. Сондықтан, осындай анықталмағандық болмауы үшін, бағдарламашы әрбір жіптің жалпы айнымалыларды пайдалануын өзі қадағалап отыруы керек.
Бағдарламаның келесі мысалын қарастырайық.
PROGRAM HELLO
INTEGER NTHREADS, TID, OMP_GET_NUM_THREADS, +OMP_GET_THREAD_NUM
С жіптердің жергілікті айнымалылармен туындауы
!$ОМР PARALLEL PRIVATE(NTHREADS, TID)
С өз нөмірін алу және баспаға шыығару
TID = OMP_GET_THREAD_NUM()
PRINT *, 'Hello World from thread = ', TID
С Жіп-мастер үшін код участогы
IF (TID .EQ. 0) THEN
NTHREADS = OMP_GET_NUM_THREADS()
PRINT *, 'Number of threads = ', NTHREADS
ENDIF
С Параллель секцияның аяқталуы
!$ОМР END PARALLEL
END
Әрбір жіп параллель секцияны көрсететін код фрагментін орындайды, және жіп нөмірімен құттықтау сөзді «Hello World from thread = жіп нөмірі» баспаға береді. Қосымша, жіп-мастер туындаған жіптердің жалпы санын баспаға шығарады. Параллель секция хабарламасында nthreads және tid айнымалыларының жергілікті екені айқын көрсетілген. Жіптердің жалпы санын және олардың нөмірлерін анықтау үшін кітапханалық функциялар omp_get_num_threads және omp_get_thread_num пайдаланылған.
Векторларды қосу бағдарламасын қарастырайық.
PROGRAM VEC_ADD_DO
INTEGER N, CHUNK, I
PARAMETER (N = 1000)
PARAMETER (CHUNK =100)
REAL A(N), B(N), C(N)
! массивтерді инициализациялау
DO I = 1,N
A(I) = I * 1.0
B(I) = A(I)
END DO
!$OMP PARALLEL SHARED(A,B,C,N) PRIVATE(I) !$OMP DO SCHEDULE(DYNAMIC,CHUNK)
DO I = 1, N
C(I) = A(I) + B(I)
END DO
!$OMP END DO NOWAIT
!$OMP END PARALLEL
END
Берілген мысалда А, В, С массивтері және n айнымалысы жалпы, ал I айнымалысы жергілікті деп хабарланған. I айнымалысы жергілікті болғандықтан, әрбір процесс бұл айнымалының өз көшірмесін алады. Параллель цикл итерациялары жіптер арасында динамикалық таратылады. Блоктар өлшемі бекітілген және тең CHUNK. NOWAIT конструкциясы пайдаланылғандықтан параллель циклдің соңында жіптердің синхрондалуы болмайды.
Жіптер жұмысын синхрондау үшін ОреnМР тұтас директивалар жиынын пайдаланады. Ең кең таралған және қарапайым синхрондау әдісі - барьер. Ол
!$ОМР BARRIER директивасы көмегімен орындалады. Барлық жіптер осы директиваға жетісімен «тоқтайды», және қалған барлық жіптердің бағдарламаның осы нүктесіне жетуін күтеді, барлығы жетісімен ары қарай жұмыстарын жалғастырады.
Директивалар master. . .end master жұбы тек қана жіп-мастермен орындалатын код участогын белгілейді. Қалған жіптер бұл участокты елемей өткізіп жібереді де end master директивасынан кейін орналасқан оператордан бастап жұмыстарын жалғастырады. Бұл директива айқын емес синхрондауды ниет етпейді.
Келесі директивалар көмегімен
!$ОМР CRITICAL [ (< критикалық секцияның аты>) ]
!$ОМР END CRITICAL [ (<критикалық секцияның аты >) ]
бағдарламаның критикалық секциясы құжатталады. Әрбір уақыт мезетінде критикалық секцияда біреуден артық жіп болмайды. Егер критикалық секция қандай да бір Р0 жіппен орындалып жатса, онда қашан Р0 бұл секцияны орындауын аяқтағанша, осы секция үшін critical директивасын орындаған барлық қалған жіптер бұғатталады. Қалай Р0 жібі end critical директивасын орындайды, солай кірістегі бұғатталған жіптердің бірі секцияға кіреді. Егер критикалық секцияның кірісінде бірнеше жіптер тұрса, онда олардың бірі кездейсоқ түрде алынады, ал қалған бұғатталған жіптер күтулерін жалғастырады. Барлық аталмаған критикалық секциялар шартты түрде бір атпен қаралады, ал бірдей атты иеленетін критикалық секциялар бірегей (бір) секция ретінде қарастырылады.
Жоғарыда келтірілген жіп нөмірін баспалауға қатысты мысалды келесі түрде жетілдіріп қарастырайық:
!$ОМР CRITICAL
I = OMP_GET_THREAD_NUM()
PRINT *, I
!$OMP END CRITICAL
Бұл жағдайда, егер I айнымалысы жалпы деп хабарланғанның өзінде, бәрібір шығысында міндетті түрде 0 ден omp_num_threads – 1-ге дейінгі барлық сандар пайда болады. Енді, критикалық секция және жалпы айнымалы I пайдаланылатын фрагмент пен критикалық секциясыз жергілікті айнымалыны I пайдаланатын фрагмент арасында қандай айырмашылық бар екенін қарап көрелік. Қазір ғана көргеніміздей нәтиже тек сандардың пайда болу ретінде ғана. Сандар жиыны бірдей болады. Бірақ бұл фрагменттердің орындалуында айтарлықтай айырмашылық бар. Егер критикалық секция бар болса, онда әрбір уақыт мезетінде фрагмент тек қандай да бір жіппен ғана өңделеді. Қалған жіптер, бағдарламаның осы нүктесіне келіп тұрғандарына және жұмысқа дайын болғандарына қарамастан өз кезектерін күтеді. Егер критикалық секция болмаса, онда барлық жіптер бір мезгілде кодтың берілген участогын орындайды. Бір жағынан, критикалық секциялар жалпы айнымалылармен жұмыс істеу үшін ыңғайлы механизм ұсынады. Ал басқа жағынан пайдаланушыға абай болу керек, себебі критикалық секциялар параллель бағдарламаға тізбекті код участкілерін қосады, олар өз кезегінде оның тиімділігін төмендетеді.
Тәжірибе жүзінде критикалық секцияны пайдаланудың дербес жағдайы ретінде жалпы айнымалыларды жаңартуды алуға болады. Мысалы, егер sum айнымалысы жалпы болып және sum = sum + Expr түріндегі оператор бағдарламаның параллель секциясында бар болса, онда берілген операторды бірнеше жіптермен бірмезгілде орындау кезінде дұрыс нәтиже алынбауы мүмкін. Мұндай жағдайларды болдырмау үшін, критикалық секциялар механизмін немесе осындай жағдайлар үшін арнайы қарастырылған atomic директивасын пайдалануға болады:
!$ОМР ATOMIC
SUM = SUM + Expr
atomic директивасы өзінен кейінгі тұрған операторға ғана қатысты. Бұл мысалда ол SUM жалпы айнымалысымен дұрыс жұмыс істеуге кепілдік береді.
Заманауи параллель есептеу жүйелерінде күрделі құрылым және жады иерархиясы пайдаланылуы мүмкін болғандықтан, пайдаланушының өзіне қажетті уақыт мезеттерінде, әрбір жіп жадының бірегей үйлестірілген бейнесін көре алатынына кепілдігі болуы керек.
!$ОМР FLUSH [ айнымалылар_тізімі ]
Бұл директиваның орындалу барысында, регистрлерде уақытша сақталынған барлық айнымалылардың мәндері негізгі жадыға енгізіледі, жіптердің жұмыс істеу барысында айнымалыларға жасаған өзгертулері қалған жіптерге көрінетін болады, егер қандай да бір ақпарат шығыс буферлерінде сақталынса, олар алынып тасталады. Берілген директиваның толық көлемде орындалуы айтарлықтай қосымша шығындарға алып келетіндіктен, қандай да бір уақыт мезетінде барлығы емес ал жекелеген айнымалылардың ғана үйлесімділігін көрсетудің кепілдігі керек болған жағдайда, бұл айнымалыларды айқын түрде тізіп көрсету қажет. Біз бұл бөлімде ОреnМР технологиясының конструкцияларына толық талдау жүргізбейміз. Fortran, С және C++ тілдері үшін ОреnМР туралы толық текстерді http://www.openmp.org сайтынан ала аласыздар. Сонымен қатар, жоғарыда келтірілген және басқа да көптеген мысалдардың Fortran, С және C++ тілдеріне арналып жасалған толық нұсқаларын А.С.Антоновтың «Параллельное программирование с использованием технологии OpenMP» атты оқу құралынан кездестіре аламыз. ОреnМР технологиясының директивалары, функциялары, опциялары және орта айнымалылары қосымша 2-де берілген.
Сонымен, ОреnМР технологиясы несімен тартымды және қызықты? Бірнеше моменттерінің ішінен екеуін арнайы айта кетуге болады. Біріншіден, ОреnМР технологиясы алғашында пайдаланушы параллель және тізбекті бағдарламалар үшін бір текспен ғана жұмыс істей алатындай етіп жобаланған. Тізбекті машинада дәстүрлі компилятор ОреnМР директиваларын жай "байқамай" қалады, себебі олар комментарилерде орналасқан. Туындайтын мәселелердің бір ғана бастауы орта айнымалылары және арнайы функциялар болуы мүмкін. Алайда, олар үшін тізбекті жағдайда ОреnМР-бағдарламасының дұрыс жұмысына кепілдік беретін стандарт спецификацияларында арнайы "бұқтырмалар" ("заглушки") қарастырылған. Ол үшін тек бағдарламаны қайта компиляция жасап, басқа кітапхананы қосу керек.
ОреnМР технологиясының келесі артықшылығы бағдарламаны біртіндеп "инкрементті" параллельдеу мүмкіндігі. Негізіне тізбекті кодты ала отырып, пайдаланушы әр қадам сайын жаңа параллель конструкцияларды бейнелейтін жаңа директиваларды қосып отырады. Мұнда бірден тұтас параллель бағдарламаны құрудың қажеттілігі жоқ, оны құру тізбекті түрде жүргізіледі. Бұл өз кезегінде бағдарламалау процесін де жөндеуді де ықшамдауға мүмкіндік береді.
Сұрақтар мен тапсырмалар
Циклда функцияны шақыру қарастырылған. Берілген циклдың барлық итерацияларының тәуелді не тәуелсіздігін анықтау үшін тілдердің қандай конструкцияларын ескеру қажет? С және Fortran тілдерін қарастырыңыздар.
ОреnМР технологиясы қай есептеу жүйесіне жақсы сәйкес келеді: жұмыс станцияларынан тұратын есептеу кластеріне ме әлде SMP-компьютерге ме?
«Процесс» және «жіп» түсініктерінің айрмашылығы неде?
ОреnМР-бағдарламасында тізбекті секциядағы жалпы және жергілікті айнымалылар айырмашылығы неде?
Бағдарламаның жұмыс істеу барысында ОреnМР параллель жіптер санын өзгертуге мүмкіндік бере ме?
ОреnМР көмегімен екі матрицаны көбейту бағдарламасын құрыңыз. Тиімділік жағынан талдау жасаңыз.
ОреnМР көмегімен кері матрицаны табу бағдарламасын құрыңыз. Тиімділік жағынан талдау жасаңыз.
ОреnМР көмегімен сызықты теңдеулер жүйесін Гаусс әдісімен шешу бағдарламасын құрыңыз. Тиімділік жағынан талдау жасаңыз.
ОреnМР технологиясының қандай артықшылықтары бар?
"инкрементті" параллельдеу дегеніміз не?
Жіптер жұмысын синхрондау, синхрондау әдісі – барьер туралы не айта аласыз?
ОреnМР айнымалылар кластары туралы не айтасыз?