Saya mencoba mendapatkan kinerja sebanyak mungkin dari beberapa metode internal.
Kode Java adalah:
List<DirectoryTaxonomyWriter> writers = Lists.newArrayList();
private final int taxos = 4;
[...]
@Override
public int getParent(final int globalOrdinal) throws IOException {
final int bin = globalOrdinal % this.taxos;
final int ordinalInBin = globalOrdinal / this.taxos;
return this.writers.get(bin).getParent(ordinalInBin) * this.taxos + bin; //global parent
}
Di profiler saya, saya melihat ada pengeluaran CPU 1% java.util.Objects.requireNonNull
, tapi saya bahkan tidak menyebutnya. Saat memeriksa bytecode, saya melihat ini:
public getParent(I)I throws java/io/IOException
L0
LINENUMBER 70 L0
ILOAD 1
ALOAD 0
INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
POP
BIPUSH 8
IREM
ISTORE 2
Jadi kompiler menghasilkan cek (tidak berguna) ini. Saya bekerja pada primitif, yang tidak bisa null
lagian, jadi mengapa kompiler menghasilkan baris ini? Apakah ini bug? Atau perilaku 'normal'?
(Saya mungkin bekerja dengan bitmask, tapi saya hanya ingin tahu)
[MEMPERBARUI]
Operator tampaknya tidak ada hubungannya dengan itu (lihat jawaban di bawah)
Menggunakan compiler eclipse (versi 4.10) saya mendapatkan hasil yang lebih masuk akal ini:
getParent publik (I) saya melempar java / io / IOException L0 LINENUMBER 77 L0 ILOAD 1 ICONST_4 IREM ISTORE 2 L1 LINENUMBER 78 L
Jadi itu lebih masuk akal.
INVOKESTATIC
javac
tidak menghasilkan ini.openjdk version "11.0.6" 2020-01-14
di ubuntu 64 bit.Jawaban:
Kenapa tidak?
Asumsi
panggilan seperti
c.test()
di manac
dinyatakan sebagaiC
harus membuang ketikac
adalahnull
. Metode Anda setara dengansaat Anda bekerja dengan konstanta saja. Dengan
test
tidak statis, pemeriksaan harus dilakukan. Biasanya, ini akan dilakukan secara implisit ketika suatu bidang diakses atau metode non-statis dipanggil, tetapi Anda tidak melakukannya. Jadi diperlukan pemeriksaan eksplisit. Satu kemungkinan adalah meneleponObjects.requireNonNull
.Bytecode
Jangan lupa bahwa bytecode pada dasarnya tidak relevan untuk kinerja. Tugasnya
javac
adalah untuk menghasilkan beberapa bytecode yang pelaksanaannya sesuai dengan kode sumber Anda. Ini tidak dimaksudkan untuk melakukan setiap optimasi, sebagai kode dioptimalkan biasanya lebih lama dan lebih sulit untuk menganalisis, sementara bytecode adalah benar-benar kode sumber untuk compiler mengoptimalkan JIT. Jadijavac
diharapkan tetap sederhana ....Penampilan
Saya akan menyalahkan profiler terlebih dahulu. Membuat profil Java cukup sulit dan Anda tidak pernah dapat mengharapkan hasil yang sempurna.
Anda mungkin harus mencoba membuat metode ini statis. Anda tentu harus membaca artikel ini tentang cek kosong .
sumber
this
itu non-null
. Seperti yang Anda katakan pada diri sendiri, panggilan sepertic.test()
harus gagal ketikac
adalahnull
dan itu harus segera gagal, bukannya memasuki metode. Jadi di dalamtest()
,this
tidak pernah bisanull
(kalau tidak akan ada bug JVM). Jadi tidak perlu diperiksa. Perbaikan sebenarnya harus mengubah bidangtaxos
menjadistatic
, karena tidak ada gunanya menyimpan memori dalam setiap contoh untuk konstanta waktu kompilasi. Kemudian, apakahtest()
inistatic
tidak relevan.Yah sepertinya pertanyaan saya 'salah' karena tidak ada hubungannya dengan operator, melainkan bidang itu sendiri. Masih tidak tahu kenapa ..
Yang berubah menjadi:
sumber
this
referensi itunull
? Apakah ini mungkin?Integer
cara, dan ini adalah hasil dari autoboxing?ALOAD 0
referensithis
? Jadi masuk akal (tidak juga) bahwa kompiler menambahkan nullcheckthis
? Hebat: /javac
untuk memverifikasi besok; dan jika itu juga menunjukkan perilaku ini, saya pikir itu mungkin javac-bug?Pertama, inilah contoh minimal dari perilaku ini:
Perilaku ini karena bagaimana kompiler Java mengoptimalkan konstanta waktu kompilasi .
Perhatikan bahwa dalam kode byte
foo()
tidak ada referensi objek diakses untuk mendapatkan nilaibar
. Itu karena itu adalah konstanta waktu kompilasi dan dengan demikian JVM dapat dengan mudah menjalankaniconst_5
operasi untuk mengembalikan nilai ini.Saat berubah
bar
menjadi konstanta waktu non-kompilasi (baik dengan menghapusfinal
kata kunci atau tidak menginisialisasi dalam deklarasi tetapi di dalam konstruktor) Anda akan mendapatkan:di mana
aload_0
mendorong referensi darithis
ke operan stack untuk kemudian mendapatkanbar
bidang obyek ini.Di sini kompiler cukup pintar untuk memperhatikan bahwa
aload_0
(this
referensi jika fungsi anggota) secara logis tidak bisanull
.Sekarang apakah kasus Anda sebenarnya adalah optimasi kompiler yang hilang?
Lihat jawaban @maaartinus.
sumber