Pemrograman shared memori

8. Pemrograman Dengan Memori Bersama
8.1. Multiprosesor Memori Bersama
8.2. Membangun Kepararelan Khusus
Membuat Proses Simultan
Threads
8.3. Data yang Digunakan Bersama
Membuat Data Yang Digunakan Bersama
Mengakses Data Yang Digunakan Bersama
Konstruksi Bahasa untuk Kepararelan
Analisis Ketergantungan
Data yang Digunakan Bersama dalam Sistem dengan Cache
8.4. Contoh Program
Pemrosesan dalam UNIX
Contoh dengan JAVA
Referensi : Wilkinson, B, Parallel Programming, Chapter 8, hal. 227-263
Pengolahan Paralel/KK021239

8/1/51

Sistem Multiprosesor Dengan Memori Bersama
Setiap lokasi memori dapat diakses oleh prosesor

manapun.
Sebuah Ruang alamat tunggal terjadi, ini berarti bahwa
setiap lokasi memori diberikan alamat tunggal/unik
diantara area tunggal suatu alamat.
Untuk sejumlah kecil prosesor, arsitektur umumnya
adalah arsitektur bus tunggal :

Pengolahan Paralel/KK021239

8/2/51

Gambar 8.1

Shared Memory Multiprocessor
menggunakan Bus Tunggal

Pengolahan Paralel/KK021239

8/3/51


Beberapa Alternatif Pemrograman
• Menggunakan bahasa pemrograman baru
• Memodifikasi bahasa sekuensial yang ada
• Menggunakan rutin library dengan bahasa sekuensial
yang ada
• Menggunakan bahasa pemrograman sekuensial yang
ada dan perintahkan kompiler paralel untuk
mengkonversinya ke dalam kode yang dapat dieksekusi
secara paralel
• Pemrosesan UNIX
• Threads (Pthreads, Java, …)

Pengolahan Paralel/KK021239

8/4/51

TABEL 8.1 Beberapa Bahasa Pemrograman Paralel

Pengolahan Paralel/KK021239


8/5/51

Membangun Kepararelan Khusus
Membuat Proses Simultan - Konstruksi

FORK-JOIN

Gambar 8.2 Konstruksi FORK-JOIN
Pengolahan Paralel/KK021239

8/6/51

Pemrosesan Heavyweight UNIX
Sistem UNIX memanggil fork() untuk membuat sebuah proses baru.
Proses baru (child process) adalah suatu salinan eksak dari suatu
pemanggilan proses kecuali apabila proses baru tersebut mempunyai
ID proses unik. Child process mempunyai salinannya sendiri dari
varibel parent- nya. Variabel parent diberikan nilai yang sama seperti
variabel original pada saat proses inisialisasi. Proses fork memulai
eksekusi pada saat titik fork.

Apabila proses fork tersebut berhasil, fork() return ()kepada child
process dan return ID proses dari child process ke parent process.
Proses-proses tersebut digabungkan (join) dengan pemanggilan sistem
wait () dan exit(), yang didefinisikan sebagai berikut :

Pengolahan Paralel/KK021239

8/7/51

wait(statusp); /*delays caller until signal received or one of its
*/
/*child processes terminates or stops */
exit(status); /*terminates a process. */
Sebuah Child Process dapat dibuat melalui :
pid = fork(); /* fork */
Kode akan dieksekusi oleh child dan parent
if (pid == 0) exit(0); else wait(0); /* join */

Pengolahan Paralel/KK021239


8/8/51

Jika sebuah child untuk mengeksekusi kode yabg berbeda,
dapat menggunakan :
pid = fork();
if (pid == 0) {
Kode akan dieksekusi oleh slave
} else {
Kode akan dieksekusi oleh parent
}
if (pid == 0) exit(0); else wait(0);
Pengolahan Paralel/KK021239

8/9/51

Threads
Proses “heavyweight”- benarbenar program terpisah dengan
Variabelnya sendiri, stack, dan
Alokasi memori


Threads - Secara bersama-sama
Membagi ruang memori yang
sama dan variabel global diantara
rutin-rutin
Pengolahan Paralel/KK021239

8/10/51

Pthreads:
IEEE Portable Operating System Interface, POSIX, section 1003.1 standard

Gambar 8.4 pthread_create()

Pengolahan Paralel/KK021239

and pthread_join().
8/11/51

Pthread Barrier
Rutin pthread_join() menunggu satu thread tertentu untuk

mengakhiri/terminasi.
Untuk membuat satu barrier menunggu seluruh threads,
pthread_join() dapat diulang :
for (i = 0; i < n; i++)
pthread_create(&thread[i], NULL, (void *) slave, (void *) &arg);
for (i = 0; i < n; i++)
pthread_join(thread[i], NULL);
Pengolahan Paralel/KK021239

8/12/51

Detached Threads
Dapat saja terjadi bahwa thread tidak terganggu ketika satu thread
terminasi dan pada kasus tersebut join tidak diperlukan.
Threads yang tidak di-join disebut sebagai detached threads.

Gambar 8.5 Detached threads.
Pengolahan Paralel/KK021239

8/13/51


Urutan Eksekusi Perintah
Pada suatu sistem multiprosesor, instruksi dari suatu proses/
thread dapat saja berselangan waktunya.

Contoh
Proses 1
Instruction 1.1
Instruction 1.2
Instruction 1.3

Proses 2
Instruction 2.1
Instruction 2.2
Instruction 2.3

Ada beberapa urutan yang memungkinkan, termasuk :
Instruction 1.1
Instruction 1.2
Instruction 2.1

Instruction 1.3
Instruction 2.2
Instruction 2.3
Diasumsikan satu instruksi tidak dapat dibagi ke dalam
langkah yang lebih kecil
Pengolahan Paralel/KK021239

8/14/51

Jika dua proses mencetak pesan, sebagai contoh, pesan
tersebut dapat muncul dalam urutan yang berbeda
tergantung kepada penjadwalan dari pemanggilan proses
dari rutin pencetakan.
Keadaan terburuk adalah, setiap karakter dari pesan tersebut
dapat berselang jika instruksi mesin dari rutin pencetakan
dapat berselang.

Pengolahan Paralel/KK021239

8/15/51


Optimasi Kompiler/Prosesor
Kompiler (atau prosesor) dapat mengurutkan kembali instruksi
untuk keperluan optimasisasi.
Contoh
Pernyataan

a = b + 5;
x = y + 4;
dapat dikompilasi untuk mengeksekusi dalam urutan terbalik :
x = y + 4;
a = b + 5;
dan masih benar secara logik.
Akan sangat menguntungkan apabila men-delay pernyataan a =
b + 5 karena beberapa instruksi sebelumnya yang masih/sedang
dieksekusi oleh prosesor memerlukan lebih banyak waktu untuk
menghasilkan nilai untuk b.
Pengolahan Paralel/KK021239

8/16/51


Rutin Thread-Safe
Pemanggilan sistem atau rutin libarari disebut sebagai thread
safe dapat dipanggil dari thread multipel secara simultan dan
selalu menghasilkan hasil and selalu menghasilkan hasil yang
benar.
Contoh
Standard I/O thread safe (mencetak pesan tanpa mengacaukan
karakternya).
Rutin yang mengakses data bersama/statis memerlukan perhatian
yang khusus agar dapat membuat suatu thread safe.

Pengolahan Paralel/KK021239

8/17/51

Mengakses Data Bersama
Dimisalkan ada 2 proses, dimana satu proses berfungsi untuk
menambah nilai 1 ke dalam item data bersama, x. Diperlukan
pembacaan untuk isi dari lokasi x, hasil perhitungan x + 1 dan
hasil penulisan kembali ke lokasi semula. Dengan 2 proses
melakukan hal tersebut pada saat yang sama, maka didapat :
Instruction
x = x + 1;

Process 1
read x
compute x+1
write to x

Process 2
read x
compute x +1
write to x

Time

Pengolahan Paralel/KK021239

8/3jml. Slide 8/18/51

Gambar 8.6 Konflik dalam Pengaksesan variabel Bersama
Pengolahan Paralel/KK021239

8/19/51

Bagian Kritis
Suatu mekanisme untuk menentukan bahwa hanya satu
proses mengakses resource tertentu pada satu waktu adalah
dengan membentuk bagian dari kode yang terlibat dalam
resource tersebut disebut sebagai Bagian Kritis (Critical
Sections) dan mengatur bahwa hanya satu bagian kritis yang
dieksekusi pada satu waktu.
Apabila satu proses telah selesai menyelesaikan bagian
kritisnya, maka proses lain diijinkan untuk memasuki bagian
kritis untuk resource yang sama. Mekanisme tersebut dikenal
sebagai mutual exclusion.

