logo

Pthreads bilan Umumiy xotirali dasturlash

Yuklangan vaqt:

29.08.2023

Ko'chirishlar soni:

0

Hajmi:

191.4072265625 KB
Mavzu: Pthreads bilan Umumiy xotirali dasturlash
Reja:
Kirish
1. Pthread s   jarayonlar va  kutubxonalar  
2. Dastlabki o'yinlar  
3. Matritsa-vektorlarni ko'shtirish
4. MUHIM BO'LIMLAR
Foydalangan adabiyotlar Kirish
Eslatib o'tamiz, dasturchi nuqtai nazaridan umumiy xotira tizimi bitta barcha
yadrolar   barcha   xotira   joylariga   kirishi   mumkin   (4.1-rasmga   qarang).   Shunday
qilib   yadrolar   ishini   muvofiqlashtirish   muammosiga   aniq   yondashuvni
aniqlashtirishdir   ba'zi   xotira   joylari   "birgalikda"   ekanligi.   Bu   parallellikka   juda
tabiiy yondashuv dasturlash. Haqiqatan ham, nima uchun barcha parallel dasturlar
buni ishlatmasligiga hayron bo'lishimiz mumkin umumiy xotira yondashuvi. Biroq,
biz   ushbu   bobda   muammolar   mavjudligini   ko'ramiz   umumiy   xotira   tizimlarini
dasturlashda   ko'pincha   muammolardan   farq   qiladi   taqsimlangan   xotira
dasturlashda duch keladigan muammolar.
Misol  uchun, 2-bobda biz ko'rdikki, agar turli yadrolar bitta umumiy xotira
manzilini yangilashga harakat qilsa, u holda uning mazmuni qo'yilgan joy oldindan
aytib   bo'lmaydigan   bo'lishi   mumkin.   Umumiy   joylashuvni   yangilaydigan   kod
muhim   bo'limga   misoldir.   Biz   tanqidiy   bo'limlarning   boshqa   misollarini   ko'rib
chiqamiz   va   biz   bir   nechta   usullarni   o'rganamiz   muhim   bo'limga   kirishni   nazorat
qilish uchun.
Biz umumiy xotira dasturlashning boshqa masalalari va usullari haqida ham
bilib   olamiz.   Umumiy   xotirali   dasturlashda   protsessorda   ishlaydigan   dasturning
namunasi odatdathreadsdeb ataladi (MPI dan farqli o'laroq, u jarayon deb ataladi).
Har   birthreadsblokni   bajarishni   kutishi   uchun   iplarni   qanday   sinxronlashtirishni
o'rganamiz
boshqa   mavzu   ba'zi   ishni   tugatmaguncha   bayonotlar.   Vaziyat   yuzaga
kelmaguncha,   biz   ipni   qanday   qilib   "uxlashga"   qo'yishni   o'rganamiz.   Biz   ba'zi
holatlar   mavjudligini   ko'ramiz,   ular   dastlab   tanqidiy   qism   juda   katta   bo'lishi
kerakdek   tuyulishi   mumkin.   Biroq,   biz   ba'zan   dasturning   ko'p   qismini   parallel
ravishda   bajarish   uchun   ushbu   katta   kod   bloklariga   kirishni   "nozik   sozlash"
imkonini   beradigan   vositalar   mavjudligini   ham   ko'ramiz.   Biz   kesh   xotiralaridan
foydalanish   umumiy   xotira   dasturining   sekinroq   ishlashiga   olib   kelishi
mumkinligini   ko'ramiz.   Nihoyat,   biz   ketma-ket   qo'ng'iroqlar   orasida   "holatni
saqlaydigan"   funktsiyalar   nomuvofiq   yoki   hatto   noto'g'ri   natijalarga   olib   kelishi
mumkinligini ko'ramiz.
Ushbu bobda biz umumiy xotira funktsiyalarining ko'pchiligi uchun POSIX
R   iplaridan   foydalanamiz.   Keyingi   bobda   biz   OpenMP   deb   nomlangan   umumiy
xotira dasturlashning muqobil yondashuvini ko'rib chiqamiz. PTHREADS  JARAYONLAR VA KUTUBXONALAR
2-bobdan eslaylikki, umumiy xotirali dasturlashdathreadsbiroz MPI dasturlash 
jarayoniga o'xshaydi. Biroq, bu printsipial jihatdan, "engilroq" bo'lishi mumkin
4.1-rasm. Umumiy xotira tizimi
Jarayon   ishlayotgan   (yoki   foydalanilgan)   dasturning   namunasidir.   Bajarilishi
mumkin bo'lgan narsadan tashqari, u quyidagilardan iborat:
 Stack uchun xotira bloki
 Yig ‘ish  uchun xotira bloki
 Tizim   jarayon   uchun   ajratgan   resurslar   deskriptorlari,   masalan,   fayl
deskriptorlari.
 Xavfsizlik ma'lumotlari - masalan, jarayon qaysi apparat va dasturiy ta'minot
resurslariga kirishi mumkinligi haqidagi ma'lumot.
 Jarayonning   holati   to'g'risidagi   ma'lumotlar,   masalan,   jarayon   ishga
tayyormi yoki resursda kutilmoqdami, registrlar tarkibi, shu jumladan dastur
hisoblagichi va boshqalar.
Aksariyat   tizimlarda,   sukut   bo'yicha,   jarayonning   xotira   bloklari   shaxsiydir:   agar
operatsion tizim aralashmasa, boshqa jarayon jarayonning xotirasiga bevosita kira
olmaydi.   Bu   mantiqiy.   Agar   siz   dastur   yozish   uchun   matn   muharriridan
foydalanayotgan   bo'lsangiz   (bitta   jarayon   -   ishlaydigan   matn   muharriri),
brauzeringiz   (boshqa   jarayon)   matn   muharriri   xotirasini   qayta   yozishni
xohlamaysiz.   Bu   ko'p   foydalanuvchi   muhitida   yanada   muhimroq.   Bir
foydalanuvchining   jarayonlariga   boshqa   foydalanuvchi   jarayonlarining   xotirasiga
kirishga ruxsat berilmasligi kerak.
Biroq,   biz   umumiy   xotira   dasturlarini   ishga   tushirganimizda,   bu   biz
xohlagan   narsa   emas.   Hech   bo'lmaganda,   biz   ba'zi   o'zgaruvchilar   bir   nechta
jarayonlar   uchun   mavjud   bo'lishini   xohlaymiz,   shuning   uchun   umumiy   xotira
"jarayonlari"   odatda   bir-birining   xotirasiga   kirishni   ancha   osonlashtiradi.
Shuningdek, ular tez-tez stdout-ga kirish kabi narsalarni baham ko'rishadi. Aslida, ular o'zlarining steklari va dastur hisoblagichlaridan tashqari, jarayonga xos bo'lgan
deyarli hamma narsani baham ko'rishlari mumkin. Buni bitta jarayonni boshlash va
keyin   jarayonni   ushbu   "yengilroq"   jarayonlarni   boshlash   orqali   nisbatan   oson
tartibga   solish   mumkin.   Shu   sababli,   ular   ko'pincha   engil   vaznli   jarayonlar   deb
ataladi.
Ko'proq ishlatiladigan atama, ip, "nazorat ipi" tushunchasidan kelib chiqqan.
Boshqaruv   zanjiri   -   bu   dasturdagi   gaplar   ketma-ketligi.   Bu   atama   bitta   jarayonda
boshqaruv   oqimini   nazarda   tutadi   va   umumiy   xotira   dasturida   bitta   jarayon   bir
nechta boshqaruv zanjiriga ega bo'lishi mumkin.
Yuqorida   aytib   o'tganimizdek,   ushbu   bobda   biz   foydalanadigan   iplarning
maxsus   amalga   oshirilishi   POSIX   iplari   yoki   ko'pincha   Pthreads   deb   ataladi.
POSIX   [41]   Unix-ga   o'xshash   operatsion   tizimlar   uchun   standartdir,   masalan,
Linux va Mac  OS X. U bunday tizimlarda mavjud bo'lishi  kerak  bo'lgan turli  xil
imkoniyatlarni   belgilaydi.   Xususan,   u   ko'p   bosqichli   dasturlash   uchun   amaliy
dasturlash interfeysini (API) belgilaydi.
Pthreads   dasturlash   tili   emas   (masalan,   C   yoki   Java).   Aksincha,   MPI   kabi,
Pthreads   C   dasturlari   bilan   bog'lanishi   mumkin   bo'lgan   kutubxonani   belgilaydi.
MPI dan farqli o'laroq, Pthreads API faqat POSIX tizimlarida mavjud - Linux, Mac
OS   X,   Solaris,   HPUX   va   boshqalar.   MPI-dan   farqli   o'laroq,   ko'p   tarmoqli
dasturlash   uchun   keng   qo'llaniladigan   bir   qator   boshqa   spetsifikatsiyalar   mavjud:
Java   iplari,   Windows   ish   zarralari,   Solaris   iplari.   Biroq,
barchathreadsspetsifikatsiyalari   bir   xil   asosiy   g'oyalarni   qo'llab-quvvatlaydi,
shuning   uchun   Pthreads-da   qanday   dasturlashni   o'rganganingizdan   so'ng,   boshqa
thread API-ni qanday dasturlashni o'rganish qiyin bo'lmaydi.
Pthreads   C   kutubxonasi   bo'lganligi   sababli,   u,   asosan,   C++   dasturlarida
ishlatilishi mumkin. Biroq, umumiy xotira dasturlash uchun C++ standartini (C++
0x) ishlab chiqish ustida ish olib borilmoqda. Agar siz C++ dasturlarini yozayotgan
bo'lsangiz, Pthreads o'rniga undan foydalanish mantiqiy bo'lishi mumkin. SALOM DUNYO
Qani   boshladik.   Keling,   Pthreads   dasturini   ko'rib   chiqaylik.   4.1-dastur   asosiy
funktsiya   bir   nechta   iplarni   ishga   tushiradigan   dasturni   ko'rsatadi.   Har   bir   mavzu
xabarni chop etadi va keyin tugaydi.
Bajarish
Dastur   oddiy   C   dasturi   kabi   tuzilgan,   bundan   mustasno,   Pthreads   kutubxonasiga
ulanishimiz kerak bo'lishi mumkin: 1
$ gcc  — g  — Wall  — o_pth hello pth hello.c  — lpthread
-lpthread   kompilyatorga   Pthreads   kutubxonasida   bog'lanmoqchi   ekanligimizni
bildiradi.   E'tibor   bering,   bu   -lpthread   emas,   balki   -lpthread.   Ba'zi   tizimlarda
kompilyator   avtomatik   ravishda   kutubxonaga   bog'lanadi   va   -lpthread   kerak
bo'lmaydi.
Dasturni ishga tushirish uchun  quyidagi kodni  yozamiz
$ ./pth hello <number of threads>
1  #include <stdio.h>
2  #include <stdlib.h>
3  #include <pthread.h>
4
5  / _  Global variable: accessible to all threads  _ /
6  int   thread count;
7
8  v oid _  Hello( v oid _  rank);  / _  Thread function  _ /
9
10  int   main( int   argc,  char _  argv[])  f
11  long  thread;  / _  Use long in case of a 64 ??????bit system  _ /
12  pthread t _  thread handles;
13
14  / _  Get number of threads from command line  _ /
15  thread count = strtol(argv[1], NULL, 10);
16
17  thread handles = malloc (thread count _ sizeof (pthread t));
18
19  for  (thread = 0; thread < thread count; thread++)
1
 Eslatib o'tamiz, dollar belgisi ($) qobiq so'rovidir, shuning uchun uni kiritmaslik kerak. Shuni ham yodda tutingki, 
biz Gnu C kompilyatoridan foydalanamiz, deb taxmin qilamiz, gcc va biz doimo variantlari  — g,  — Wall, and  — o. 
Qo'shimcha ma'lumot uchun 2.9-bo'limga qarang. 20  pthread create(&thread handles[thread], NULL,
21  Hello, ( v oid _ ) thread);
22
23  printf("Hello from the main thread n n");
24
25  for  (thread = 0; thread < thread count; thread++)
26  pthread join(thread handles[thread], NULL);
27
28  free(thread handles);
29  ret urn  0;
30  g  / _  main  _ /
31
32  v oid _  Hello( v oid _  rank)  f
33  long  my rank = ( long ) rank
/ _  Use long in case of 64 ??????bit system  _ /
34
35  printf("Hello from thread %ld of %d n n", my rank,
thread count);
36
37  ret urn  NULL;
38  g  / _  Hello  _ /
4 .1-dastur: Pthreads “salom, dunyo” dasturi
Masalan, dasturni bittathreadsbilan ishga tushirish uchun biz yozamiz
$ ./pth hello 1
va chiqish quyidagicha ko'rinadi:
Hello from the main thread
Hello from thread 0 of 1
Dasturni to'rttathreadsbilan ishga tushirish uchun biz yozamiz
$ ./pth hello 4
va chiqish quyidagicha ko'rinadi:
Hello from the main thread
Hello from thread 0 of 4
Hello from thread 1 of 4 Hello from thread 2 of 4
Hello from thread 3 of 4
Dastlabki o'yinlar
Keling,     4.1-dasturda   manba   kodini   batafsil   ko'rib   chiqaylik.   Avval   e'tibor
bering,   bu   faqat   asosiy   va   boshqa   funktsiyaga   ega   C   dasturi.   Dasturda   tanish
stdio.h   va   stdlib.h   sarlavhalari   fayllari   mavjud.   Biroq,   juda   ko'p   yangi   va   farqli
narsalar mavjud. 3-qatorda biz pthread.h, Pthreads sarlavha faylini kiritamiz, u turli
Pthreads funksiyalarini, konstantalarini, turlarini va hokazolarni e'lon qiladi.
6-qatorda   biz   global   o'zgaruvchan   iplar   sonini   aniqlaymiz.   Pthreads
dasturlarida   global   o'zgaruvchilar   barcha   iplar   tomonidan   almashadilar.   Mahalliy
o'zgaruvchilar   va   funktsiya   argumentlari,   ya'ni   funksiyalarda   e'lon   qilingan
o'zgaruvchilar   (odatda)   funktsiyani   bajaruvchithreadsuchun   shaxsiydir.   Agar   bir
nechta iplar bir xil funktsiyani bajarayotgan bo'lsa, har bir ish zarrachasi mahalliy
o'zgaruvchilar va funktsiya argumentlarining shaxsiy  nusxalariga ega bo'ladi. Har
bir ipning o'z stekiga ega ekanligini eslasangiz, bu mantiqiy bo'ladi.
Shuni   yodda   tutishimiz   kerakki,   global   o'zgaruvchilar   nozik   va   chalkash
xatolarni   keltirib   chiqarishi   mumkin.   Misol   uchun,   biz   int   x   global   o'zgaruvchini
e'lon  qiladigan  dastur  yozamiz   deylik.  Keyin  f  funktsiyasini   yozamiz,  unda  biz  x
deb   nomlangan   lokal   o'zgaruvchidan   foydalanmoqchimiz,   lekin   uni   e'lon   qilishni
unutamiz.   Dastur   hech   qanday   ogohlantirishsiz   kompilyatsiya   qilinadi,   chunki   f
global x ga kirish huquqiga ega. Ammo biz dasturni ishga tushirganimizda, u juda
g'alati natijalarni beradi, biz oxir-oqibat x global o'zgaruvchisi g'alati qiymatga ega
ekanligidan   kelib   chiqqanligini   aniqlaymiz.   Bir   necha   kun   o'tgach,   biz   nihoyat
g'alati   qiymat   f   dan   kelganligini   aniqladik.   Qoida   tariqasida,   biz   global
o'zgaruvchilardan   foydalanishni   ular   haqiqatan   ham   zarur   bo'lgan   holatlarda,
masalan, umumiy o'zgaruvchi uchun cheklashga harakat qilishimiz kerak.
strtol   funktsiyasi  satrni  uzun  int  ga  aylantiradi.  U  stdlib.h  da   e'lon  qilingan
va uning sintaksisi shunday
long  strtol(
const  char _  number p  / _  in  _ / ,
char __  end p  / _  out  _ / ,
int   base  / _  in  _ / );
Bu   p   raqami   bilan   ko'rsatilgan   qatorga   mos   keladigan   uzun   intni   qaytaradi.
Raqamni   ifodalash   asosi   asosiy   argument   bilan   beriladi.   Agar   p   oxiri   NULL
bo'lmasa,   u   p  raqamidagi   birinchi   yaroqsiz   (ya'ni   sonli   bo'lmagan)   belgiga   ishora
qiladi. Threadsni ishga tushirish
Asosiy funktsiyani bajaradigan threads  ba'zan asosiy threads  deb ataladi. Shunday
qilib, threads ishga tushirgandan so'ng, u xabarni chop etadi
Hello from the main thread
Ayni   paytda,   pthread   yaratish   qo'ng'iroqlari   bilan   boshlangan   mavzular   ham
ishlamoqda. Ular o'z darajalarini 33-qatordagi kasting orqali olishadi va keyin o'z
xabarlarini   chop   etishadi.   E'tibor   bering,   ip   bajarilganda,   uning   funksiyasi   turi
qaytariladigan   qiymatga   ega   bo'lganligi   sababli,   ip   nimanidir   qaytarishi   kerak.
Ushbu misolda iplar aslida hech narsani qaytarishi shart emas, shuning uchun ular
NULLni qaytaradi.
4.2-rasm Asosiy funksiya ikkita threadsni birlashtiradi
Pthreads da dasturchi iplar qayerda ishga tushirilishini bevosita nazorat qilmaydi.2
pthread createda qaysi yadro qaysi ipni ishga tushirishi kerak degan argument yo'q.
Mavzuni   joylashtirish   operatsion   tizim   tomonidan   boshqariladi.   Haqiqatan   ham,
og'ir yuklangan tizimda barcha iplar bir xil yadroda ishlashi mumkin. Aslida, agar
dastur yadrolardan ko'ra ko'proq ish zarralarini  ishga tushirsa, biz bir  yadroda bir
nechta   ish   zarrachalarini   ishga   tushirishni   kutishimiz   kerak.   Biroq,
foydalanilmayotgan   yadro   mavjud   bo'lsa,   operatsion   tizimlar   odatda   bunday
yadroga yangi ipni joylashtiradi.
Threadsni to'xtatish
25 va 26-qatorlarda biz pthread funksiyasini har bir ip uchun bir marta birlashtirish
deb   ataymiz.   pthread   qo'shilishi   uchun   bitta   qo'ng'iroq   pthread   t   ob'ekti   bilan
bog'langan ip tugashini kutadi. Pthread qo'shilish sintaksisi
int   pthread join(
pthread t thread  / _  in  _ / ,
v oid __  ret val p  / _  out  _ / ); Ikkinchi   argument   ip   tomonidan   hisoblangan   har   qanday   qaytish   qiymatini   olish
uchun ishlatilishi mumkin. Shunday qilib, bizning misolimizda har bir ip qaytishni
amalga oshiradi va oxir-oqibat, asosiy ish zarrachalari tugatishni yakunlash uchun
ushbu ip uchun pthread qo'shilishini chaqiradi.
Ushbu   funktsiya   ko'p   oqimli   jarayonda   iplarni   tasvirlash   uchun   ko'pincha
ishlatiladigan   diagramma   uslubi   tufayli   pthread   qo'shilish   deb   ataladi.   Agar   biz
diagrammamizdagi   asosiy   ipni   bitta   chiziq   deb   hisoblasak,   pthread   create   deb
ataganimizda, biz asosiy ipdan novda yoki vilka hosil qilishimiz mumkin. Pthread
yaratish   uchun   bir   nechta   qo'ng'iroqlar   bir   nechta   filiallar   yoki   vilkalar   paydo
bo'lishiga olib keladi. Keyin, pthread create tomonidan boshlangan iplar tugagach,
diagrammada asosiy ipni birlashtiruvchi novdalar ko'rsatilgan. 4.2-rasmga qarang.
Xatolarni tekshirish
Dasturni ixcham va o'qish oson bo'lishi uchun biz "haqiqiy" dasturga muhim
bo'lgan   ko'plab   tafsilotlarni   kiritish   vasvasasiga   qarshi   turdik.   Ushbu   misolda   (va
ko'plab dasturlarda) muammolarning eng ko'p manbai foydalanuvchi kiritishi yoki
uning   etishmasligi.   Shuning   uchun   dastur   buyruq   qatori   argumentlari   bilan
boshlanganligini   tekshirish   va   agar   shunday   bo'lsa,   haqiqiyligini   tekshirish   juda
yaxshi   fikr   bo'lar   edi   o'rinli   yoki   yo'qligini   ko'rish   uchun   iplar   sonining   qiymati.
Agar siz kitobning veb-saytiga tashrif buyursangiz, ushbu asosiy xato tekshiruvini
o'z ichiga olgan dastur versiyasini yuklab olishingiz mumkin.
Pthreads   funksiyalari   tomonidan   qaytarilgan   xato   kodlarini   tekshirish   ham
yaxshi   fikr   bo'lishi   mumkin.   Bu,   ayniqsa,   Pthreads   va   dan   foydalanishni
boshlaganingizda   foydali   bo'lishi   mumkin   funktsiyadan   foydalanishning   ba'zi
tafsilotlari to'liq aniq emas.
Mavzuni ishga tushirishning boshqa yondashuvlari
Bizning misolimizda foydalanuvchi buyruq qatori argumentini kiritish orqali
boshlash uchun iplar sonini belgilaydi. Keyin asosiy ip barcha "qo'shimcha" iplarni
yaratadi.   Mavzular   ishlayotganda,   asosiy   ip   xabarni   chop   etadi   va   keyin   boshqa
iplar   tugashini   kutadi.   Yivli   dasturlashning   bu   yondashuvi   MPI   dasturlashga
bo'lgan   yondashuvimizga   juda   o'xshaydi,   bunda   MPI   tizimi   jarayonlar   to'plamini
ishga tushiradi va ularning tugashini kutadi.
Biroq,   ko'p   bosqichli   dasturlarni   loyihalashda   juda   boshqacha   yondashuv
mavjud. Ushbu yondashuvda yordamchi iplar faqat zarurat tug'ilganda boshlanadi.
Misol   sifatida,   San-Fransisko   ko'rfazi   hududida   avtomagistrallar   harakati   haqida
ma'lumot   so'rovlarini   bajaradigan   veb-serverni   tasavvur   qiling.   Faraz   qilaylik,
asosiy tarmoq so'rovlarni  oladi  va yordamchi  oqimlar so'rovlarni bajaradi. Odatiy seshanba kuni ertalab soat 1 da, ehtimol, juda kam so'rovlar bo'ladi, seshanba kuni
kechqurun soat 5 da, ehtimol, minglab so'rovlar bo'ladi. Shunday qilib, ushbu veb-
serverni   loyihalashda   tabiiy   yondashuv   -   bu   so'rovlarni   qabul   qilganda   asosiy
tarmoq yordamchi oqimlarni ishga tushirishdir.
Endi   shuni   ta'kidlab   o'tishimiz   kerakki,   ish   zarrachasini   ishga   tushirish,
albatta,   biroz   qo'shimcha   xarajatlarni   o'z   ichiga   oladi.   Ipni   boshlash   uchun   zarur
bo'lgan   vaqt,   aytaylik,   suzuvchi   nuqta   arifmetik   operatsiyasidan   ancha   katta
bo'ladi,   shuning   uchun   maksimal   ishlashga   muhtoj   bo'lgan   ilovalarda   "zarur
bo'lganda   iplarni   boshlash"   yondashuvi   ideal   bo'lmasligi   mumkin.   Bunday   holda,
biroz   murakkabroq   sxemadan   foydalanish   mantiqiy   bo'lishi   mumkin   -   bu   ikkala
yondashuvning   xususiyatlariga   ega   bo'lgan   sxema.   Bizning   asosiy   ipimiz
dasturning   boshida   kerak   bo'lgan   barcha   mavzularni   boshlashi   mumkin   (bizning
misol   dasturimizda   bo'lgani   kabi).   Biroq,   agar   ipning   ishi   bo'lmasa,   uni   tugatish
o'rniga,   u   ko'proq   ish   mavjud   bo'lguncha   bo'sh   o'tirishi   mumkin.   Dasturlash
topshirig'i   4.5   da   biz   bunday   sxemani   qanday   amalga   oshirishimiz   mumkinligini
ko'rib chiqamiz.
MATRITSA-VEKTORLARNI KO'SHTIRISH
Keling, Pthreads matritsa-vektorni ko'paytirish dasturini yozishni ko'rib chiqaylik.
Eslatib o'tamiz, agar A D .aij/ m n matritsa va x D .x0, x1, : : : , xn 1/T n o'lchovli
ustun vektori bo'lsa, matritsa-vektor mahsuloti Ax D y bo'ladi. m o‘lchamli ustun
vektori, y D .y0, y1, : : : , ym1/T bunda i-komponent yi topib olinadi.	
΀
4.3-rasm Matritsa-vektorlarni ko'paytirish
x bilan A ning i qatorining nuqta mahsuloti:
4.3-rasmga qarang. Shunday qilib, matritsa-vektorlarni ko'paytirish uchun ketma-
ket dastur uchun psevdokod quyidagicha ko'rinishi mumkin:
/*  For each row of A */
for (i = 0; i < m; i++) f y[i] = 0.0;
/*  For each element of the row and each element of x */
for (j = 0; j < n; j++)
y[i] += A[i][j]* x[j];
}
Biz   ishni   iplar   o'rtasida   taqsimlash   orqali   buni   parallel   qilishni   xohlaymiz.
Imkoniyatlardan   biri   -   tashqi   halqaning   iteratsiyasini   iplar   o'rtasida   taqsimlash.
Agar   shunday   qilsak,   har   bir   ip   y   ning   ba'zi   komponentlarini   hisoblab   chiqadi.
Masalan, m D n D 6 va iplar soni, iplar soni yoki t, uchta bo'lsin. Keyin hisoblashni
iplar orasida quyidagicha taqsimlash mumkin:
Y[0] ni hisoblash uchun 0-ip kodni bajarishi kerak bo'ladi
y[0] = 0.0;
for (j = 0; j < n; j++)
   y[0] += A[0][j]* x[j];
