Back to IF3130 Sistem Paralel dan Terdistribusi
UTS 2013/2014
1. Instruction-Level Parallelism (ILP)
Jelaskan apa yang dimaksud dengan ILP, dan berikan contohnya.
-
Jawaban:
Instruction-Level Parallelism (ILP) adalah kemampuan prosesor untuk mengeksekusi beberapa instruksi dari satu stream program secara bersamaan dalam satu clock cycle. Tujuannya adalah untuk meningkatkan throughput instruksi tanpa harus mengubah kode program secara eksplisit oleh programmer. Teknik utama untuk mencapai ILP adalah pipelining dan arsitektur superscalar (multiple-issue).
-
Contoh:
Misalkan kita memiliki potongan kode berikut:
a = b + c; // Instruksi 1 x = y * z; // Instruksi 2Instruksi 1 dan Instruksi 2 tidak memiliki dependensi data (hasil dari satu instruksi tidak digunakan oleh yang lain). Sebuah prosesor superscalar dengan dua unit eksekusi (misalnya, satu untuk penjumlahan dan satu untuk perkalian) dapat mengeksekusi kedua instruksi ini secara paralel dalam clock cycle yang sama, sehingga menyelesaikan pekerjaan lebih cepat daripada prosesor skalar yang harus mengeksekusinya secara berurutan.
-
2. Jumlah Thread vs. Jumlah Core
Pada sebuah sistem multicore, jelaskanlah apakah ada manfaatnya membuat program paralel dengan menggunakan jumlah thread yang lebih besar dibandingkan jumlah core fisik yang ada.
-
Jawaban:
Ya, ada manfaatnya, terutama untuk menyembunyikan latensi (latency hiding). Manfaat utamanya adalah:
-
Menangani Operasi I/O atau Blocking: Jika sebuah thread melakukan operasi yang memblokir, seperti membaca data dari disk, menunggu data dari jaringan, atau menunggu input pengguna, core yang menjalankan thread tersebut akan menganggur. Jika ada lebih banyak thread daripada core, sistem operasi dapat melakukan context switch dan menjalankan thread lain yang sudah siap di-core tersebut. Ini membuat core tetap sibuk dan produktif, sehingga meningkatkan utilisasi sistem secara keseluruhan.
-
Meningkatkan Responsivitas: Pada aplikasi dengan antarmuka pengguna (GUI), satu thread dapat didedikasikan untuk menangani interaksi pengguna (menjaga agar UI tetap responsif), sementara thread-thread lain melakukan pekerjaan komputasi di latar belakang.
Namun, perlu diingat bahwa membuat terlalu banyak thread juga memiliki kekurangan, yaitu peningkatan overhead dari context switching yang sering terjadi, yang justru dapat menurunkan performa.
-
3. Hukum Amdahl
Jika sebuah algoritma sekuensial diubah menjadi paralel, hanya 80% bagian algoritma tersebut yang dapat diparalelkan. Jelaskan berapa maksimum speedup yang dapat dicapai dengan menggunakan jumlah prosesor tak terbatas.
-
Jawaban:
Ini adalah aplikasi langsung dari Hukum Amdahl.
-
Bagian paralel (p) = 80% = 0.8
-
Bagian serial (s) = 1 - p = 20% = 0.2
Speedup maksimal (ketika jumlah prosesor mendekati tak terhingga) dibatasi oleh bagian serial dari kode. Rumusnya adalah:
Jadi, maksimum speedup yang dapat dicapai adalah 5 kali, tidak peduli seberapa banyak prosesor yang ditambahkan.
-
4. Analisis Kode MPI
Diberikan kode MPI berikut. Jelaskan dengan singkat apa yang dihitung pada kode tersebut.
int main(int argc, char *argv[]) {
int rank, received, myvalue, p, neighbor;
MPI_Init(&argc, &argv);
MPI_Status status;
MPI_Comm_size(MPI_COMM_WORLD, &p);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
myvalue = rank;
for(int step = 1; step < p; step = step*2) {
if( rank % step == 0) {
if( isEven(rank/step)) { // isEven(x) mengembalikan 1 jika x even, // dan 0 jika x odd
MPI_Recv(&received, 1, MPI_INT, rank+step, 0, MPI_COMM_WORLD, &status);
myvalue += received;
} else {
MPI_Send(&myvalue, 1, MPI_INT, rank-step, 0, MPI_COMM_WORLD); }
} else { break;}
} if(rank == 0) {
printf("%d\n", myvalue);
} return 0;
}-
Jawaban:
Kode tersebut menghitung penjumlahan total dari rank semua proses (yaitu, 0+1+2+…+(p−1)) menggunakan algoritma reduksi paralel dengan pola komunikasi recursive doubling atau butterfly.
-
Cara Kerja:
-
Setiap proses diinisialisasi dengan nilainya sendiri (
myvalue = rank). -
Dalam setiap langkah (
step), jarak komunikasi antar proses digandakan. -
Proses-proses dibagi menjadi pasangan pengirim dan penerima. Proses dengan
rankgenap dalam grupnya (isEven(rank/step)) akan menerima nilai dari tetangganya dan menambahkannya ke nilainya sendiri. Proses denganrankganjil akan mengirim nilainya dan kemudian berhenti berpartisipasi. -
Proses ini berlanjut, dengan semakin sedikit proses yang aktif di setiap langkah, hingga semua nilai terakumulasi di proses
rank 0. -
Akhirnya,
rank 0akan mencetak hasil penjumlahan total.
-
-
UTS 2015/2016
1. Multiple Issue Processors dan Pipelining
Bagaimana hubungan antara multiple issue processors dengan pipelining. Berikan ilustrasi, misalkan jumlah issues=4 dengan 5 tahapan pipeline IF, ID, EX, MEM, WB. Hitunglah CPI (cycle per instruction) nya.
-
Jawaban:
-
Hubungan: Pipelining adalah teknik yang memungkinkan sebuah prosesor untuk mengerjakan beberapa tahap dari instruksi yang berbeda secara bersamaan. Multiple issue processor (arsitektur superscalar) membawa konsep ini lebih jauh dengan memiliki beberapa pipeline (atau satu pipeline yang lebih lebar) untuk memulai lebih dari satu instruksi dalam satu clock cycle. Jadi, multiple issue adalah bentuk paralelisasi dari pipelining.
-
Ilustrasi (4-issue, 5-stage pipeline):
Bayangkan sebuah “pabrik” dengan 4 jalur perakitan paralel. Setiap jalur memiliki 5 stasiun kerja (IF, ID, EX, MEM, WB).
Clock Cycle Jalur 1 Jalur 2 Jalur 3 Jalur 4 1 IF (Instr 1) IF (Instr 2) IF (Instr 3) IF (Instr 4) 2 ID (Instr 1) ID (Instr 2) ID (Instr 3) ID (Instr 4) 3 EX (Instr 1) EX (Instr 2) EX (Instr 3) EX (Instr 4) 4 MEM (Instr 1) MEM (Instr 2) MEM (Instr 3) MEM (Instr 4) 5 WB (Instr 1) WB (Instr 2) WB (Instr 3) WB (Instr 4) 6 (Instr 1-4 selesai) -
Perhitungan CPI (Cycle Per Instruction):
Dalam kondisi ideal (tanpa hazard atau dependensi), prosesor 4-issue dapat menyelesaikan 4 instruksi per clock cycle.
CPI=Jumlah InstruksiJumlah Cycle=4 instruksi1 cycle=0.25
CPI idealnya adalah 0.25.
-
2. Perbedaan MPI_Bcast dan MPI_Scatter
Jelaskan perbedaan antara perintah MPI_Bcast dan MPI_Scatter, berikan ilustrasi aliran data dari sender ke dest untuk kedua perintah tersebut.
-
Jawaban:
-
MPI_Bcast(Broadcast): Operasi “one-to-all”. Satu proses root mengirimkan salinan data yang identik ke semua proses lain (termasuk dirinya sendiri) dalam komunikator. -
MPI_Scatter(Scatter): Operasi “one-to-many”. Satu proses root memiliki sebuah array data, lalu membagi array tersebut menjadi potongan-potongan dan mengirimkan satu potongan unik ke setiap proses. -
Ilustrasi:
Misalkan
root=0dan ada 4 proses.-
MPI_Bcast:-
Sebelum:
P0punya data[A].P1, P2, P3kosong. -
Sesudah:
P0punya[A],P1punya[A],P2punya[A],P3punya[A].
-
-
MPI_Scatter:-
Sebelum:
P0punya data[A, B, C, D].P1, P2, P3kosong. -
Sesudah:
P0punya[A],P1punya[B],P2punya[C],P3punya[D].
-
-
-
3. Analisis Program MPI
Diberikan bagian kode program MPI berikut. Jelaskan apa yang dilakukan oleh program tersebut secara lengkap sampai program selesai. Apa yang dilakukan oleh setiap proses, misalkan ada 5 proses yang di spawn.
next = (rank + 1) % size;
prev = (rank + size - 1) % size;
if (0 == rank) {
message = 10;
MPI_Send(&message, 1, MPI_INT, next, tag, MPI_COMM_WORLD);
} while (1) {
MPI_Recv(&message, 1, MPI_INT, prev, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
if (0 == rank) {--message;}
MPI_Send(&message, 1, MPI_INT, next, tag, MPI_COMM_WORLD);
if (0 == message) { break; }
}
if (0 == rank) {
MPI_Recv(&message, 1, MPI_INT, prev, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}-
Jawaban:
Program ini mengimplementasikan algoritma token passing dalam topologi ring (cincin), di mana sebuah token (integer) beredar dan nilainya dimodifikasi hingga mencapai kondisi berhenti.
-
Alur Eksekusi dengan 5 Proses (P0, P1, P2, P3, P4):
-
Inisialisasi: Setiap proses menentukan tetangga kanan (
next) dan kiri (prev). Misalnya, untuk P1,nextadalah P2 danprevadalah P0. Untuk P4,nextadalah P0. -
Awal: P0 mengirimkan token dengan nilai
10ke P1. -
Sirkulasi Putaran 1-9:
-
P1 menerima token
10dari P0, lalu mengirimkannya ke P2. -
P2 menerima
10dari P1, mengirimkannya ke P3. -
P3 menerima
10dari P2, mengirimkannya ke P4. -
P4 menerima
10dari P3, mengirimkannya ke P0. -
P0 menerima
10dari P4. Karenarankadalah 0, ia mengurangi nilai token menjadi9, lalu mengirimkannya ke P1. -
Proses ini berulang. Token beredar dengan nilai yang sama hingga kembali ke P0, di mana nilainya dikurangi satu.
-
-
Sirkulasi Terakhir:
- P0 akhirnya menerima token bernilai
1dari P4. Ia menguranginya menjadi0dan mengirimkannya ke P1. Setelah itu, P0 keluar dariwhileloop.
- P0 akhirnya menerima token bernilai
-
Pemberhentian Berantai:
-
P1 menerima token
0dari P0, mengirimkannya ke P2, lalu keluar dari loop. -
P2 menerima
0dari P1, mengirimkannya ke P3, lalu keluar dari loop. -
…dan seterusnya hingga P4 menerima
0dan mengirimkannya kembali ke P0.
-
-
Finalisasi: P0, yang sudah berada di luar loop, memiliki satu
MPI_Recvterakhir untuk menerima token0yang sudah beredar penuh. Ini mencegah deadlock. Setelah itu, semua proses mencapaiMPI_Finalizedan program berakhir.
-
-
4. OpenMP - Pencarian Nilai Maksimum
Jelaskan apa yang kurang tepat pada kode berikut ini dan perbaikilah:
#pragma omp parallel for
for ( i = 0; i < N; ++i ) {
#pragma omp critical
{ if (arr[i] > max) max = arr[i]; }
}-
Jawaban:
-
Yang Kurang Tepat: Penggunaan
#pragma omp criticaluntuk melindungi operasi sederhana seperti perbandingan dan penetapan nilai maksimum sangat tidak efisien. Ini menciptakan bottleneck karena hanya satu thread yang dapat memasuki critical section pada satu waktu, yang pada dasarnya membuat bagian terpenting dari loop menjadi sekuensial dan menghilangkan manfaat paralelisasi. -
Perbaikan: Cara yang benar dan jauh lebih efisien untuk melakukan operasi reduksi seperti ini adalah dengan menggunakan clause
reductiondari OpenMP. Ini memungkinkan setiap thread untuk memiliki salinan privat dari variabelmax, mengerjakannya secara paralel, dan kemudian OpenMP akan secara otomatis menggabungkan semua hasil privat tersebut menjadi satu hasil global akhir. -
Kode yang Diperbaiki:
// Inisialisasi max dengan nilai yang sangat kecil max = SOME_VERY_SMALL_VALUE; #pragma omp parallel for reduction(max:max) for ( i = 0; i < N; ++i ) { if (arr[i] > max) { max = arr[i]; } }
-
5. CUDA - Bucket Sort
a. Jelaskan struktur data yang anda gunakan untuk implementasi bucket sort pada CUDA.
b. Jelaskan strategi (berupa algoritma dan penjelasannya) pembagian data ke dalam masing-masing bucket dengan menggunakan threads pada kernel CUDA.
-
Jawaban:
a. Struktur Data:
Untuk implementasi bucket sort paralel yang efisien di CUDA, kita memerlukan beberapa array di memori device (GPU):
-
d_input_data: Array 1D yang berisi data mentah yang akan diurutkan. -
d_buckets: Array 1D besar yang akan menyimpan data setelah dibagi ke bucket. Ukurannya sama dengand_input_data. -
d_bucket_offsets: Array 1D yang ukurannya sama dengan jumlah bucket. Array ini akan digunakan untuk menyimpan histogram (jumlah elemen per bucket) dan kemudian hasil dari prefix sum (indeks awal setiap bucket di dalamd_buckets).
b. Strategi Pembagian Data (Algoritma):
Strategi yang umum digunakan adalah pendekatan tiga kernel untuk menghindari race condition dan memaksimalkan paralelisme:
-
Kernel 1: Membuat Histogram (Menghitung Ukuran Bucket)
-
Tujuan: Menghitung berapa banyak elemen data yang masuk ke setiap bucket.
-
Algoritma:
-
Input dibagi di antara semua thread. Setiap thread membaca satu atau lebih elemen dari
d_input_data. -
Untuk setiap elemen, thread menentukan nomor bucket-nya (misalnya,
bucket_id = floor(value / bucket_range)). -
Thread menggunakan operasi atomik (
atomicAdd) untuk menambah counter padad_bucket_offsets[bucket_id]. -
Setelah kernel ini selesai,
d_bucket_offsetsberisi jumlah elemen untuk setiap bucket.
-
-
-
Kernel 2: Menghitung Posisi Awal Bucket (Prefix Sum/Scan)
-
Tujuan: Mengubah histogram menjadi alamat awal untuk setiap bucket di dalam array keluaran.
-
Algoritma:
-
Lakukan operasi parallel prefix sum (exclusive scan) pada array
d_bucket_offsets. Algoritma scan yang efisien (seperti Blelloch) digunakan di sini. -
Setelah kernel ini selesai,
d_bucket_offsets[i]akan berisi total jumlah elemen dari bucket 0 hinggai-1, yang merupakan indeks awal untuk bucketi.
-
-
-
Kernel 3: Menyebar Data ke Bucket (Scatter)
-
Tujuan: Menempatkan setiap elemen data ke posisi yang benar di dalam array
d_buckets. -
Algoritma:
-
Luncurkan kernel di mana setiap thread kembali membaca satu elemen dari
d_input_data. -
Thread menentukan nomor bucket dari elemen tersebut (
bucket_id). -
Thread mendapatkan posisi awal bucket dari
d_bucket_offsets[bucket_id]. -
Untuk mendapatkan posisi yang tepat di dalam bucket, thread secara atomik menaikkan nilai di
d_bucket_offsets[bucket_id]dan menggunakan nilai lama sebagai offset (atomicAddmengembalikan nilai sebelum ditambah). -
Posisi akhir =
posisi_awal_bucket+offset_lokal. -
Thread menulis elemen datanya ke
d_buckets[posisi_akhir].
-
-
Setelah ketiga kernel ini, data akan terdistribusi di dalam
d_bucketsdi mana semua elemen dari bucket 0 berada di depan, diikuti oleh semua elemen dari bucket 1, dan seterusnya. -