Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
Implementasi Bubble Sort Dengan Metode Foster dan MPI Pendahuluan Pengurutan/sorting data merupakan operasi yang sangat umum dilakukan oleh komputer karena data yang terurut lebih mudah diproses daripada data yang masih acak. Sorting didefinisikan sebagi sebuah task untuk mengatur sekumpulan data yang tersusun secara acak sehingga dihasilkan susunan yang terurut sesuai dengan elemennya (dari yang kecil ke besar atau sebaliknya). Mem-parallel-kan sebuah algoritma sequencial sorting melibatkan pendistribusian elemen yang akan diurutkan kepada prosesor yang tersedia. Ada beberapa hal yang harus diperhatikan, yaitu: 1. dimana tempat penyimpanan input dan output o pada algoritma sekuensial proses ini dengan cepat dapat dilakukan hanya dengan menggunakan memori lokalnya o pada algoritma parallel, jika elemen terletak pada proses lokal maka akan menjadi cukup mudah sedangkan jika elemen berada pada proses yang berbeda maka akan menjadi lebih rumit 2. Bagaimana proses pembandingan dilakukan oleh algoritma o Satu elemen per proses o Lebih dari satu elemen per proses Desain Parallel Dengan Metode Foster Salah satu metode yang umum dipakai dalam mendesain algoritma parallel adalah dengan menerapkan metode Foster. Task sebagai unit komputasi terdiri dari: Program Local Memory Sekumpulan I/O ports Task pada program parallel berkomunikasi satu sama lain melalui pertukaran pesan (message excahange). Metode Foster pada dasarnya merupakan serangkaian tahapan mulai dari pembagian data/proses menjadi beberapa bagian (task), menentukan komunikasi antar task, mengumpulkan task yang memiliki komunikasi intens terhadap task yang lainnya, dan terakhir melakukan mapping kelompok task tersebut kedalam beberapa prosesor yang ada. Berikut detail metode Foster: 1. Partitioning Pada tahap ini dilakukan pemecahan data maupun komputasi menjadi bagian yang lebih kecil. Terdapat 2 jenis partitioning: Domain Decomposition dan Functional Decomposition. Beberapa hal yang perlu diperhatikan ketika melakukan partitioning antara lain: Minimasi kompustasi yang berlebihan dan storage data yang redundan. Task yang sudah primitif (task yang tidak terbagi lagi) berukuran sama satu dengan yang lainnya. Jumlah task akan bertambah seiring dengan bertambahnya ukuran masalah.
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
2. Communication Pada tahap ini ditentukan struktur komunikasi antar task yang ada. Komunikasi ini nanti akan menentukan struktur data dan nilai yang akan dipertukarkan diantara task. Terdapat 2 macam komunikasi: Komunikasi Lokal (Local Communication): task memerlukan nilai dari sejumlah kecil task yang lainnya. Channel untuk ilustrasi aliran data perlu dibuat. Komunikasi Global (Global Communication): task dalam jumlah yang signifikan mengkontribusikan data untuk melakukan komunikasi satu sama lain. Sangat tidak disarankan untuk membuat channel pada tahap awal desain. Beberapa hal yang perlu diperhatikan agar operasi komunikasi berjalan seimbang diantara task antara lain: Setiap task berkomunikasi hanya dengan grup kecil saja Task dapat melakukan komunikasi secara concurrent Task dapat melakukan komputasi secara concurrent 3. Agglomeration Pada tahap ini, beberapa task dikelompokkan menjadi bagian yang lebih besar. Task dikelompokkan dipilih berdasarkan "kedekatannya" satu sama lain sehingga performa dapat ditingkatkan, memelihara skalabilitas program dan menyederhanakan pemrograman. Pada MPI, biasanya satu agglomerated task dibuat untuk satu prosesor. 4. Mapping Pada proses ini, agglomerated task diproses pada prosesor. Terdapat situasi dimana mapping otomatis dilakukan oleh Operating System (Centralized Multiprocessor), dan pada situasi lainnya mapping harus dilakukan manual (Distributed Memory System). Terdapat tujuan yang saling bertolak belakang ketika melakukan mapping, dimana disatu sisi utilisasi prosesor harus dimaksimumkan sedangkan disisi lain komunikasi interprosesor harus diminimasi. Untuk memperoleh mapping task yang optimal merupakan sebuah kasus yang cukup rumit (NP-Hard Problem). MPI (Message Passing Interface) Paradigma pemrograman Message Passing merupakan salah satu paradigma yang tertua dan telah cukup lama digunakan untuk pendekatan pemrograman parallel. Secara umum ada dua atribut kunci dalam pemrograman message passing (MP). Atribut pertama adalah MPI mengasumsikan address space yang terbagi dan yang kedua MP hanya mendukung parallelisasi yang eksplisit. Pandangan logis dari sebuah mesin yang mendukung paradigma MP terdiri dari p proses, setiap proses memiliki address space-nya sendiri yang eksklusif. Ada dua implikasi penting dari address space terbagi, pertama setiap elemen data harus merupakan bagian dari address space, sehingga data dibagai dan diposisikan secara eksplisit. Hal ini akan menambah kompleksitas program nantinya. Implikasi ke-2 adalah semua interaksi (read-only atau read/write) membutuhkan kerjasama dari dua proses (proses yang memiliki data dan proses yang akan mengakses data tersebut). Kebutuhan akan kerjasama ini juga menambah kompleksitas. Dimana proses yang memiliki data harus terlibat walaupun tidak ada hubungan logis terhadap even pada proses yang memerlukan data.
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
Struktur MP biasanya dibuat dengan paradigma asynchronous atau loosely synchronous. Pada paradigma asynchronous, semua task yang concurrent dieksekusi secara asynchronous. Hal ini memungkinkan implementasi berbagai macam algoritma parallel. Secara umum, paradigma MP mendukung eksekusi program yang berbeda pada setiap 'p' proses. Hampir semua program MP dibuat dengan pendekatan SPMD (Single Program Multiple Data). MPI (Message Passing Interface) mendefinisikan sebuah library standar untuk MPI yang nantinya dapat digunakan untuk membangun program MP diselesaikan. Library MPI terdiri dari 125 rutin, dan rutin standar yang biasa dipakai adalah sebagai berkut: Fungsi
Notasi
Keterangan
MPI_Init
int MPI_Init(int *argc, char ***argv)
Inisialisasi MPI
MPI_Finalize
int MPI_Finalize()
MPI_Comm_size
int MPI_Comm_size(MPI_Comm comm, int *size)
MPI_Comm_rank
int MPI_Comm_rank(MPI_Comm comm, int *rank)
Terminasi MPI Menentukan jumlah proses Menentukan label proses
MPI_Send
int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
Mengirim pesan
MPI_Recv
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
Menerima pesan
int MPI_Barrier
int MPI_Barrier(MPI_Comm comm)
Sinkronisasi operasi
int MPI_Bcast
int MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int source, MPI_Comm comm)
Operasi one-to-all broadcast
int MPI_Reduce
int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int target, MPI_Comm comm)
Operasi one-to-all reduction
int MPI_Gather
int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype senddatatype, void *recvbuf, int recvcount, MPI_Datatype recvdatatype, int target, MPI_Comm comm)
Operasi gather
int MPI_Scatter
int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype senddatatype, void *recvbuf, int recvcount, MPI_Datatype recvdatatype, int source, MPI_Comm comm)
operasi scatter
Bubble Sort Algoritma bubble sort menganalogikan setiap angka sebagai sebuah gelembung. Angka/elemen yang lebih kecil akan naik sama seperti gelembung yang selalu mengambang ke permukaan. Berikut algoritma Bubble Sort sekuensial: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
procedure BUBBLE_SORT(n) begin for i := n-1 to 0 do for j := 0 to i do : k = j+1 if A(j)> A(k) then temp = A(j) A(j) = A(k) A(k) = temp end if end BUBBLE_SORT
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
Dalam setiap loop i algoritma akan "menggelembungkan" bilangan terkecil ke-i hingga ke posisi ke-i didalam array. Penggelembungan dimulai dari elemen terakhir array. Setiap pasang elemen yang berdekatan akan dibandingkan. Jika elemen yang dibelakang lebih kecil, maka dilakukan penukaran. Dengan cara ini, bilangan terkecil pasti akan "naik terbawa" hingga ke posisi yang sebenarnya. Iterasi pada setiap loop terjadi sebanyak (n) kali, sehingga total waktu yang diperlukan adalah (n2).
Gambar 1. Ilustrasi Algoritma Bubble Sort Algoritma sekuensial ini dapat di-parallelkan dengan menerapkan metode Foster. Berikut tahap-tahap desain algoritma parallel Bubble Sort: 1. Partitioning Dekomposisi dilakukan dengan membagi data (domain decomposition). Pembagian data dilakukan oleh prosesor master dengan membagi jumlah data rata kesetiap prosesor sebanyak N/P (N = jumlah data, P = jumlah prosesor). Kemudian setiap prosesor akan melakukan pengurutan data secara sekuensial disetiap prosesor. Berikut proses sekuensial yang terjadi disetiap prosesor: sort (int* v, int n) { int i, j; for (i = n - 2; i >= 0; i--) for (j = 0; j <= i; j++) if (v[j] > v[j + 1]) int t; t = v[i]; v[i] = v[j]; v[j] = t; }
Setelah data diurutkan disetiap prosesor, kemudian prosesor master bertugas untuk menggabungkan hasil tersebut:
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
int* mergeArrays (int* v1, int n1, int* v2, int n2) { int i, j, k; int* result; result = (int *)malloc ((n1 + n2) * sizeof (int)); //alokasi space //untuk hasil array //gabung array dan letakkan dalam urutan ! i = 0; j = 0; k = 0; while (i < n1 && j < n2) if (v1[i] < v2[j]) { result[k] = v1[i]; i++; k++; } else { result[k] = v2[j]; j++; k++; } if (i == n1) while (j < n2) { result[k] = v2[j]; j++; k++; } else while (i < n1) { result[k] = v1[i]; i++; k++; } return result; }
2. Communication Komunikasi terjadi ketika data disebarkan dari prosesor master menuju prosesor slave dan sebaliknya ketika prosesor slave mengirimkan hasil komputasi ke prosesor master. Pengiriman data menuju prosesor slave: MPI_Bcast (&s, 1, MPI_INT, 0, MPI_COMM_WORLD); resultant_array = (int *)malloc (s * sizeof (int)); //Alokasi hasil array //Kirim data array dari Prosesor Utama (Master) ke prosesor slave MPI_Scatter (data, s, MPI_INT, resultant_array, s, MPI_INT, 0, MPI_COMM_WORLD); sort (resultant_array, s); } else { MPI_Bcast (&s, 1, MPI_INT, 0, MPI_COMM_WORLD); //Alokasi hasil array resultant_array = (int *)malloc (s * sizeof (int)); MPI_Scatter (data, s, MPI_INT, resultant_array, s, MPI_INT, 0, MPI_COMM_WORLD); //Setiap slave prosesor akan menyortir berdasarkan pembagian array n/p sort (resultant_array, s); //Sortir array hingga index s. }
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
Pengiriman hasil pengurutan data dari prosesor slave menuju master while (move < p) { if (id%(2*move)==0) { if (id + move < p) { //Terima sub array MPI_Recv (&m, 1, MPI_INT, id + move, 0, MPI_COMM_WORLD, &status); sub = (int *)malloc (m * sizeof (int)); //alokasi space untuk sub array MPI_Recv (sub, m, MPI_INT, id + move, 0, MPI_COMM_WORLD, &status); }
3. Agglomeration Proses agglomerasi terjadi ketika penggabungan 2 sub-array, dan mengurutkannya kembali ke prosesor lain. 4. Mapping Mapping dilakukan ketika data yang telah di-agglomerasi siap dikirim ke prosesor terdekat. int near = id - move; //mencari prosesor terdekat MPI_Send (&s, 1, MPI_INT, near, 0, MPI_COMM_WORLD); MPI_Send (resultant_array, s, MPI_INT, near, 0, MPI_COMM_WORLD); break;
Berikut hasil implementasi serangkaian proses diatas.
Gambar 2. Hasil implementasi Parallel Bubble Sort dengan MPI
Kompleksitas algoritma bubble sort setelah diparallelkan adalah sebagai berikut: Jika p adalah jumlah prosesor, dimana p
n n Tp log n n p p sort lokal
pembandingan
komunikasi
Mengingat kompleksitas pengurutan sekuensial adalah (n log n), maka speedup dan efisiensi formulasi ini adalah (n log n) 1 S , E ((n / p)log(n / p)) (n) 1 ((log p)/(log n)) (p / log n)
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
Kesimpulan Pengurutan/sorting data merupakan operasi yang sangat umum dilakukan oleh komputer karena data yang terurut lebih mudah diproses daripada data yang masih acak. Mem-parallel-kan sebuah algoritma sequencial sorting melibatkan pendistribusian elemen yang akan diurutkan kepada prosesor yang tersedia. Salah satu metode yang umum dipakai dalam mendesain algoritma parallel adalah dengan menerapkan metode Foster. Metode Foster pada dasarnya merupakan serangkaian tahapan mulai dari pembagian data/proses menjadi beberapa bagian (task), menentukan komunikasi antar task, mengumpulkan task yang memiliki komunikasi intens terhadap task yang lainnya, dan terakhir melakukan mapping kelompok task tersebut kedalam beberapa prosesor yang ada. Paradigma pemrograman Message Passing merupakan salah satu paradigma yang tertua dan telah cukup lama digunakan untuk pendekatan pemrograman parallel. Struktur MP biasanya dibuat dengan paradigma asynchronous atau loosely synchronous. Pada paradigma asynchronous, semua task yang concurrent dieksekusi secara asynchronous. Secara umum, paradigma MP mendukung eksekusi program yang berbeda pada setiap 'p' proses. Hampir semua program MP dibuat dengan pendekatan SPMD (Single Program Multiple Data). Algoritma bubble sort menganalogikan setiap angka sebagai sebuah gelembung. Angka/elemen yang lebih kecil akan naik sama seperti gelembung yang selalu mengambang ke permukaan. Waktu yang diperlukan oleh algoritma bubblesort sekuensial adalah (n2) dan untuk algoritma bubblesort parallel menjadi n n Tp log n n . p p
Referensi Grama, A; Gupta, A; Karypis, George; dan Kumar, V. 2003. Introduction to Parallel Computing, Second Edition – Chapter 6 Programming Using the MessagePassing Paradigm. Addison Wesley. USA Quinn, M.J. 2004. Parallel Programming in C with MPI and OpenMP. McGrawHill. USA.
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
Listing kode: ======================================================= /* PARALLEL BUBLE SORT #include <stdio.h> #include <mpi.h> int* mergeArrays (int* v1, int n1, int* v2, int n2) { int i, j, k; int* result; result = (int *)malloc ((n1 + n2) * sizeof (int)); //alokasi space //gabung array dan letakkan dalam urutan ! i = 0; j = 0; k = 0; while (i < n1 && j < n2) if (v1[i] < v2[j]) { result[k] = v1[i]; i++; k++; } else { result[k] = v2[j]; j++; k++; } if (i == n1) while (j < n2) { result[k] = v2[j]; j++; k++; } else while (i < n1) { result[k] = v1[i]; i++; k++; } return result; } sort (int* v, int n) { int i, j; int t; for (i = n - 2; i >= 0; i--) for (j = 0; j <= i; j++) if (v[j] > v[j + 1]) t = v[i]; v[i] = v[j]; v[j] = t; } main (int argc, char ** argv) { FILE* fin; //Input File, (unsorted Array) int* data; //unsorted array int* resultant_array; //Sorted Array int* sub; //Variables Penting int m, n; int id, p; int s; int i; int z; int move; MPI_Status status; MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &id); MPI_Comm_size (MPI_COMM_WORLD, &p);
Metode Foster: Bubble Sort Parallel Wayan Sriyasa/G651080154 Departemen Ilmu Komputer, FMIPA, IPB
//Task untuk Processor Utama if (id == 0) { //Baca data dari file dan prosedur inisialisasi lain. int r; fin = fopen ("data.txt", "r"); //Open file . fscanf (fin, "%d", &n); s = n / p; r = n % p; data = (int *)malloc ((n + s - r) * sizeof (int)); for (i = 0; i < n; i++) fscanf (fin, "%d", &(data[i])); //Baca data dari file. fclose (fin);
//Tutup file.
if (r != 0) { for (i = n; i < n + s - r; i++) data[i] = 0; s = s + 1; } MPI_Bcast (&s, 1, MPI_INT, 0, MPI_COMM_WORLD); resultant_array = (int *)malloc (s * sizeof (int)); //Alokasi hasil array //Kirim data array dari Prosesor Utama (Master) ke prosesor slave MPI_Scatter (data, s, MPI_INT, resultant_array, s, MPI_INT, 0, MPI_COMM_WORLD); sort (resultant_array, s); } else { MPI_Bcast (&s, 1, MPI_INT, 0, MPI_COMM_WORLD); //Alokasi hasil array resultant_array = (int *)malloc (s * sizeof (int)); MPI_Scatter (data, s, MPI_INT, resultant_array, s, MPI_INT, 0, MPI_COMM_WORLD); //Setiap slave prosesor akan menyortir berdasarkan pembagian array n/p sort (resultant_array, s); //Sortir array hingga index s. } move = 1; //Gabung sub sortir array untuk menghasilkan resultan array yg sudh di sortir while (move < p) { if (id%(2*move)==0) { if (id + move < p) { //Terima sub array MPI_Recv (&m, 1, MPI_INT, id + move, 0, MPI_COMM_WORLD, &status); sub = (int *)malloc (m * sizeof (int)); //alokasi space untuk sub array MPI_Recv (sub, m, MPI_INT, id + move, 0, MPI_COMM_WORLD, &status); //menghasilkan resultant array dengan menggabung sub sortir array resultant_array = mergeArrays (resultant_array, s, sub, m); s = s + m; } } else { //Kirim data ke prosesor yang lain int near = id - move; MPI_Send (&s, 1, MPI_INT, near, 0, MPI_COMM_WORLD); MPI_Send (resultant_array, s, MPI_INT, near, 0, MPI_COMM_WORLD); break; } move = move * 2; } //Tahapan Final, Master CPU memberikan hasil.!!! if (id == 0) { //Hasilkan ukuran Array, Processors Yang Digunakan, Total Waktu Kerja printf ("\nUkuran Array: %d \nTotal processor(s): %d \n",s, p); } MPI_Finalize (); //Finalisasi MPI. //Hasilkan Unsorted array dan Yang telah di sortir. printf ("\nArray Sebelum Di Sortir:\n\n"); for (z = 0; z < s; ++z) printf ("%d ", data[z]); printf ("\n\nArray Yang Telah Di Sortir:\n\n"); for (z = 0; z < s; ++z) printf ("%d ", resultant_array[z]); printf ("\n"); }