Upload
fariz-maulana
View
226
Download
56
Embed Size (px)
DESCRIPTION
Compare matrix multiplication performance between serial and parallel programming. The parallel programming implemented using Message Passing Interface.
Citation preview
Matrix Multiplication using MPI
Fariz Maulana
Department of Electrical Engineering
Institut Teknologi Bandung
Bandung, Indonesia
Abstrak—Pada tugas ini akan dibuat program perkalian
antara matriks dengan vektor dengan ukuran matriks yang
cukup besar yaitu 25x25, 100x100, dan 1000x1000 dan ukuran
vektor yang berkesesuaian. Program dibuat dalam platform
komputas paralel dengan menggunakan MPI. Pemrograman
parallel diimplementasikan dengan mengginakan point to point
communication. Dari program yang dibuat akan dilakukan
perbandingan performa dari ketiga operasi perkalian matriks
dengan vektor tersebut misal dari waktu yang diperlukan untuk
melakukan komputasi.
Kata kunci : Komputasi paralel, MPI, point to point
communication.
I. PENDAHULUAN
Message Passing Interface (MPI) ada suatu standar
sistem penerusan pesan (message-passing) yang digunakan
pada pemrograman paralel atau komputasi terdistribusi.
Standar tersebut mendefinisikan sintaks dan semantik dari inti
rutin pustaka (library) yang berguna untuk berbagai kalangan
pengguna yang menulis program message-passing di bahasa
pemrograman komputer yang berbeda-beda seperti Fortran, C,
C++ dan Java. MPI bukanlah suatu bahasa pemrograman
namun sebuah pustaka yang digunakan untuk membuat suatu
program dipecah menjadi beberapa proses dan
mengomunikasikan antar masing-masing proses menggunakan
protokol yang efisien. Ada beberapa contoh MPI yang umum
digunakan antara lain, MPICH, MVAPICH, OpenMPI,
Microsoft MPI, dll.
MPI bertugas untuk mengirim data antar komputer di
dalam sistem paralel (biasa disebut sebagai node atau host).
Dalam MPI terdapat terminologi job scheduler yang berfungsi
menerima tugas dari user dan menjadwalkannya pada
beberapa node di dalam sistem paralel.
Pemrograman paralel pada program perkalian matriks ini
akan diimplementasikan dengan menggunakan point to point
communication pada MPI. Sebagai ilustrasi, perhatikan
matriks 1 berukuran 5x4 dan matriks 2 yang berukuran 4x3.
Perkalian dari kedua buah matriks ini akan menghasilkan
matriks yang berukuran 5x3. Misalkan kita menginginkan
perhitungan perkalian matriks ini dilakukan oleh 3 buah
proses yang berbeda, maka perhitungan akan dilakukan seperti
diilustrasikan pada gambar di bawah. Kita dapat membagi
pekerjaan tersebut dengan menggunakan perulangan for(i =
rank; i < X; i = i+size).
Gambar 1. Ilustrasi perhitungan matriks secara paralel
Program dibuat dengan menggunakan pustaka MPICH.
MPICH dipilih karena memiliki performa yang sangat baik
dan paling umum digunakan untuk implementasi MPI yang
dibuktikan oleh 9 dari 10 super komputer tercepat
menggunakan MPICH (Juni 2015) termasuk super komputer
tercepat saat ini yaitu Tianhe-2.
II. HASIL DAN ANALISIS
Ada dua buah program utama yang dibuat yaitu program
perkalian matriks secara paralel dan sekuensial. Untuk source
code dari kedua buah program tersebut dapat dilihat pada
bagian lampiran.
Program perkalian matriks untuk matriks yang berukuran
25x25, 100x100, dan 1000x1000 menggunakan source code
yang sama. Perbedaannya terletak pada nilai konstanta N, X,
dan Y.
Perhatikan potongan source code di bawah ini …
MPI_Init(&argc, &argv);
…
MPI_Finalize();
Perintah di atas digunakan untuk melakukan pembuatan
penghancuran proses MPI. Fungsi MPI_Init(&argc, &argv)
berfungsi untuk menginisialisasi lingkungan eksekusi MPI
sedangkan fungsi MPI_Finalize() akan mengakhiri lingkungan
eksekusi MPI. Jika kita lupa tidak menjalankan rutin ini maka
akan dimungkinkan ada proses-proses yang tersisa di memori
(orphans process). …
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPIC_Comm_rank(MPI_COMM_WORLD, &rank);
…
Fungsi MPI_Comm_size(MPI_COMM_WORLD, &size)
akan menentukan jumlah proses. Variabel rank pada fungsi
MPIC_Comm_rank(MPI_COMM_WORLD, &rank) digunakan
sebagai process identifier.
Untuk membuat matriks digunakan prosedur
create_matrix(). Prosedur ini berfungsi untuk membuat
matriks dinamis dan melakukan alokasi memori serta
inisialisasi nilai-nilai elemen masing-masing matriks dengan
angka acak (menggunakan fungsi rand() % 8).
Kompilasi dilakukan di lingkungan Windows dengan
menggunakan gcc. Perintah yang dipakai untuk melakukan
kompilasi adalah
gcc –I “c:\Program Files\MPICH2\include” –o
matrix_mul_mpi matrix_mul_mpi.c –L “c:\Program
Files\MPICH2\lib” –lmpi
Setelah dilakukan kompilasi dan dihasilkan file executable,
program dieksekusi dengan menjalankan perintah berikut pada
jendela terminal
mpiexec –n x ./matrix_mul_mpi
Parameter –n x menunjukkan bahwa program akan dijalankan
dengan menggunakan x buah node (proses) secara bersamaan.
Berikut ini adalah tangkapan layar dari beberapa hasil
eksekusi program perhitungan matriks yang menggunakan
pemrograman sekuensial dan pemrograman paralel dengan
jumlah proses yang berbeda-beda.
Gambar 2. Hasil perhitungan paralel matrix 25x25 dengan
n = 2
Gambar 3. Hasil perhitungan paralel matrix 25x25 dengan
n = 4
Gambar 4. Hasil perhitungan parallel matrix 1000x1000
dengan n = 2
Gambar 5. Hasil perhitungan paralel matrix 1000x1000
dengan n = 4
Gambar 6. Hasil perhitungan sekuensial matrix 25x25
n = 1 n = 2 n = 3 n = 4
25 x 25 0.000007 0.00044 0.00159 0.000077
100 x 100 0.000062 0.00023 0.000357 0.000379
1000 x 1000 0.005579 0.005533 0.006058 0.004469
Waktu Eksekusi (s)Ukuran Matriks
Ukuran Matriks Waktu Eksekusi (s)
25 x 25 0
100 x 100 0
1000 x 1000 0.015626
Gambar 7. Hasil perhitungan sekuensial matrix 1000x1000
Hasil eksekusi program menampilkan informasi mengenai
waktu yang dibutuhkan untuk melakukan komputasi dan
matriks hasil perkalian. Hasil tersebut disajikan dalam bentuk
tabel di bawah ini
Tabel 1. Waktu Eksekusi Pemrograman Paralel
Tabel 2. Waktu Eksekusi Pemrograman Sekuensial
Apabila kita menjalankan program MPI pada komputer
yang memiliki 2 inti (cores) atau lebih memungkinkan untuk
dihasilkannya performa yang lebih baik dibandingkan
pemrograman sekuensial. Dari tabel di atas, terlihat bahwa
pada perhitungan matriks yang memiliki 25x25 dan 100x100
(ukurannya relative kecil) waktu eksekusi lebih cepat
dibandingkan pemrograman paralel. Namun untuk matriks
yang berukuran cukup besar (1000x1000) terlihat bahwa
waktu eksekusi pemrograman paralel lebih cepat bila
dibandingkan pemrograman sekuensial. Hal ini disebabkan
pada pemrograman paralel dibutuhkan waktu untuk
pembuatan proses yang lebih banyak dan transmisi pesan antar
proses membutuhkan waktu juga.
Ukuran matriks memberikan dampak pada waktu eksekusi
program. Dari data di atas terlihat bahwa, semakin besar
ukuran matriks maka waktu eksekusi akan menjadi semakin
besar. Hal ini disebabkan karena dengan ukuran matriks yang
besar maka dibutuhkan jumlah komputasi yang lebih banyak
dibandingkan matriks yang berukuran kecil.
Dalam segi pemakaian memori, penggunaan MPI akan
membutuhkan memori yang lebih besar bila dibandingkan
pemrograman sekuensial biasa. Hal ini disebabkan karena
program yang menggunakan MPI dijalankan dengan banyak
proses yang berjalan bersama sehingga penggunaan
memoripun akan semakin meningkat. Selain itu semakin besar
ukuran matriks juga berkontribusi pada semakin besarnya
memori yang dibutuhkan ketika eksekusi program. Matriks
dengan ukuran besar membutuhkan variabel yang lebih
banyak dan juga jumlah stack yang lebih banyak bila
dibandingkan matriks berukuran kecil.
PUSTAKA
[1] https://en.wikipedia.org/wiki/Message_Passing_Interface
[2] http://www.just4tech.com/2013/10/matrix-multiplication-in-mpi.html
[3] http://mpitutorial.com/tutorials/mpi-hello-world/
[4] http://galihguawel.blogspot.co.id/2014/10/cara-install-mpi-compiler-di-windows.html
LAMPIRAN
Paralel matrix multiplication program /* File : matrix_mul_mpi.c
* Program untuk melakukan perhitungan perkalian matriks secara paralel dengan menggunakan
library MPI
*/
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#define N 1000
#define X 1000
#define Y 1
void create_matrix(void);
int i, j, k;
int **matrix_1; // matrix_1[X][N]
int **matrix_2; // matrix_2[N][Y]
int **matrix_result; // matrix_result[X][Y]
int main(int argc , char **argv) {
double start_time, end_time, total_time;
int size, rank;
int sum = 0;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
create_matrix();
MPI_Barrier(MPI_COMM_WORLD);
if (rank == 0) {
// Start measuring time
start_time = MPI_Wtime();
}
// Divide the task in multiple processes
for (i = rank;i < X;i = i+size) {
for (j = 0;j < Y;j++) {
sum = 0;
for (k = 0;k < N;k++) {
sum = sum + matrix_1[i][k] * matrix_2[k][j];
}
matrix_result[i][j] = sum;
}
}
if (rank != 0) {
for (i = rank;i < X;i = i+size) {
// Send calculated rows to process with rank 0
MPI_Send(&matrix_result[i][0], Y, MPI_INT, 0, 10+i, MPI_COMM_WORLD);
}
}
if (rank == 0) {
for (j = 1;j< size;j++) {
for (i = j;i < X;i = i+size) {
// Receive calculated rows from respective process
MPI_Recv(&matrix_result[i][0], Y, MPI_INT, j, 10+i, MPI_COMM_WORLD, &status);
}
}
}
MPI_Barrier(MPI_COMM_WORLD);
if (rank == 0) {
// Stop measuring time
end_time = MPI_Wtime();
}
total_time = end_time - start_time;
if (rank == 0) {
for (i = 0;i < X;i++) {
for (j = 0;j < Y;j++) {
// Print the result
printf("%d\t", matrix_result[i][j]);
}
printf("\n");
}
}
if (rank == 0) {
// Total time taken for performing matrix multiplication
printf("Total time taken by CPU is = %f second\n", total_time);
}
MPI_Finalize();
return 0;
}
void create_matrix(void) {
// Create array of pointers (Rows)
matrix_1 = (int **)malloc(X * sizeof(int*));
matrix_2 = (int **)malloc(N * sizeof(int*));
matrix_result = (int **)malloc(X * sizeof(int*));
// Allocate memory for each Row pointer
for (i = 0;i < X;i++) {
matrix_1[i] = (int *)malloc(N * sizeof(int));
matrix_result[i] = (int *)malloc(Y * sizeof(int));
}
for (i = 0;i < N;i++) {
matrix_2[i]=(int *)malloc(Y * sizeof(int));
}
for (i = 0; i < X; i++) {
for (j = 0; j < N; j++) {
// initialize random number to matrix1 for all processes
matrix_1[i][j] = rand() % 8;
}
}
for (i = 0; i < N; i++) {
for (j = 0;j < Y;j++) {
// initialize random number to matrix2 for all processes
matrix_2[i][j] = rand() % 8;
}
}
}
Sequential matrix multiplication /* File : matrix_mul_seq.c
* Program untuk melakukan perhitungan perkalian matriks secara sekuensial
*/
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#define N 1000
#define X 100
#define Y 1
void create_matrix(void);
int i, j, k;
int **matrix_1; // matrix_1[X][N]
int **matrix_2; // matrix_2[N][Y]
int **matrix_result; // matrix_result[X][Y]
int main() {
double start_time, end_time, total_time;
int sum = 0;
struct timeval time;
create_matrix();
gettimeofday(&time, NULL);
start_time = time.tv_sec + (time.tv_usec/1000000.0);
for (i = 0;i < X;i++) {
for (j = 0;j < Y;j++) {
sum = 0;
for (k = 0;k < N;k++) {
sum = sum + matrix_1[i][k] * matrix_2[k][j];
}
matrix_result[i][j] = sum;
}
}
gettimeofday(&time, NULL);
end_time = time.tv_sec + (time.tv_usec/1000000.0);
total_time = end_time - start_time;
for (i = 0;i < X;i++) {
for (j = 0;j < Y;j++) {
printf("%d\t", matrix_result[i][j]);
}
printf("\n");
}
printf("Total time taken by CPU is = %f second\n", total_time);
return 0;
}
void create_matrix(void) {
// Create array of pointers(Rows)
matrix_1 = (int **)malloc(X*sizeof(int*));
matrix_2 = (int **)malloc(N*sizeof(int*));
matrix_result = (int **)malloc(X*sizeof(int*));
// Allocate memory for each Row pointer
for (i = 0;i < X;i++) {
matrix_1[i] = (int *)malloc(N * sizeof(int));
matrix_result[i] = (int *)malloc(Y * sizeof(int));
}
for (i = 0;i < N;i++) {
matrix_2[i] = (int *)malloc(Y * sizeof(int));
}
for (i = 0;i < X;i++) {
for (j = 0;j < N;j++) {
// Initialize random number to matrix_1 for all processes
matrix_1[i][j] = rand() % 8;
}
}
for (i = 0;i < N;i++) {
for (j = 0;j < Y;j++) {
// Initialize random number to matrix_2 for all processes
matrix_2[i][j] = rand() % 8;
}
}
}