Fungsi murni dikenal untuk memfasilitasi pembuatan parellel. Ada apa dengan pemrograman fungsional yang membuatnya secara inheren disesuaikan dengan eksekusi paralel?
Apakah kompiler seperti Javac cukup pintar untuk mendeteksi kapan suatu metode merupakan fungsi murni? Seseorang selalu dapat mengimplementasikan kelas yang mengimplementasikan antarmuka fungsional seperti Fungsi , tetapi memiliki efek samping.
functional-programming
Naveen
sumber
sumber
NullPointerException
. Manfaat optimasi berdasarkan ini juga mungkin cukup kecil untuk aplikasi Java yang khas.Jawaban:
Ini bukan pertanyaan "cukup pintar". Ini disebut Analisis Kemurnian dan terbukti mustahil dalam kasus umum: ini setara dengan menyelesaikan Masalah Pemutusan Hubungan.
Sekarang, tentu saja, pengoptimal melakukan hal-hal yang terbukti mustahil sepanjang waktu, "terbukti tidak mungkin dalam kasus umum" tidak berarti tidak pernah berhasil, itu hanya berarti tidak dapat berfungsi dalam semua kasus. Jadi, sebenarnya ada algoritma untuk memeriksa apakah suatu fungsi murni atau tidak, hanya saja lebih sering hasilnya adalah "Saya tidak tahu", yang berarti bahwa untuk alasan keamanan dan kebenaran, Anda perlu mengasumsikan bahwa fungsi khusus ini mungkin tidak murni.
Dan bahkan dalam kasus di mana tidak bekerja, algoritma yang kompleks dan mahal.
Jadi, itulah Masalah # 1: itu hanya berfungsi untuk kasus khusus .
Masalah # 2: Perpustakaan . Agar suatu fungsi menjadi murni, ia hanya dapat memanggil fungsi murni (dan fungsi tersebut hanya dapat memanggil fungsi murni, dan seterusnya dan seterusnya). Javac jelas hanya tahu tentang Java, dan hanya tahu tentang kode yang dapat dilihatnya. Jadi, jika fungsi Anda memanggil fungsi di unit kompilasi lain, Anda tidak bisa tahu apakah itu murni atau tidak. Jika itu memanggil fungsi yang ditulis dalam bahasa lain, Anda tidak bisa tahu. Jika itu memanggil fungsi di perpustakaan yang bahkan mungkin belum diinstal, Anda tidak bisa tahu. Dan seterusnya.
Ini hanya berfungsi, ketika Anda memiliki analisis seluruh program, ketika seluruh program ditulis dalam bahasa yang sama, dan semua dikompilasi sekaligus dalam satu waktu. Anda tidak dapat menggunakan perpustakaan apa pun.
Masalah # 3: Penjadwalan . Setelah Anda mengetahui bagian mana yang murni, Anda masih harus menjadwalkannya untuk memisahkan utas. Atau tidak. Memulai dan menghentikan utas sangat mahal (terutama di Jawa). Bahkan jika Anda menyimpan kumpulan utas dan tidak memulai atau menghentikannya, pergantian konteks utas juga mahal. Anda harus yakin bahwa perhitungan akan berjalan jauh lebih lama daripada waktu yang diperlukan untuk menjadwalkan dan mengubah konteks, jika tidak, Anda akan kehilangan kinerja, bukan memperolehnya.
Seperti yang mungkin sudah Anda tebak sekarang, mencari tahu berapa lama perhitungan akan terbukti tidak mungkin dalam kasus umum (kami bahkan tidak dapat menentukan apakah akan membutuhkan waktu yang terbatas, apalagi berapa lama) dan sulit dan mahal bahkan di kasus khusus.
Selain: Javac dan optimisasi . Perhatikan bahwa sebagian besar implementasi javac tidak benar-benar melakukan banyak optimasi. Implementasi Oracle dari javac, misalnya, bergantung pada mesin eksekusi yang mendasari untuk melakukan optimasi . Ini mengarah ke serangkaian masalah lain: katakanlah, javac memutuskan bahwa fungsi tertentu murni dan cukup mahal, sehingga mengkompilasinya untuk dieksekusi pada utas yang berbeda. Kemudian, pengoptimal platform (misalnya, kompiler JIT HotSpot C2) datang dan mengoptimalkan seluruh fungsi. Sekarang, Anda memiliki utas kosong yang tidak melakukan apa-apa. Atau, bayangkan, sekali lagi, javac memutuskan untuk menjadwalkan fungsi pada utas yang berbeda, dan pengoptimal platform bisa mengoptimalkannya sepenuhnya, kecuali itu tidak dapat melakukan inlining melintasi batas-batas thread, dan fungsi yang dapat dioptimalkan sepenuhnya sepenuhnya sekarang dieksekusi tanpa perlu.
Jadi, melakukan sesuatu seperti ini hanya benar-benar masuk akal jika Anda memiliki satu kompiler membuat sebagian besar optimasi dalam sekali jalan, sehingga kompiler mengetahui dan dapat mengeksploitasi semua optimasi yang berbeda di tingkat yang berbeda dan interaksinya satu sama lain.
Perhatikan bahwa, misalnya, kompiler HotSpot C2 JIT benar - benar melakukan beberapa auto-vektorisasi, yang juga merupakan bentuk paralelisasi otomatis.
sumber
definition
, menggunakan berbedadefinition
daripurity
mungkin jelasStringBuilder
) adalah tidak masuk akal, jadi saya akan mengabaikannya dan berasumsi, OP menulis javac tetapi berarti Hotspot. Masalah Anda # 2 adalah alasan yang cukup bagus untuk tidak mengoptimalkan apa pun di javac.Jawaban yang dipilih gagal mencatat satu hal. Komunikasi sinkron antara utas sangat mahal. Jika fungsi ini mampu dieksekusi pada kecepatan jutaan panggilan per detik, itu sebenarnya lebih menyakitkan Anda untuk memparalelkannya daripada membiarkannya apa adanya.
Bentuk komunikasi inter-thread sinkron tercepat, menggunakan loop sibuk dengan variabel atom, sayangnya tidak efisien energi. Jika Anda harus menggunakan variabel kondisi untuk menghemat energi, kinerja komunikasi antar-thread Anda akan terganggu.
Jadi, kompiler tidak hanya perlu menentukan apakah suatu fungsi murni, tetapi juga perlu memperkirakan waktu eksekusi fungsi untuk melihat apakah paralelisasi adalah kemenangan bersih. Selain itu, perlu memilih antara loop sibuk menggunakan variabel atom atau variabel kondisi. Dan itu perlu membuat utas di belakang Anda.
Jika Anda membuat utas secara dinamis, itu bahkan lebih lambat daripada menggunakan variabel kondisi. Jadi, kompiler perlu menyiapkan sejumlah utas yang sudah berjalan.
Jadi, jawaban untuk pertanyaan Anda adalah tidak , kompiler tidak cukup "pintar" untuk melakukan paralelisasi fungsi murni terutama di dunia Java. Mereka pintar dengan tidak membuat mereka paralel!
sumber