Pengolahan Paralel/KK021239

8/20/51

LOCK
adalah mekanisme sederhana untuk memastikan mutual
exclusion dari suatu bagian kritis (critical section)
Suatu lock adalah variabel 1-bit dimana angka 1
mengindikasikan bahwa satu proses telah masuk ke bagian
kritis dan satu () mengindikasikan bahwa tidak ada proses di
dalam bagian kritis.
Suatu lock beroperasi seperti penguncian suatu pintu.
Suatu proses menuju ke suatu “pintu” dari bagian kritis dan
menemukan “pintu” yang terbuka maka diperbolehkan masuk
ke bagian kritis, kemudian mengunci pintunya agar
menhindari proses lain yang ingin masuk. Setelah proses
selesai, maka pintunya dibuka (unlock) dan meninggalkannya.
Pengolahan Paralel/KK021239

8/21/51

SPIN LOCK
Contoh
while (lock == 1) do_nothing;
lock = 1;
.
critical section
.
lock = 0;

Pengolahan Paralel/KK021239

/* no operation in while loop */
/* enter critical section */

/* leave critical section */

8/22/51

Gambar 8.7 Mengontrol bagian kritis melalui busy waiting.

Pengolahan Paralel/KK021239

8/23/51

Deadlock
Deadlock dapat terjadi dengan 2 proses, dimana proses
yang satu memerlukan satu resource yang sedang
digunakan oleh yang lainnya, dan proses ini
memerlukan resource yang digunakan oleh proses
pertama.

Gambar 8.8 Deadlock (deadly embrace).
Pengolahan Paralel/KK021239

8/24/51

Dapat saja terjadi suatu dealock yang circular, dimana beberapa
proses mempunyai resource yang diinginkan oleh proses lainnya.

Gambar 8.8 Deadlock n-proses
Pengolahan Paralel/KK021239

8/25/52

Semaphores
Suatu semaphore, s (say), adalah satu bilangan bulat positif
(termasuk nol) yang dioperasikan pada dua operasi yang
diberi nama p dan v.
Operasi p, p(s)
menunggu sampai s lebih besar dari 0 (nol) dan kemudian
mengurangi s dengan 1 (satu) dan mengijinkan proses untuk
berlanjut.
Operasi v, v(s)
menambah s dengan 1 (satu) untuk melepaskan satu proses
yang menunggu (bila memang ada).
Operasi p dan v dilaksanakan secara individu. Proses yang didelay oleh p(s) disimpan dalam suatu abeyance sampai
dilepaskan oleh v(s) pada semaphore yang sama.
Pengolahan Paralel/KK021239

8/26/51

Mutual Exclusion dari Bagian Kritis (Critical Section)
: Dapat dicapai dengan satu semaphore yang mempunyai
nilai 0 atau 1 (binary semaphore), yang bertindak sebagai
satu variabel lock, tetapi operasi p dan v termasuk mekanisme
proses penjadwalan. Semaphore diinisialisasi dengan 1,
mengindikasikan bahwa tidak ada proses di dalam bagian
kritis yang berasosiasi dengan semaphore.

Pengolahan Paralel/KK021239

8/27/51

General Semaphores
: Dapat diambil pada nilai positif selain 0 (nol) dan 1 (satu).
Rutin semaphore terdapat untuk proses dalam UNIX. Tetapi
rutin tersebut tidak terdapat dala Ptread.

Monitor
Suatu prosedur yang menyediakan metode untuk mengakses resource
bersama. (shared resource).
Data dan operasi yang dapat beroperasi pada suatu data adalah
terenkapsulasi ke dalam satu struktur. Membaca dan menulis hanya
dapat dilakukan denganmenggunakan prosedur monitor, dan hanya satu
proses dapat menggunakan prosedur pada satu saat.
Pengolahan Paralel/KK021239

8/28/51

Satu prosedur monitor dapat diimplementasikan
menggunakan semaphore untuk melindungi entri-nya.
Contoh monitor dengan Java :

Pengolahan Paralel/KK021239

8/29/51

Operasi Variabel Kondisi
Wait(cond_var)
Signal(cond_var)
Status(cond_var)

Kondisi menunggu terjadi
Sinyal yang kondisinya terjadi
Return jumlah proses menunggu yang
terjadi.

Operasi wait juga akan melepaskanlock atau semaphoredan dapat
digunakan untuk mengijinkan proses lain masuk ke dalam kondisi.

Contoh :
Umpamakan satu atau lebih proses (atau thread) didesain untuk
mengambil aksi ketika satu kaunter, x, adalah nol (0). Proses atau
thread lain bertanggungjawab untuk mengurangi kaunter.
Pengolahan Paralel/KK021239

8/30/51

Rutin dapat berbentuk sebagai berikut :

Pengolahan Paralel/KK021239

8/31/51

Konstruksi Bahasa untuk Kepararelan
Data Bersama
Variabel memori bersama dapat dideklarasikan sebagai
bersama (shared), katakan sebagai : shared int x;
Konstruksi Paralel : par
Untuk menspesifikasikan perintah yang simultan :
par {
S1;
S2;
.
.
Sn;
}
Pengolahan Paralel/KK021239

8/32/51

Konstruksi forall
Untuk memulai proses sejenis yang serupa secara bersama-sama :
Forall (I=0; I < n; I++) {
S1;
S2;
.
.
Sm;
}
Yang menghasilkan proses n, dimana masing-masingnya berisi perintah
yang membentuk tubuh dari loop for, s1,s2, …,sm. Setiap proses
mengubakan nilai yang berbeda dari i.
Contoh :

forall (i = 0; i < 5; i++)
a[i] = 0;

Membersihkan a[0], a[1], a[2], a[3], dan a[4] menjadi nol secara
simultan.
Pengolahan Paralel/KK021239

8/33/51

Analisis Ketergantungan
Untuk mengidentifikasi proses mana yang dapat dieksekusi secara
bersama-sama :
Contoh :
Forall (i=0; i < 5; i++)
a[i] = 0;
Topi setiap instant dari tubuh adalah independen dari instant lain dan
semua instant dapat dieksekusi secar simultan.

Bagaimanapun juga hal tersebut tidak begitu jelas. Memerlukan cara
algoritma dalam mengenalkan ketergantunga untuk kompiler paralel.

Pengolahan Paralel/KK021239

8/34/51

Kondisi Bernstein
Himpunan kondisi yang cukup untuk menentukan apakah 2 proses
dapat dieksekusi secara simultan.
Mari kita mendefinisikan 2 buah himpunan berikut ini :
Ii adalah himpunan dari lokasi memori yang dibaca oleh proses Pi.
Oj adalah himpunan dari lokasi memori yang dimasuki oleh proses Pj.

Untuk dua proses P1 dan P2 yang akan dieksekusi secara simultan
input untuk proses P1 harus bukan bagian dari output dari P2 dan

input dari P2 harus bukan bagian dari output dari P1 misalnya :

Pengolahan Paralel/KK021239

8/35/51

Dimana  adalah himpunan kosong. Himpunan output dari tiap
proses harus berbeda. Misalnya :

Jika tiga kondisi terpenuhi, dua proses dapat dieksekusi secara simultan

Contoh :
Misalkan 2 perintah (dalam C) berikut ini :

a = x + y;
b = x + z;
Pengolahan Paralel/KK021239

8/36/51

Kita mempunyai :

I1 = (x, y) O1 = (a)
I2 = (x, z) O2 = (b)
Dan kondisi :

Adalah terpenuhi. Maka perintah a = x+y dan b =x+z dapat
dieksekusi secara simultan.
Pengolahan Paralel/KK021239

8/37/51

Data Bersama dalam Sistem dengan Cache
Semua komputer modern mempunyai memori cache, memori
berkecepatan tinggi sangat lekat dengan prosesor untuk menangani
data referansi dan kode terkini.

Protokol Koheren Cache
Dalam kebijaksanaan update, salinan data dalam semua cache
dimodifikasi pada saat satu salinan masuk.
Dalam kebijaksanaan invalidate, ketika satu salinan data dimasukan,
data yang sama pada cache lain di-invalidate (melalui pe-reset-an
kembali satu bit yang valid dalam cache). Salinan tersebut hanya
dimodifikasi ketika prosesor yang berasosiasi membuat referensi
untuk itu.
Pengolahan Paralel/KK021239

8/38/51

False Sharing
Bagian yang berbeda
dari blok dibutuhkan oleh
prosesor yang berbeda tetapi
byte-nya tidak sama.
Jika satu prosesor menulis
satu bagian dari blok, salinan
dari blok komplit dalam cache
berbeda harus sudah
dimodifikasi atau di -invalidatemelalui data yang aktual tidak
dapat dibagi bersama.

