Memakai Fork/Join Framework Di Groovy


Salah satu masalah yang timbul pada percobaan saya di artikel sebelumnya (Multithreading Di Groovy) adalah walaupun saya membuat beberapa thread berbeda, thread yang sudah menyelesaikan tugasnya terlebih dahulu akan nganggur (berada dalam kondisi wait). Untuk mengatasi permasalahan tersebut, Java 7 telah dilengkapi dengan framework Fork/Join dengan kemampuan work-stealing. Groovy kemudian menyediakan cara mudah memakai Fork/Join Framework melalui GPars.

Sebagai latihan, saya akan mencoba membuat kode program sebelumnya yang memakai framework Executor menjadi menggunakan framework Fork/Join seperti berikut ini:

import groovy.transform.CompileStatic
import javax.swing.JOptionPane
import static groovyx.gpars.GParsPool.runForkJoin
import static groovyx.gpars.GParsPool.withPool

JOptionPane.showMessageDialog(null, 'Attach Terlebih Dahulu Profiler Bila Perlu...')

long waktuMulai = System.nanoTime()

@CompileStatic
boolean isPrimary(long angka) {
    for (int i=2; i<angka-1; i++) {
        if ((angka % i) == 0) return false
    }
    true
}

List hasil

withPool(4) {
    hasil = runForkJoin(2, 100000) { angka1, angka2 ->
        List hasilSementara = []
        if ((angka2 - angka1) > 500) {
            def tengah = (int)((angka2-angka1)/2)
            forkOffChild(angka1, angka1+tengah-1)
            forkOffChild(angka1+tengah, angka2)
            childrenResults.each { hasilSementara.addAll(it) }
        } else {
            for (long angka=angka1; angka<=angka2; angka++) {
                if (isPrimary(angka)) {
                    hasilSementara << angka
                }
            }
        }
        hasilSementara
    }
}

hasil.sort()
long waktuSelesai = System.nanoTime()
println "Waktu Eksekusi: ${waktuSelesai - waktuMulai}"

Pada kode program di atas, saya perlu meletakkan kode program dalam withPool() yang akan memakai framework Fork/Join. Saya juga dapat menentukan jumlah thread yang akan dipakai dengan memberikan parameter angka seperti withPool(4) { ... }. Setelah itu, saya meletakkan kode program yang memakai algoritma divide and conquer pada closure di runForkJoin(). Di dalam closure untuk runForkJoin(), saya dapat memanggil method forkOffChild() dan getChildrenResults(). Method forkOffChild() akan menjadwalkan pemanggilan dan langsung lanjut ke perintah berikutnya (setara dengan fork). Method getChildrenResults() akan menunggu hingga seluruh child selesai di-eksekusi (setara dengan join) dan mengembalikan hasilnya dalam bentuk List. Tanpa @CompileStatic di method isPrimary(), kode program akan berjalan sangat lambat, bahkan lebih lambat dari versi yang hanya memakai thread tunggal.

Bila saya menjalankan kode program di atas dan melihat daftar thread melalui JVisualVM, saya akan memperoleh hasil seperti pada gambar berikut ini:

Tampilan thread yang sibuk semua

Tampilan thread yang sibuk semua

Kali ini terlihat bahwa thread yang ada semuanya sibuk dan tidak ada lagi yang ‘santai’. Walaupun demikian, secara garis besar, kinerjanya tidak lebih baik daripada versi sebelumnya yang memakai framework Executor. Hal ini karena kode program harus melakukan hal baru yang sebelumnya tidak ada seperti menjadwalkan tugas (dengan forkOffChild()) dan mengisi/menggabungkan List.

Setidaknya bila dibandingkan dengan kode program versi Java di artikel Seberapa Jauh Bedanya Program Yang Multithreading?, versi Groovy-nya terasa lebih sederhana dan lebih mudah dipahami. Bukan hanya itu, kode program di atas sebenarnya masih dapat disederhanakan lagi menjadi seperti:

import groovy.transform.CompileStatic
import javax.swing.JOptionPane
import static groovyx.gpars.GParsPool.withPool

JOptionPane.showMessageDialog(null, 'Attach Terlebih Dahulu Profiler Bila Perlu...')

long waktuMulai = System.nanoTime()

@CompileStatic
boolean isPrimary(long angka) {
    for (int i=2; i<angka-1; i++) {
        if ((angka % i) == 0) return false
    }
    true
}

List hasil

withPool(4) {
    hasil = (2..100000).findAllParallel { isPrimary(it) }
    hasil.sort()
}

long waktuSelesai = System.nanoTime()
println "Waktu Eksekusi: ${waktuSelesai - waktuMulai}"

Kode program di atas memakai findAllParallel() yang akan menggunakan framework Fork/Join untuk mengerjakan operasi pada List secara paralel. Pada Groovy, range seperti (2..100000) adalah sebuah List (Range diturunkan dari List).

Bila saya ingin mencetak setiap bilangan prima ke layar, saya dapat menggunakan kode program seperti:

withPool {
    (2..100000).eachParallel {
        if (isPrimary(it)) println it
    }
}

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: