Kaedah Inlining dalam JVM

1. Pengenalan

Dalam tutorial ini, kita akan melihat kaedah inlining apa yang ada di Java Virtual Machine dan bagaimana ia berfungsi.

Kami juga akan melihat bagaimana mendapatkan dan membaca maklumat yang berkaitan dengan inlining dari JVM dan apa yang dapat kami lakukan dengan maklumat ini untuk mengoptimumkan kod kami.

2. Apakah Kaedah Menyisipkan?

Pada asasnya, sebaris adalah cara untuk mengoptimumkan kod sumber yang disusun pada waktu berjalan dengan menggantikan pemanggilan kaedah yang paling sering dilaksanakan dengan badannya.

Walaupun kompilasi terlibat, ia tidak dilakukan oleh penyusun javac tradisional , tetapi oleh JVM itu sendiri. Untuk lebih tepatnya, itu adalah tanggungjawab penyusun Just-In-Time (JIT) , yang merupakan sebahagian daripada JVM; javac hanya menghasilkan bytecode dan membolehkan JIT melakukan keajaiban dan mengoptimumkan kod sumber.

Salah satu akibat yang paling penting dari pendekatan ini adalah jika kita menyusun kod menggunakan Java lama, sama. fail kelas akan lebih pantas pada JVM yang lebih baru. Dengan cara ini kita tidak perlu menyusun semula kod sumber, tetapi hanya mengemas kini Java.

3. Bagaimana JIT Melakukannya?

Pada asasnya, penyusun JIT cuba menggariskan kaedah yang sering kita panggil sehingga kita dapat mengelakkan overhead kaedah pemanggilan . Perlu mengambil kira dua perkara ketika memutuskan sama ada kaedah sebaris atau tidak.

Pertama, ia menggunakan pembilang untuk mengetahui berapa kali kita menggunakan kaedah ini. Apabila kaedah dipanggil lebih daripada sebilangan kali tertentu, ia menjadi "panas". Ambang ini ditetapkan ke 10,000 secara lalai, tetapi kami dapat mengkonfigurasinya melalui bendera JVM semasa permulaan Java. Kami pasti tidak mahu menyusun segalanya kerana akan memakan masa dan akan menghasilkan kod bytec yang besar.

Kita harus ingat bahawa sebaris akan berlaku hanya apabila kita berada dalam keadaan stabil. Ini bermaksud bahawa kita perlu mengulangi pelaksanaan beberapa kali untuk memberikan maklumat profil yang cukup untuk penyusun JIT.

Selanjutnya, menjadi "panas" tidak menjamin bahawa kaedah tersebut akan diselaraskan. Sekiranya terlalu besar, JIT tidak akan menyusunnya. Ukuran yang boleh diterima dibatasi oleh bendera -XX: FreqInlineSize = , yang menentukan bilangan maksimum arahan bytecode untuk sebaris kaedah.

Walaupun begitu, sangat disarankan untuk tidak mengubah nilai lalai bendera ini melainkan kami benar-benar yakin untuk mengetahui apa kesannya. Nilai lalai bergantung pada platform - untuk Linux 64-bit, ia adalah 325.

JIT merangkumi kaedah statik , peribadi , atau akhir secara umum . Walaupun kaedah awam juga merupakan calon untuk mencantumkan, tidak setiap kaedah awam semestinya akan diselaraskan. JVM perlu menentukan bahawa hanya ada satu kaedah pelaksanaan sahaja . Sebarang subkelas tambahan akan menghalang inline dan prestasi pasti akan menurun.

4. Mencari Kaedah Panas

Kami pasti tidak mahu meneka apa yang sedang dilakukan oleh JIT. Oleh itu, kita memerlukan beberapa kaedah untuk melihat kaedah mana yang sebaris atau tidak sebaris. Kami dapat mencapainya dengan mudah dan mencatat semua maklumat ini ke output standard dengan menetapkan beberapa bendera JVM tambahan semasa permulaan:

-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining

Bendera pertama akan log apabila penyusunan JIT berlaku. Bendera kedua membolehkan bendera tambahan termasuk -XX: + PrintInlining , yang akan mencetak kaedah apa yang masuk dan di mana.

Ini akan menunjukkan kepada kita kaedah sebaris dalam bentuk pokok. Daun diberi penjelasan dan ditandakan dengan salah satu pilihan berikut:

  • sebaris (panas) - kaedah ini ditandakan sebagai panas dan sebaris
  • terlalu besar - kaedahnya tidak panas, tetapi juga bytecode yang dihasilkannya terlalu besar, jadi tidak sebaris
  • kaedah panas terlalu besar - ini adalah kaedah panas, tetapi tidak sebaris kerana kod bytek terlalu besar