Shuning uchun 0 mavzui A satrining 0-qatorining har bir elementiga va x ning har
bir elementiga kirishi kerak bo'ladi. Umuman olganda, y[i] ga tayinlangan ip kodni
bajarishi kerak bo'ladi
y[i] = 0.0;
for (j = 0; j < n; j++)
y[i] += A[i][j]*x[j];
Shunday   qilib,   bu   ip   A   ning   i   qatorining   har   bir   elementiga   va   x   ning   har   bir
elementiga kirishi kerak bo'ladi. Biz har bir ip x ning har bir komponentiga kirishi
kerakligini  ko'ramiz, har  bir  ip esa faqat  o'ziga  tayinlangan A qatorlari  va y ning
tayinlangan   komponentlariga   kirishi   kerak.   Bu,  hech   bo'lmaganda,   x   ni   bo'lishish
kerakligini   ko'rsatadi.   Keling,   A   va   y   ni   ham   baham   ko'raylik.   Bu   faqat   global
bo'lishi kerak bo'lgan o'zgaruvchilarni global qilishimiz kerak degan tamoyilimizni
buzayotgandek tuyulishi mumkin. Biroq, mashqlarda biz A va y o'zgaruvchilarni ip
funksiyasi uchun lokal qilish bilan bog'liq ba'zi masalalarni batafsil ko'rib chiqamiz
va biz ularni global qilish yaxshi ma'noga ega ekanligini ko'ramiz. Shu nuqtada biz
shuni   kuzatamizki,   agar   ular   global   bo'lsa,   asosiy   ip   A   ning   barcha   yozuvlarini
stdin   dan   o'qish   orqali   osongina   ishga   tushirishi   mumkin   va   mahsulot   vektori   y asosiy   ip   tomonidan   osongina   chop   etilishi   mumkin.   Ushbu   qarorlarni   qabul
qilgandan   so'ng,   biz   faqat   y   ning   qaysi   komponentlarini   hisoblashini   hal   qilish
uchun   har   bir   ip   foydalanadigan   kodni   yozishimiz   kerak.   Kodni   soddalashtirish
uchun   m   va   n   ni   ham   t   ga   teng   bo'linishini   faraz   qilaylik.   m   D   6   va   t   D   3   bilan
bizning misolimiz shuni ko'rsatadiki, har bir ip m=t komponentlarini oladi. Bundan
tashqari, 0-ip birinchi m=t ni, 1-ip keyingi m=t ni oladi va hokazo. Shunday qilib,
q ipiga tayinlangan komponentlar uchun formulalar bo'lishi mumkin.
birinchi component:   va oxirgi component 
Ushbu   formulalar   yordamida   biz   matritsa-vektorni   ko'paytirishni   amalga
oshiradigan   ip   funksiyasini   yozishimiz   mumkin.   4.2.   Dasturga   qarang.   E'tibor
bering, ushbu kodda biz A, x, y, m va n global va umumiydir deb taxmin qilamiz.
Void* Pth mat vect(void* rank) f
long my rank = (long) rank;
int i, j;
int local m = m/thread count;
int my first row = my rank*local m;
int my last row = (my rank+1)*local m  *  1;
for (i = my first row; i <= my last row; i++) f
y[i] = 0.0;
for (j = 0; j < n; j++)
y[i] += A[i][j]*x[j];
g
return NULL;
g /* Pth mat vect */
4.2-dastur: Pthreads matritsasi-vektorlarni ko'paytirish
Agar siz MPI bo'limini allaqachon o'qib chiqqan bo'lsangiz, MPI yordamida
matritsa-vektorni   ko'paytirish   dasturini   yozish   uchun   ko'proq   mehnat   talab
qilinganini   eslaysiz.   Buning   sababi,   ma'lumotlar   tuzilmalari   majburiy   ravishda
taqsimlangan,   ya'ni   har   bir   MPI   jarayoni   faqat   o'zining   mahalliy   xotirasiga
to'g'ridan-to'g'ri   kirish   huquqiga   ega.   Shunday   qilib,   MPI   kodi   uchun   biz   kerak
Barcha x ni har bir jarayon xotirasiga aniq to'plang. Ushbu misoldan biz umumiy
xotira dasturlarini yozish taqsimlangan xotira dasturlarini yozishdan ko'ra osonroq
bo'lgan   holatlar   mavjudligini   ko'ramiz.   Biroq,   biz   tez   orada   umumiy   xotira
dasturlari murakkabroq bo'lishi mumkin bo'lgan vaziyatlar mavjudligini ko'ramiz. MUHIM BO'LIMLAR
Matritsa-vektorlarni   ko'paytirishni   kodlash   juda   oson   edi,   chunki   umumiy
xotira joylariga juda kerakli tarzda kirish mumkin edi. Ishga tushirilgandan so'ng,
barcha o'zgaruvchilar (y-dan tashqari) faqat iplar tomonidan o'qiladi. Ya'ni, y dan
tashqari,   umumiy   o'zgaruvchilarning   hech   biri   asosiy   oqim   tomonidan   ishga
tushirilgandan   keyin   o'zgartirilmaydi.   Bundan   tashqari,   iplar   y   ga   o'zgartirishlar
kiritsa-da, faqat bitta ip har qanday individual komponentga o'zgartirishlar kiritadi,
shuning   uchun   ikkita   (yoki   undan   ko'p)   iplar   tomonidan   biron   bir   komponentni
o'zgartirishga urinishlar bo'lmaydi. Agar bunday bo'lmasa nima bo'ladi? Ya'ni, bir
nechta   mavzular   bitta   xotira   joyini   yangilaganda   nima   bo'ladi?   Biz   buni   2   va   5-
boblarda  ham  muhokama qilamiz, shuning uchun agar  siz  ushbu boblardan birini
o'qigan bo'lsangiz, javobni allaqachon bilasiz. Ammo bir misolni ko'rib chiqaylik.
ning   qiymatini   baholashga   harakat   qilaylik.   Biz   foydalanishimiz   mumkin   bo'lgan
juda ko'p turli xil formulalar mavjud. Eng oddiylaridan biri bu hisoblash uchun eng
yaxshi formula emas, chunki u juda aniq bo'lishidan oldin o'ng tomonda juda ko'p
atamalarni   oladi.   Biroq,   bizning   maqsadlarimiz   uchun   ko'plab   shartlar   yaxshiroq
bo'ladi.
Quyidagi ketma-ket kod ushbu formuladan foydalanadi:
double factor = 1.0;
double sum = 0.0;
for (i = 0; i < n; i++, factor =  - factor) f
sum += factor/(2*i+1);
g
pi = 4.0*sum;
Biz   buni   matritsa-vektorni   ko'paytirish   dasturini   parallel   qilganimiz   kabi
parallellashtirishga harakat qilishimiz mumkin: for tsiklidagi takrorlanishlarni iplar
o'rtasida   taqsimlang   va   yig'indini   umumiy   o'zgaruvchiga   aylantiring.   Hisob-
kitoblarni soddalashtirish uchun iplar soni, iplar soni yoki t, yig'indidagi atamalar
sonini teng ravishda taqsimlaydi, n.
Keyin, agar Nn D n=t bo'lsa, 0-ip birinchi Nn hadlarni qo'shishi mumkin. Shuning
uchun,   0-ip   uchun,   halqa   o'zgaruvchisi   i   0   dan   Nn 1   gacha   bo'ladi.   1-mavzuga
keyingi   Nn   shartlar   qo'shiladi,   shuning   uchun   1-ip   uchun   halqa   o'zgaruvchisi   Nn
dan   2Nn	
 1   gacha   bo'ladi.   Umuman   olganda,   q   ip   uchun   halqa   o'zgaruvchi
oralig'ida bo'ladi 1 void* Thread sum(void* rank) f
2 long my rank = (long) rank;
3 double factor;
4 long long i;
5 long long my n = n/thread count;
6 long long my first i = my n*my rank;
7 long long my last i = my first i + my n;
8
9 if (my first i % 2 == 0) /* my first i is even */
10 factor = 1.0;
11 else /* my first i is odd */
12 factor =  - 1.0;
13
14 for (i = my first i; i < my last i; i++, factor =  - factor) f
15 sum += factor/(2*i+1);
16 }
17
18 return NULL;
19 g /* Thread sum */
Dastur 4.3  Hisoblash uchun threads funksiyasiga urinish
Bundan   tashqari,   birinchi   hadning   belgisi   qNn   hadi,   agar   qNn   juft   bo'lsa   ijobiy,
qNn toq bo'lsa manfiy bo'ladi. Tarmoq funktsiyasi 4.3-dasturda ko'rsatilgan koddan
foydalanishi  mumkin. Agar Pthreads dasturini ikkita ip bilan ishga tushirsak va n
nisbatan   kichik   bo'lsa,   Pthreads   dasturining   natijalari   ketma-ket   yig'indi   dasturi
bilan   mos   kelishini   topamiz.   Biroq,   n   kattalashgan   sari,   biz   o'ziga   xos   natijalarni
olishni boshlaymiz. Masalan, ikki yadroli protsessor bilan biz quyidagi natijalarga
erishamiz: E'tibor   bering,   biz   n   ni   oshirganimizdan   so'ng,   bitta   ip   bilan   baholash
yaxshilanadi va yaxshilanadi. Aslida, n ning har bir 10 omili ortishi bilan biz yana
bir   to'g'ri   raqamni   olamiz.   n   D   105   bilan   bitta   ip   bilan   hisoblangan   natija   beshta
to'g'ri  raqamga  ega. n D 106 bilan u oltita to'g'ri  raqamga ega va hokazo.  Ikki  ip
bilan hisoblangan natija bilan mos keladi.
n   D   bo'lganda   bitta   ip   bilan   hisoblangan   natija   105.   Biroq,   n   ning   kattaroq
qiymatlari   uchun   ikkita   ip   bilan   hisoblangan   natija   aslida   yomonlashadi.
Haqiqatdan ham, agar dasturni ikkita ip va bir xil n qiymati bilan bir necha marta
ishga   tushirsak,   ikkita   ip   bilan   hisoblangan   natija   bajarilishdan   ishga   o'tishini
ko'ramiz.   Bizning   asl   savolimizga   javob   aniq   bo'lishi   kerak:   "Ha,   bir   nechta
mavzular bitta umumiy o'zgaruvchini yangilashga harakat qilsa, bu muhim."
Keling, nima uchun bunday bo'lganini eslaylik. Esda tutingki, ikkita qiymat
qo'shilishi   odatda   bitta   mashina   ko'rsatmasi   emas.   Masalan,   biz   y   xotira
manzilining mazmunini bitta C iborasi bilan x xotira joyiga qo'shishimiz mumkin
bo'lsa-da,
x = x + y;
Mashinaning qiladigan ishi odatda murakkabroq. X va y da saqlangan joriy
qiymatlar, umuman olganda, arifmetik amallarni bajarish uchun sxemasi bo'lmagan
kompyuterning asosiy xotirasida saqlanadi. Qo'shishni amalga oshirishdan oldin, x
va  y   da   saqlangan   qiymatlarni   asosiy   xotiradan   CPU   registrlariga   o'tkazish   kerak
bo'lishi   mumkin.   Qiymatlar   registrlarga   kiritilgandan   so'ng,   qo'shish   amalga
oshirilishi   mumkin.   Qo'shish   tugagandan   so'ng,   natija   registrdan   xotiraga
o'tkazilishi kerak bo'lishi mumkin.
Aytaylik,   bizda   ikkita   ip   bor   va   ularning   har   biri   o'zining   shaxsiy
o'zgaruvchisi   yda   saqlanadigan   qiymatni   hisoblaydi.   Bundan   tashqari,   biz   ushbu
shaxsiy   qiymatlarni   asosiy   oqim   tomonidan   0   ga   ishga   tushirilgan   umumiy   x
o'zgaruvchisiga   qo'shmoqchimiz   deb   faraz   qilaylik.   Har   bir   ip   quyidagi   kodni
bajaradi:
y = Compute(my rank);
x = x + y;
Shuningdek, 0 ip y = 1 ni va 1 ip y = 2 ni hisoblaydi deb faraz qilaylik. “To‘g‘ri” 
natija x = 3 bo‘lishi kerak. Mana bitta mumkin bo‘lgan stsenariy:  Foydalangan adabiyotlar
1.  I. Foster, Designing and Building Parallel Programs, Addison-Wesley, Reading, 
MA,   1995. Also available from <http://www.mcs.anl.gov/itf/dbpp/> (accessed 
21.09.10).
2.  I. Foster, C. Kesselman, (Eds.), The Grid 2, second ed., Blueprint for a New 
Computing   Infrastructure, Morgan Kaufmann, San Francisco, 2003.
3.    The GNU MP Bignum Library. <http://gmplib.org/> (accessed 21.09.10)
4.   A. Grama, et al., Introduction to Parallel Computing, second ed., Addison-
Wesley,   Harlow, Essex, 2003.

Mavzu: Pthreads bilan Umumiy xotirali dasturlash Reja: Kirish 1. Pthread s jarayonlar va kutubxonalar 2. Dastlabki o'yinlar 3. Matritsa-vektorlarni ko'shtirish 4. MUHIM BO'LIMLAR Foydalangan adabiyotlar

Kirish Eslatib o'tamiz, dasturchi nuqtai nazaridan umumiy xotira tizimi bitta barcha yadrolar barcha xotira joylariga kirishi mumkin (4.1-rasmga qarang). Shunday qilib yadrolar ishini muvofiqlashtirish muammosiga aniq yondashuvni aniqlashtirishdir ba'zi xotira joylari "birgalikda" ekanligi. Bu parallellikka juda tabiiy yondashuv dasturlash. Haqiqatan ham, nima uchun barcha parallel dasturlar buni ishlatmasligiga hayron bo'lishimiz mumkin umumiy xotira yondashuvi. Biroq, biz ushbu bobda muammolar mavjudligini ko'ramiz umumiy xotira tizimlarini dasturlashda ko'pincha muammolardan farq qiladi taqsimlangan xotira dasturlashda duch keladigan muammolar. Misol uchun, 2-bobda biz ko'rdikki, agar turli yadrolar bitta umumiy xotira manzilini yangilashga harakat qilsa, u holda uning mazmuni qo'yilgan joy oldindan aytib bo'lmaydigan bo'lishi mumkin. Umumiy joylashuvni yangilaydigan kod muhim bo'limga misoldir. Biz tanqidiy bo'limlarning boshqa misollarini ko'rib chiqamiz va biz bir nechta usullarni o'rganamiz muhim bo'limga kirishni nazorat qilish uchun. Biz umumiy xotira dasturlashning boshqa masalalari va usullari haqida ham bilib olamiz. Umumiy xotirali dasturlashda protsessorda ishlaydigan dasturning namunasi odatdathreadsdeb ataladi (MPI dan farqli o'laroq, u jarayon deb ataladi). Har birthreadsblokni bajarishni kutishi uchun iplarni qanday sinxronlashtirishni o'rganamiz boshqa mavzu ba'zi ishni tugatmaguncha bayonotlar. Vaziyat yuzaga kelmaguncha, biz ipni qanday qilib "uxlashga" qo'yishni o'rganamiz. Biz ba'zi holatlar mavjudligini ko'ramiz, ular dastlab tanqidiy qism juda katta bo'lishi kerakdek tuyulishi mumkin. Biroq, biz ba'zan dasturning ko'p qismini parallel ravishda bajarish uchun ushbu katta kod bloklariga kirishni "nozik sozlash" imkonini beradigan vositalar mavjudligini ham ko'ramiz. Biz kesh xotiralaridan foydalanish umumiy xotira dasturining sekinroq ishlashiga olib kelishi mumkinligini ko'ramiz. Nihoyat, biz ketma-ket qo'ng'iroqlar orasida "holatni saqlaydigan" funktsiyalar nomuvofiq yoki hatto noto'g'ri natijalarga olib kelishi mumkinligini ko'ramiz. Ushbu bobda biz umumiy xotira funktsiyalarining ko'pchiligi uchun POSIX R iplaridan foydalanamiz. Keyingi bobda biz OpenMP deb nomlangan umumiy xotira dasturlashning muqobil yondashuvini ko'rib chiqamiz.

PTHREADS JARAYONLAR VA KUTUBXONALAR 2-bobdan eslaylikki, umumiy xotirali dasturlashdathreadsbiroz MPI dasturlash jarayoniga o'xshaydi. Biroq, bu printsipial jihatdan, "engilroq" bo'lishi mumkin 4.1-rasm. Umumiy xotira tizimi Jarayon ishlayotgan (yoki foydalanilgan) dasturning namunasidir. Bajarilishi mumkin bo'lgan narsadan tashqari, u quyidagilardan iborat:  Stack uchun xotira bloki  Yig ‘ish uchun xotira bloki  Tizim jarayon uchun ajratgan resurslar deskriptorlari, masalan, fayl deskriptorlari.  Xavfsizlik ma'lumotlari - masalan, jarayon qaysi apparat va dasturiy ta'minot resurslariga kirishi mumkinligi haqidagi ma'lumot.  Jarayonning holati to'g'risidagi ma'lumotlar, masalan, jarayon ishga tayyormi yoki resursda kutilmoqdami, registrlar tarkibi, shu jumladan dastur hisoblagichi va boshqalar. Aksariyat tizimlarda, sukut bo'yicha, jarayonning xotira bloklari shaxsiydir: agar operatsion tizim aralashmasa, boshqa jarayon jarayonning xotirasiga bevosita kira olmaydi. Bu mantiqiy. Agar siz dastur yozish uchun matn muharriridan foydalanayotgan bo'lsangiz (bitta jarayon - ishlaydigan matn muharriri), brauzeringiz (boshqa jarayon) matn muharriri xotirasini qayta yozishni xohlamaysiz. Bu ko'p foydalanuvchi muhitida yanada muhimroq. Bir foydalanuvchining jarayonlariga boshqa foydalanuvchi jarayonlarining xotirasiga kirishga ruxsat berilmasligi kerak. Biroq, biz umumiy xotira dasturlarini ishga tushirganimizda, bu biz xohlagan narsa emas. Hech bo'lmaganda, biz ba'zi o'zgaruvchilar bir nechta jarayonlar uchun mavjud bo'lishini xohlaymiz, shuning uchun umumiy xotira "jarayonlari" odatda bir-birining xotirasiga kirishni ancha osonlashtiradi. Shuningdek, ular tez-tez stdout-ga kirish kabi narsalarni baham ko'rishadi. Aslida,

ular o'zlarining steklari va dastur hisoblagichlaridan tashqari, jarayonga xos bo'lgan deyarli hamma narsani baham ko'rishlari mumkin. Buni bitta jarayonni boshlash va keyin jarayonni ushbu "yengilroq" jarayonlarni boshlash orqali nisbatan oson tartibga solish mumkin. Shu sababli, ular ko'pincha engil vaznli jarayonlar deb ataladi. Ko'proq ishlatiladigan atama, ip, "nazorat ipi" tushunchasidan kelib chiqqan. Boshqaruv zanjiri - bu dasturdagi gaplar ketma-ketligi. Bu atama bitta jarayonda boshqaruv oqimini nazarda tutadi va umumiy xotira dasturida bitta jarayon bir nechta boshqaruv zanjiriga ega bo'lishi mumkin. Yuqorida aytib o'tganimizdek, ushbu bobda biz foydalanadigan iplarning maxsus amalga oshirilishi POSIX iplari yoki ko'pincha Pthreads deb ataladi. POSIX [41] Unix-ga o'xshash operatsion tizimlar uchun standartdir, masalan, Linux va Mac OS X. U bunday tizimlarda mavjud bo'lishi kerak bo'lgan turli xil imkoniyatlarni belgilaydi. Xususan, u ko'p bosqichli dasturlash uchun amaliy dasturlash interfeysini (API) belgilaydi. Pthreads dasturlash tili emas (masalan, C yoki Java). Aksincha, MPI kabi, Pthreads C dasturlari bilan bog'lanishi mumkin bo'lgan kutubxonani belgilaydi. MPI dan farqli o'laroq, Pthreads API faqat POSIX tizimlarida mavjud - Linux, Mac OS X, Solaris, HPUX va boshqalar. MPI-dan farqli o'laroq, ko'p tarmoqli dasturlash uchun keng qo'llaniladigan bir qator boshqa spetsifikatsiyalar mavjud: Java iplari, Windows ish zarralari, Solaris iplari. Biroq, barchathreadsspetsifikatsiyalari bir xil asosiy g'oyalarni qo'llab-quvvatlaydi, shuning uchun Pthreads-da qanday dasturlashni o'rganganingizdan so'ng, boshqa thread API-ni qanday dasturlashni o'rganish qiyin bo'lmaydi. Pthreads C kutubxonasi bo'lganligi sababli, u, asosan, C++ dasturlarida ishlatilishi mumkin. Biroq, umumiy xotira dasturlash uchun C++ standartini (C++ 0x) ishlab chiqish ustida ish olib borilmoqda. Agar siz C++ dasturlarini yozayotgan bo'lsangiz, Pthreads o'rniga undan foydalanish mantiqiy bo'lishi mumkin.

SALOM DUNYO Qani boshladik. Keling, Pthreads dasturini ko'rib chiqaylik. 4.1-dastur asosiy funktsiya bir nechta iplarni ishga tushiradigan dasturni ko'rsatadi. Har bir mavzu xabarni chop etadi va keyin tugaydi. Bajarish Dastur oddiy C dasturi kabi tuzilgan, bundan mustasno, Pthreads kutubxonasiga ulanishimiz kerak bo'lishi mumkin: 1 $ gcc — g — Wall — o_pth hello pth hello.c — lpthread -lpthread kompilyatorga Pthreads kutubxonasida bog'lanmoqchi ekanligimizni bildiradi. E'tibor bering, bu -lpthread emas, balki -lpthread. Ba'zi tizimlarda kompilyator avtomatik ravishda kutubxonaga bog'lanadi va -lpthread kerak bo'lmaydi. Dasturni ishga tushirish uchun quyidagi kodni yozamiz $ ./pth hello <number of threads> 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 5 / _ Global variable: accessible to all threads _ / 6 int thread count; 7 8 v oid _ Hello( v oid _ rank); / _ Thread function _ / 9 10 int main( int argc, char _ argv[]) f 11 long thread; / _ Use long in case of a 64 ??????bit system _ / 12 pthread t _ thread handles; 13 14 / _ Get number of threads from command line _ / 15 thread count = strtol(argv[1], NULL, 10); 16 17 thread handles = malloc (thread count _ sizeof (pthread t)); 18 19 for (thread = 0; thread < thread count; thread++) 1 Eslatib o'tamiz, dollar belgisi ($) qobiq so'rovidir, shuning uchun uni kiritmaslik kerak. Shuni ham yodda tutingki, biz Gnu C kompilyatoridan foydalanamiz, deb taxmin qilamiz, gcc va biz doimo variantlari — g, — Wall, and — o. Qo'shimcha ma'lumot uchun 2.9-bo'limga qarang.