Saya terjun ke dunia pemrograman fungsional dan saya terus membaca di mana-mana bahwa bahasa fungsional lebih baik untuk program multithreading / multicore. Saya mengerti bagaimana bahasa fungsional melakukan banyak hal secara berbeda, seperti rekursi , angka acak , dll. Tetapi saya tidak dapat menemukan apakah multithreading lebih cepat dalam bahasa fungsional karena dikompilasi secara berbeda atau karena saya menulisnya secara berbeda.
Sebagai contoh, saya telah menulis sebuah program di Jawa yang mengimplementasikan protokol tertentu. Dalam protokol ini, kedua pihak saling mengirim dan menerima satu sama lain ribuan pesan, mereka mengenkripsi pesan-pesan itu dan mengirim ulang (dan menerimanya) berulang-ulang. Seperti yang diharapkan, multithreading adalah kunci ketika Anda berurusan dengan skala ribuan. Dalam program ini tidak ada penguncian yang terlibat .
Jika saya menulis program yang sama di Scala (yang menggunakan JVM), apakah implementasi ini akan lebih cepat? Jika ya, mengapa? Apakah karena gaya penulisan? Jika ini karena gaya penulisan, sekarang Jawa termasuk ekspresi lambda, tidak bisa saya mencapai hasil yang sama dengan menggunakan Java dengan lambda? Atau lebih cepat karena Scala akan mengkompilasi berbagai hal secara berbeda?
sumber
Jawaban:
Alasan orang mengatakan bahasa fungsional lebih baik untuk pemrosesan paralel adalah karena fakta bahwa mereka biasanya menghindari keadaan yang bisa berubah. Keadaan yang bisa berubah adalah "akar dari segala kejahatan" dalam konteks pemrosesan paralel; mereka membuatnya sangat mudah untuk mengalami kondisi lomba ketika mereka dibagi di antara proses bersamaan. Solusi untuk kondisi balapan kemudian melibatkan mekanisme penguncian dan sinkronisasi, seperti yang Anda sebutkan, yang menyebabkan runtime overhead, karena proses menunggu satu sama lain untuk menggunakan sumber daya bersama, dan kompleksitas desain yang lebih besar, karena semua konsep ini cenderung bersarang dalam aplikasi tersebut.
Saat Anda menghindari keadaan yang bisa berubah, kebutuhan untuk sinkronisasi dan mekanisme penguncian menghilang bersamaan dengannya. Karena bahasa fungsional biasanya menghindari keadaan berubah-ubah, mereka secara alami lebih efisien dan efektif untuk pemrosesan paralel - Anda tidak akan memiliki overhead runtime sumber daya bersama, dan Anda tidak akan memiliki kompleksitas desain tambahan yang biasanya mengikuti.
Namun, ini semua kebetulan. Jika solusi Anda di Jawa juga menghindari keadaan yang dapat berubah (khusus dibagi di antara utas), mengonversinya menjadi bahasa fungsional seperti Scala atau Clojure tidak akan menghasilkan manfaat dalam hal efisiensi bersamaan, karena solusi asli sudah bebas dari biaya overhead yang disebabkan oleh mekanisme penguncian dan sinkronisasi.
TL; DR: Jika solusi di Scala lebih efisien dalam pemrosesan paralel daripada di Jawa, itu bukan karena cara kode dikompilasi atau dijalankan melalui JVM, tetapi karena solusi Java berbagi keadaan yang dapat berubah antar thread, baik menyebabkan kondisi balapan atau menambahkan overhead sinkronisasi untuk menghindarinya.
sumber
Semacam keduanya. Lebih cepat karena lebih mudah menulis kode Anda dengan cara yang lebih mudah untuk dikompilasi lebih cepat. Anda tidak perlu mendapatkan perbedaan kecepatan dengan mengganti bahasa, tetapi jika Anda memulai dengan bahasa fungsional, Anda mungkin bisa melakukan multithreading dengan upaya programmer yang jauh lebih sedikit . Sejalan dengan itu, jauh lebih mudah bagi seorang programmer untuk membuat kesalahan threading yang akan membutuhkan biaya dalam bahasa yang sangat penting, dan jauh lebih sulit untuk melihat kesalahan-kesalahan itu.
Alasannya adalah pemrogram yang sangat penting umumnya mencoba untuk meletakkan semua kode bebas-penguncian dalam kotak sekecil mungkin, dan menghindarinya sesegera mungkin, kembali ke dunia sinkron mereka yang nyaman dan dapat diubah. Sebagian besar kesalahan yang membuat Anda kecepatan dibuat pada antarmuka batas itu. Dalam bahasa pemrograman fungsional, Anda tidak perlu terlalu khawatir membuat kesalahan pada batas itu. Sebagian besar kode panggilan Anda juga "di dalam kotak," untuk berbicara.
sumber
Pemrograman fungsional tidak membuat untuk program yang lebih cepat, sebagai aturan umum. Apa yang membuatnya adalah untuk pemrograman paralel dan bersamaan yang lebih mudah . Ada dua kunci utama untuk ini:
Salah satu contoh bagus dari poin # 2 adalah bahwa di Haskell kita memiliki perbedaan yang jelas antara paralelisme deterministik vs konkurensi non-deterministik . Tidak ada penjelasan yang lebih baik selain mengutip buku Simon Marlow yang sangat bagus Parallel and Concurrent Programming di Haskell (kutipan dari Bab 1 ):
Selain itu, Marlow juga menyebutkan dimensi determinisme :
Dalam Haskell fitur paralelisme dan konkurensi dirancang di sekitar konsep-konsep ini. Secara khusus, apa yang dikelompokkan oleh bahasa lain sebagai satu set fitur, Haskell terbagi menjadi dua:
Jika Anda hanya ingin mempercepat perhitungan yang murni dan deterministik, memiliki paralelisme deterministik sering membuat segalanya lebih mudah. Seringkali Anda hanya melakukan sesuatu seperti ini:
Saya benar-benar melakukan ini dengan salah satu program proyek mainan saya beberapa minggu yang lalu . Itu sepele untuk memparalelkan program - hal utama yang harus saya lakukan adalah, pada dasarnya, menambahkan beberapa kode yang mengatakan "menghitung elemen-elemen dari daftar ini secara paralel" (baris 90), dan saya mendapat peningkatan throughput linear dekat pada beberapa kasus pengujian saya yang lebih mahal.
Apakah program saya lebih cepat daripada jika saya menggunakan utilitas multithreading berbasis kunci konvensional? Saya sangat meragukannya. Hal yang rapi dalam kasus saya adalah mendapatkan begitu banyak dari begitu sedikit uang — kode saya mungkin sangat suboptimal, tetapi karena sangat mudah untuk memaralelkan saya mendapat percepatan yang besar dari itu dengan usaha yang jauh lebih sedikit daripada dengan benar membuat profil dan mengoptimalkannya, dan tidak ada risiko kondisi balapan. Dan itu, saya akan klaim, adalah cara utama pemrograman fungsional memungkinkan Anda untuk menulis program "lebih cepat".
sumber
Dalam Haskell, modifikasi secara harfiah tidak mungkin tanpa mendapatkan variabel yang dapat dimodifikasi khusus melalui perpustakaan modifikasi. Sebagai gantinya, fungsi membuat variabel yang mereka butuhkan pada saat yang sama dengan nilainya (yang dihitung dengan malas), dan sampah dikumpulkan ketika tidak lagi diperlukan.
Bahkan ketika Anda membutuhkan variabel modifikasi, Anda biasanya bisa menggunakan sparely, dan bersama dengan variabel yang tidak dapat dimodifikasi. (Hal lain yang menyenangkan di haskell adalah STM, yang menggantikan kunci dengan operasi atom, tapi saya tidak yakin apakah ini hanya untuk pemrograman fungsional atau tidak.) Biasanya, hanya satu bagian dari program yang perlu dibuat paralel untuk meningkatkan hal-hal kinerja-bijaksana.
Ini membuat paralelisme di Haskell sangat mudah, dan pada kenyataannya upaya sedang dilakukan untuk membuatnya otomatis. Untuk kode sederhana, paralelisme dan logika bahkan dapat dipisahkan.
Juga, karena fakta bahwa urutan evaluasi tidak penting di Haskell, kompiler hanya membuat antrian hal-hal yang perlu dievaluasi, dan mengirimkannya ke core apa pun yang tersedia, sehingga Anda dapat membuat banyak "utas" yang tidak sebenarnya menjadi utas sampai diperlukan. Urutan evaluasi tidak penting adalah karakteristik kemurnian, yang biasanya memerlukan pemrograman fungsional.
Bacaan Lebih Lanjut
Paralelisme di Haskell (HaskellWiki)
Pemrograman Bersamaan dan Multicore dalam Pemrograman Paralel dan Bersamaan Haskell Nyata di Dunia
oleh Haskell oleh Simon Marlow
sumber
grep java this_post
.grep scala this_post
dangrep jvm this_post
tanpa hasil :)