Kita harus memperhatikan nilai ketiga dan berusaha mengoptimumkan kaedah dengan label "kaedah panas terlalu besar".

Secara amnya, jika kita menjumpai kaedah panas dengan pernyataan bersyarat yang sangat kompleks, kita harus cuba memisahkan kandungan pernyataan if dan meningkatkan butiran sehingga JIT dapat mengoptimumkan kodnya. Begitu juga dengan suis dan bagi- penyata gelung.

Kita dapat menyimpulkan bahawa kaedah manual inlining adalah sesuatu yang tidak perlu kita lakukan untuk mengoptimumkan kod kita. JVM melakukannya dengan lebih cekap, dan kami mungkin membuat kodnya panjang dan sukar diikuti.

4.1. Contohnya

Mari kita lihat bagaimana kita dapat memeriksa ini dalam praktik. Mula-mula kita buat kelas sederhana yang mengira jumlah N bilangan bulat positif berturut-turut pertama :

public class ConsecutiveNumbersSum { private long totalSum; private int totalNumbers; public ConsecutiveNumbersSum(int totalNumbers) { this.totalNumbers = totalNumbers; } public long getTotalSum() { totalSum = 0; for (int i = 0; i < totalNumbers; i++) { totalSum += i; } return totalSum; } }

Seterusnya, kaedah mudah akan menggunakan kelas untuk melakukan pengiraan:

private static long calculateSum(int n) { return new ConsecutiveNumbersSum(n).getTotalSum(); }

Akhirnya, kami akan memanggil kaedah ini beberapa kali dan melihat apa yang berlaku:

for (int i = 1; i < NUMBERS_OF_ITERATIONS; i++) { calculateSum(i); }

Pada jangka pertama, kita akan menjalankannya 1,000 kali (kurang daripada nilai ambang 10,000 yang disebutkan di atas). Sekiranya kita mencari output untuk kaedah calculSum () , kita tidak akan menjumpainya. Ini dijangka kerana kita tidak menyebutnya cukup lama.

Sekiranya kita sekarang menukar bilangan lelaran menjadi 15,000 dan mencari outputnya lagi, kita akan melihat:

664 262 % com.baeldung.inlining.InliningExample::main @ 2 (21 bytes) @ 10 com.baeldung.inlining.InliningExample::calculateSum (12 bytes) inline (hot)

Kita dapat melihat bahawa kali ini kaedah memenuhi syarat untuk memasukkan dan JVM menggariskannya.

Perlu diperhatikan lagi bahawa jika kaedahnya terlalu besar, JIT tidak akan menyisipkannya, tanpa mengira jumlah lelaran. Kami dapat memeriksa ini dengan menambahkan bendera lain ketika menjalankan aplikasi:

-XX:FreqInlineSize=10

Seperti yang dapat kita lihat pada output sebelumnya, ukuran kaedah kita adalah 12 bait. The -XX: FreqInlineSize bendera akan membataskan saiz layak kaedah untuk penyebarisan 10 bait. Oleh itu, inlining tidak seharusnya berlaku pada masa ini. Dan sememangnya, kami dapat mengesahkannya dengan melihat outputnya:

330 266 % com.baeldung.inlining.InliningExample::main @ 2 (21 bytes) @ 10 com.baeldung.inlining.InliningExample::calculateSum (12 bytes) hot method too big

Walaupun kami telah menukar nilai bendera di sini untuk tujuan ilustrasi, kami mesti menekankan cadangan untuk tidak mengubah nilai lalai bendera -XX: FreqInlineSize kecuali jika benar-benar diperlukan.

5. Kesimpulan

Dalam artikel ini, kami melihat kaedah sebaris dalam JVM dan bagaimana JIT melakukannya. Kami menerangkan bagaimana kami dapat memeriksa sama ada kaedah kami layak untuk disisipkan atau tidak dan mencadangkan bagaimana memanfaatkan maklumat ini dengan berusaha mengurangkan ukuran kaedah panjang yang sering disebut yang terlalu besar untuk disisipkan.

Akhirnya, kami menggambarkan bagaimana kita dapat mengenal pasti kaedah yang panas dalam praktik.

Semua coretan kod yang disebutkan dalam artikel boleh didapati di repositori GitHub kami.