Pengolahan Paralel/KK021239

8/39/51

Solusi untuk False Sharing
Kompiler memasuki layout dari data yang disimpan dalam memori utama,
memisahkan data hanya yang dimasukan oleh satu prosesor ke dalam
blok yang berbeda.

Contoh Program
Untuk menjumlahkan elemen dari suatu array, a[1000]:
int sum, a[1000];
sum = 0;
for (i = 0; i < 1000; i++)
sum = sum + a[i];
Pengolahan Paralel/KK021239

8/40/51

Proses UNIX
Kalkulasi akan dibagi menjadi dua bagian, satu melakukan i genap
dan yang satu lagi melakukan i ganjil, misalnya :
Process 1
Process 2
sum1 = 0;
sum2 = 0;
for (i = 0; i < 1000; i = i + 2)
for (i = 1; i < 1000; i = i + 2)
sum1 = sum1 + a[i];
sum2 = sum2 + a[i];
Tiap proses akan menambah hasilnya (sum1 atau sum2) ke satu akumulai hasil,
sum :
sum = sum + sum1;
sum = sum + sum2;

Pengolahan Paralel/KK021239

8/41/51

Gambar 8.10 Lokasi Memori Bersama
untuk contoh program UNIX

Pengolahan Paralel/KK021239

8/42/51

#include
#include
#include
#include
#include
#include
#define array_size 1000
/* no of elements in shared memory */
extern char *shmat();
void P(int *s);
void V(int *s);
int main()
{
int shmid, s, pid;
/* shared memory, semaphore, proc id */
char *shm;
/*shared mem. addr returned by shmat()*/
int *a, *addr, *sum;
/* shared data variables*/
int partial_sum;
/* partial sum of each process */
int i;
/* initialize semaphore set */
int init_sem_value = 1;
s = semget(IPC_PRIVATE, 1, (0600 | IPC_CREAT))
if (s == -1) {
/* if unsuccessful*/
perror("semget");
exit(1);
}
Pengolahan Paralel/KK021239

8/43/51

if (semctl(s, 0, SETVAL, init_sem_value) < 0) {
perror("semctl");
exit(1);
}
/* create segment*/
shmid = shmget(IPC_PRIVATE,(array_size*sizeof(int)+1),
(IPC_CREAT|0600));
if (shmid == -1) {
perror("shmget");
exit(1);
}
/* map segment to process data space */
shm = shmat(shmid, NULL, 0);
/* returns address as a character*/
if (shm == (char*)-1) {
perror("shmat");
exit(1);
}

Pengolahan Paralel/KK021239

8/44/51

addr = (int*)shm; /* starting address */
sum = addr; /* accumulating sum */
addr++;
a = addr; /* array of numbers, a[] */
*sum = 0;
for (i = 0; i < array_size; i++) /* load array with numbers */
*(a + i) = i+1;
pid = fork(); /* create child process */
if (pid == 0) { /* child does this */
partial_sum = 0;
for (i = 0; i < array_size; i = i + 2)
partial_sum += *(a + i);
else { /* parent does this */
partial_sum = 0;
for (i = 1; i < array_size; i = i + 2)
partial_sum += *(a + i);
}
P(&s); /* for each process, add partial sum */
*sum += partial_sum;
V(&s);

Pengolahan Paralel/KK021239

8/45/51

printf("\nprocess pid = %d, partial sum = %d\n", pid, partial_sum);
if (pid == 0) exit(0); else wait(0); /* terminate child proc */
printf("\nThe sum of 1 to %i is %d\n", array_size, *sum);
/* remove semaphore */
if (semctl(s, 0, IPC_RMID, 1) == -1) {
perror("semctl");
exit(1);
}
/* remove shared memory */
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl");
exit(1);
}
} /* end of main */

Pengolahan Paralel/KK021239

8/46/51

void P(int *s) /* P(s) routine*/
{
struct sembuf sembuffer, *sops;
sops = &sembuffer;
sops->sem_num = 0;
sops->sem_op = -1;
sops->sem_flg = 0;
if (semop(*s, sops, 1) < 0) {
perror("semop");
exit(1);
}
return;
}
void V(int *s) /* V(s) routine */
{
struct sembuf sembuffer, *sops;
sops = &sembuffer;
sops->sem_num = 0;
sops->sem_op = 1;
sops->sem_flg = 0;
if (semop(*s, sops, 1)