Mengenal Konkurensi dan Paralelisme

Hisma Mulya S
Pujangga Teknologi
Published in
9 min readDec 3, 2018

Tanpa sadar sebenarnya konkurensi dan paralelisme sudah menjadi bagian dari kehidupan kita sebagai pengguna komputer. Ketika sedang browsing, membuat dokumen, bermain game, atau ketika sedang mendengarkan musik di Spotify. Tanpa sadar CPU time telah dibagi bagi oleh sistem operasi supaya dapat mengeksekusi task yang berbeda. Walaupun tidak terlihat oleh kita karena perpindahan task berlangsung dengan sangat cepat.

Tunggu? CPU Time dibagi? Pindah task? Maksudnya?

Ya. CPU dan sistem operasi modern memang didesain untuk dapat mengeksekusi banyak task secara bersamaan. Melakukan satu task kemudian pindah melakukan task lain, mengalokasikan berapa persen untuk aplikasi A, pindah ke aplikasi B, kemudian balik lagi ke A dan seterusnya. Sehingga kita bisa mendengarkan musik di Spotify sembari mengetik di Medium.

Dan itu adalah lingkup antar aplikasi, kita bisa juga melakukan hal yang sama di dalam lingkup aplikasi. Misalnya, ketika kita memutar musik di Spotify, kita bisa melakukan pencarian playlist lain, atau melakukan pencarian lagu bukan?

Iya, keren kan? Itulah yang dinamakan konkurensi.

Nah sebenarnya ada yang lebih keren lagi.

Komputer saat ini serba multi, multikomputer di cloud, multiprocessor, multicore, multiprocess, hingga multithread. Hal ini terjadi karena salah satunya karena clock speed dari sebuah prosesor tidak bisa dibuat lebih kencang lagi.

Lalu bagaimana jika ada task yang akan memakan waktu dan task itu banyak pula? Bagaimana caranya supaya bisa lebih efisien?

Caranya, task-task tersebut mesti dikerjakan secara paralel lalu dikerjakan secara serentak, secara paralel.

Kok mirip mirip ya, Mas? Bedanya di mana?

Nah, dalam kesempatan kali ini, kita akan membahas kedua kasus di atas sebenernya bagaimana dalam contoh yang sederhana.

Konkurensi dan Paralelisme, Mengenal Lebih Dekat

TL;DR Untuk pembaca yang sudah paham, dan tak sabar ingin melihat contoh implementasinya silakan skip ke Implementasi.

Concurrency is about dealing with lots of things at once.

Parallelism is about doing lots of things at once.

— Rob Pike, Golang creator

Istilah konkurensi dan paralelisme itu mirip, namun berbeda, Rob Pike kreator bahasa pemrograman Golang menjelaskan bahwa:

Konkurensi adalah bagaimana caranya menghadapi banyak pekerjaan sekaligus. Konkurensi berkaitan dengan cara dan bagaimana membuat struktur dan cara kerja, sehingga banyak pekerjaan bisa dikerjakan secara bersamaan.

Paralelisme adalah bagaimana melakukan banyak pekerjaan dengan serentak. Dengan kata lain, eksekusi banyak perkerjaan secara bersamaan.

Memang penjelasannya sedikit membingungkan, dan akan lebih jelas jika kita ceritakan dengan sebuah analogi sebagai berikut:

Diceritakan seorang karyawan ingin:

  1. Memperpanjang SIM(Surat Izin Mengemudi) di kantor polisi setempat.
  2. Mengerjakan pekerjaan kantor.

Mengantre perpanjangan SIM di kantor polisi ini membutuhkan waktu yang cukup lama sekitar 4 jam dari mulai mengantre, menghadap beberapa petugas, hingga SIM baru didapat. Sedangkan pekerjaan kantor ini cukup kritikal sehingga mesti diselesaikan hari itu. Sang karyawan ingin agar perpanjangan SIM dan kerjaan di kantornya bisa diselesaikan di hari yang sama. Pertanyaannya bagaimana 2 tugas tersebut bisa dikerjakan?

Sekuensial

Pengerjaan 2 task secara sekuensial

Tentunya 2 tugas tersebut bisa dilakukan dengan mengerjakan salah satu terlebih dahulu dan karena pembuatan SIM sebaiknya pagi, maka pembuatan SIM dilakukan dulu. Perlu 1 jam dari rumah karyawan, proses registrasi dan mengantre selama 4 jam kemudian ke kantor 1 jam, kemudian dilanjutkan selama 5 jam, sehingga total hari itu untuk kedua pekerjaan adalah 11 jam, sehingga karyawan tersebut dengan terpaksa harus bekerja lembur.

Konkuren

Pengerjaan 2 task secara konkuren

Karyawan tersebut tentunya cerdas, dia membawa laptop-nya ke tempat pembuatan SIM, sehingga selama menunggu 4 jam, dia bisa bekerja sehingga, sisa pekerjaan tinggal 1 jam saja. Total waktu 7 jam, karyawan tidak perlu lembur.

Pembuatan SIM tentunya ada beberapa fase yang mengharuskan sang karyawan menghadap ketika dipanggil, nah ketika ini terjadi, sang karyawan akan mengalihkan task ke pembuatan SIM, dan meninggalkan pekerjaan kantornya. Ketika selesai dipanggil, karyawan bisa melanjutkan pekerjaannya kembali.

Paralel

Pengerjaan 2 task secara paralel

Karyawan tersebut cerdas dan memiliki kemampuan interpersonal yang mumpuni, dia berhasil membujuk seseorang sebagai asistennya hari itu, lalu mendelegasikan pekerjaan yang independen bisa dikerjakan oleh sang asisten, kemudian sang karyawan bisa tetap mengantre sembari bekerja. Seselesainya, sang karyawan berkoordinasi dengan asistennya mengenai pekerjaan hari itu, sedikit finalisasi dan karyawan pun tidak perlu lembur.

Kita lihat karena sang karyawan bisa membagi tugas yang bersifat independen dan tidak perlu klarifikasi yang sering, maka kerjaan kantor sang karyawan dapat dieksekusi oleh 2 orang secara independen.

Bila kita asosiasikan karyawan dan asisten sebagai prosesor, membuat SIM dan kerjaan kantor sebagai task, maka kita bisa melihat dengan jelas analogi ini.

Paralelisme Murni

Pengerjaan 2 task secara paralel, tanpa ada switching task

Kita lihat karyawan sembari mengantre masih perlu menghadap petugas SIM sesekali (switching task). Untuk dapat mencapai paralelisme murni, kita musti memastikan bahwa tiap prosesor, dalam hal ini karyawan + asisten melakukan pekerjaan secara independen. Karena ada 3 task berbeda: SIM, kantor oleh karyawan, kantor oleh asisten, dan pihak yang bekerja hanya 2 orang (asisten dan karyawan), maka kita bisa menambahkan pihak satu lagi, misalnya dengan membayar calo atau meminta polisi yang “baik” untuk memroses SIM hingga selesai.

Apa yang mendasari adanya konkurensi dan paralelisme?

Efisiensi Pengerjaan Task

Dengan pengerjaan yang efisien, lebih banyak task yang bisa dikerjakan secara bersamaan.

Lebih Cepat dalam Pengerjaan Banyak Task

Seperti yang sudah kita bahas, kecepatan prosesor sudah tidak bisa lebih cepat lagi, cara supaya banyak task bisa selesai lebih cepat, adalah dengan memanfaatkan lebih banyak sumber daya pemroses dan memrosesnya secara paralel.

Multi User, Multi Task, Di Mana Saja

Untuk memenuhi case yang memenuhi kebutuhan orang di mana saja dan kapan saja, perlu sebuah sistem yang pada dasarnya sangat konkuren dan paralel.

Implementasi

Saya akan mengimplementasikan konkurensi dan paralelisme dengan dua program. Keduanya ditulis dengan bahasa pemrograman Go. Dengan Go membuat sebuah program yang konkuren dan paralel mudah dilakukan. Kita bisa membuat banyak goroutine untuk membagi pekerjaan dan kemudian hasilnya bisa kita terima dengan menggunakan cara komunikasi yang khusus dan aman menggunakan channel.

Konkurensi dan Paralelisme Secara Sederhana

Main program, akan me-run secara concurrent dan menambahkan paralelisme setelahnya.
Program dijalankan secara konkuren

Pada concurrent.go di atas, dengan perintah GOMAXPROCS, di set jumlah logical processor menjadi 1, dengan kata lain, hanya 1 core processor yang akan melakukan task.

Ada 2 task yang di run dalam goroutine, mencetak ‘M’ 100 kali, dan mencetak angka dari 1 hingga 100.

Sedangkan waitgroup berfungsi untuk menunggu hingga kedua goroutine selesai terlebih dahulu sebelum program selesai.

Program dijalankan secara konkuren dan paralel

Pada parallel.go, implementasinya sama dengan concurrent.go, hanya saja jumlah logical processor yang digunakan berjumlah 4.

2 fungsi: mencetak M 100 kali dan mencetak angka dari 1 hingga 100

Hasilnya ketika di jalankan adalah seperti berikut:

⇒  go run main.go
>>> CONCURRENT
Starting goroutines
Waiting to finish..
M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 Termination Program
521.97µs
>>>> PARALLEL
Starting goroutines
Waiting to finish..
M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 M M M M M M M M M M M M M M M M M 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 M M M M M M M 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 M M M M 64 65 66 67 68 69 70 71 M M M 72 73 74 75 76 77 78 79 80 M M M M M M M M M M M M M M 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 M M M M M M M M M M M M M M M M M M M M M M M M Termination Program
1.089517ms

Kita lihat perbedaanya.

  • Pada bagian CONCURRENT, mesikipun printNumbers() dilakukan terlebih dahulu, baru setelahnya printM() ketika di-run, terlihat karakter ‘M’ di-cetak terlebih dahulu baru kemudian karakter angka, ini dikarenakan masing-masing function di run pada gouroutine yang berbeda, dan gouroutine terakhirlah yang mengeksekusi perintah dulu. Karena program di-set untuk berjalan di satu logical processor, satu thread menyelesaikan satu task terlebih dahulu baru yang lainnya.
  • Pada bagian PARALLEL, sama seperti bagian CONCURRENT printNumbers() di-run terlebih dahulu, namun kita lihat ada bagian yang mencetak angka sebelum karakter M selesai dicetak semua. Ini terjadi karena program di-set untuk berjalan di atas maksimum 4 logical processor, sehingga goroutine secara paralel bisa mengeksekusi perintah secara independen tidak menunggu satu logical processor selesai melakukan task.

Dapat kita lihat juga ternyata eksekusi yang dilakukan secara paralel ternyata membutuhkan waktu yang lebih lama.

Konversi Gambar secara Paralel

Pada contoh kasus berikutnya, saya akan menunjukkan proses konversi gambar menjadi beberapa jenis gambar dengan resolusi yang berbeda. Gambar yang dikonversi adalah gambar JPEG dengan resolusi 4480 pixel x 6720 pixel menjadi 3 resolusi: 1280 x 800, 1024 x 768, dan 800 x 600.

Untuk mengonversi masing-masing gambar bisa dilakukan secara paralel karena masing-masing adalah sebuah task yang independen. Kali ini, kita akan melakukan perbandingannya antara yang dilakukan secara sekuensial, secara konkuren dengan 1 logical processor, dan paralel dengan menambah 4 logical processor.

Library liliput digunakan untuk melakukan konversi ini.

main.go — Main Program
Resize image secara sekuensial
Resize image secara paralel

Kemudian di main directory kita lakukan kompilasi:

go build

Konversi Gambar: Sekuensial

Jalankan di main directory:

./main --input="big.jpg"

Hasilnya:

Input file name:  big.jpg
Sizes: [{1280 800} {1024 768} {800 600}]
Maxprocs: -1
file type: JPEG
4480px x 6720px
image written to out800x1280.jpg
Image resized out800x1280.jpg
file type: JPEG
4480px x 6720px
image written to out768x1024.jpg
Image resized out768x1024.jpg
file type: JPEG
4480px x 6720px
image written to out600x800.jpg
Image resized out600x800.jpg
ResizeSequential time: 1.172260208s

Konversi Gambar: Sekuensial dengan 1 Logical Prosesor

Jika kita run dengan 1 logical processor dengan menggunalan goroutine:

./main --input="big.jpg" --maxprocs=1

Hasilnya:

./main --input="big.jpg" --maxprocs=1
Input file name: big.jpg
Sizes: [{1280 800} {1024 768} {800 600}]
Maxprocs: 1
file type: JPEG
4480px x 6720px
file type: JPEG
4480px x 6720px
file type: JPEG
4480px x 6720px
image written to out768x1024.jpg
Image resized out768x1024.jpg
image written to out800x1280.jpg
Image resized out800x1280.jpg
image written to out600x800.jpg
Image resized out600x800.jpg
ResizeParallel time: 1.710344498s

Konversi Gambar: Paralel dengan Maksimum 4 logical processor

./main --input="big.jpg" --maxprocs=4

Hasilnya:

./main --input="big.jpg" --maxprocs=4
Input file name: big.jpg
Sizes: [{1280 800} {1024 768} {800 600}]
Maxprocs: 4
file type: JPEG
4480px x 6720px
file type: JPEG
4480px x 6720px
file type: JPEG
4480px x 6720px
image written to out600x800.jpg
Image resized out600x800.jpg
image written to out768x1024.jpg
Image resized out768x1024.jpg
image written to out800x1280.jpg
Image resized out800x1280.jpg
ResizeParallel time: 375.379731ms

~3 kali lebih cepat

Konklusi

  • Konkurensi berbeda dengan paralelisme. Konkurensi adalah tentang bagaimana cara dan struktur pengerjaan, supaya banyak task bisa dilakukan sekaligus. Paralelisme adalah tentang eksekusi banyak task secara bersamaan.
  • Eksekusi pemrosesan task secara paralel dengan menggunakan lebih dari satu logical prosesor bisa mempersingkat waktu pemrosesan. Namun implementasi yang salah bisa justru memperlambat. Tidak semua kasus bisa diselesaikan dengan melakukan eksekusi secara paralel.
  • Tidak semua task cocok dieksekusi secara paralel, kuncinya perlu independensi antar task, antara satu task dan lainnya tidak ada switching.

Sumber

P.S. Jika teman-teman menyukai artikel semacam ini, silakan subscribe ke newsletter kita dan dapatkan notifikasi artikel terbaru langsung di inbox kamu!

--

--