İnt üzerinde kalan operatör java.util.Objects.requireNonNull?


12

Bazı iç yöntemlerden mümkün olduğunca fazla performans elde etmeye çalışıyorum.

Java kodu:

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
}

Profilimde% 1 CPU harcaması olduğunu gördüm java.util.Objects.requireNonNull, ama bunu bile aramıyorum. Bayt kodunu incelerken şunu gördüm:

 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

Böylece derleyici bu (yararsız?) Kontrol üretir. Ben nullzaten olamaz ilkel üzerinde çalışıyorum, neden derleyici bu satırı üretir? Bu bir hata mı? Yoksa 'normal' davranış mı?

(Bir bit maskesi ile çalışabilirim, ama sadece merak ediyorum)

[GÜNCELLEME]

  1. Operatörün bununla hiçbir ilgisi yok gibi görünüyor (aşağıdaki cevaba bakınız)

  2. Tutulma derleyicisini (sürüm 4.10) kullanarak bu daha makul bir sonuç elde ederim:

    public getParent (I) java / io / IOException'ı atar 
       L0
        LINENUMBER 77 L0
        ILOAD 1
        ICONST_4
        IREM
        ISTORE 2
       L1
        LINENUMBER 78 L

Yani bu daha mantıklı.


@Lino emin, ama bu 70 satır için gerçekten önemli değilINVOKESTATIC
RobAu

Hangi derleyiciyi kullanıyorsunuz? Normal javacbunu üretmez.
apangin

Hangi derleyiciyi kullanıyorsunuz? Java sürümü, Openjdk / Oracle / vb. Düzenleme: whops, @apangin daha hızlı, üzgünüm
lugiorgi

1
Ubuntu openjdk version "11.0.6" 2020-01-1464 bit üzerinde java 11 ile Intellij 2019.3'ten derlenmiştir .
RobAu

Yanıtlar:


3

Neden olmasın?

varsayarsak

class C {
    private final int taxos = 4;

    public int test() {
        final int a = 7;
        final int b = this.taxos;
        return a % b;
    }
}

Böyle bir çağrı c.test()nerede cdiye ilan edilir C gerekir zaman atmak colduğunu null. Yönteminiz şuna eşdeğerdir:

    public int test() {
        return 3; // `7 % 4`
    }

sadece sabitlerle çalışırken. İle testsigara statik olma, çek yapılmalıdır. Normalde, bir alana erişildiğinde veya statik olmayan bir yöntem çağrıldığında örtük olarak yapılır, ancak bunu yapmazsınız. Bu yüzden açık bir kontrol gereklidir. Bir olasılık aramaktır Objects.requireNonNull.

Bayt kodu

Bayt kodunun performansla temel olarak alakasız olduğunu unutmayın. Görevi javacüretmektir bazı kimin yürütme kaynak kodu ile karşılık bayt kodu. Optimize edilmiş kodun analizi genellikle daha uzun ve zor olduğu için herhangi bir optimizasyon yapılması gerekmez , bayt kodu aslında optimizasyon JIT derleyicisinin kaynak kodudur . Yani javacbasit tutulması bekleniyor ....

Performans

Profilimde% 1 CPU harcaması olduğunu gördüm java.util.Objects.requireNonNull

Önce profiler suçlanacaktı. Java profili oluşturmak oldukça zordur ve asla mükemmel sonuçlar beklemezsiniz.

Muhtemelen yöntemi statik hale getirmeyi denemelisiniz. Boş kontrollerle ilgili bu makaleyi mutlaka okumalısınız .


1
Anlayışlı yanıtınız için @maaartinus'a teşekkürler. Bağlantılı makalenizi kesinlikle okuyacağım.
RobAu

1
“Test statik olmadığında, kontrol yapılmalıdır” Aslında, thisolmayan olup olmadığını test etmek için bir neden yoktur null. Kendin söyledin, böyle bir çağrı c.test()başarısız gerekir colduğunu nullve bunun yerine yöntemi girmek yerine, hemen başarısız zorundadır. Yani içinde test(), thisasla olamaz null(aksi takdirde bir JVM hatası olurdu). Yani kontrol etmeye gerek yok. Gerçek düzeltme alanını değiştirerek olmalıdır taxosiçin staticbir derleme zamanı sabiti için her durumda hafızada yer anlamı yok olduğu gibi. Sonra, ister test()bir staticönemi yoktur.
Holger

2

Operatör ile hiçbir ilgisi olmadığı için sorumun yanlış olduğu anlaşılıyor, daha çok alanın kendisi. Hala nedenini bilmiyorum ..

   public int test() {
        final int a = 7;
        final int b = this.taxos;
        return a % b;
    }

Hangi dönüşür:

  public test()I
   L0
    LINENUMBER 51 L0
    BIPUSH 7
    ISTORE 1
   L1
    LINENUMBER 52 L1
    ALOAD 0
    INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
    POP
    ICONST_4
    ISTORE 2
   L2
    LINENUMBER 53 L2
    BIPUSH 7
    ILOAD 2
    IREM
    IRETURN

1
Derleyici bu thisreferanslardan gerçekten korkabilir nullmi? Bu mümkün mü?
atalantus

1
Hayır, derleyici alanı bir Integerşekilde derlemedikçe hiçbir anlam ifade etmiyor ve bu otomatik boksun sonucu mu?
RobAu

1
Does not ALOAD 0başvuru this? Derleyicinin bir nullcheck eklediği (gerçekten değil) mantıklı olurdu
Lino

1
Yani derleyici aslında bir null çek ekliyor this? Mükemmel: /
RobAu

1
javacYarın doğrulamak için komut satırı ile en az kod parçası yapmaya çalışacağım ; ve bu da bu davranışı gösterirse, bir javac-bug olabileceğini düşünüyorum?
RobAu

2

İlk olarak, bu davranışın minimal bir tekrarlanabilir örneği:

/**
 * OS:              Windows 10 64x
 * javac version:   13.0.1
 */
public class Test {
    private final int bar = 5;

    /**
     * public int foo();
     *   Code:
     *     0: iconst_5
     *     1: ireturn
     */
    public int foo() {
        return bar;
    }

    /**
     * public int foo2();
     *   Code:
     *     0: aload_0
     *     1: invokestatic  #13     // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
     *     4: pop
     *     5: iconst_5
     *     6: ireturn
     */
    public int foo2() {
        return this.bar;
    }
}

Davranış, Java derleyicisinin derleme zamanı sabitlerini nasıl optimize ettiğidir .

foo()Hiçbir nesne başvurusunun bayt kodunda değerini almak için erişildiğini unutmayın bar. Çünkü derleme zamanı sabiti olduğundan JVM iconst_5bu değeri döndürmek için işlemi gerçekleştirebilir .

barDerleme dışı bir zaman sabitine geçtiğinizde (ya finalanahtar kelimeyi kaldırarak ya da bildirim içinde ancak kurucu içinde başlatma yapmadan) şunları elde edersiniz:

/**
 * OS:              Windows 10 64x
 * javac version:   13.0.1
 */
public class Test2 {
    private int bar = 5;

    /**
     * public int foo();
     *   Code:
     *     0: aload_0
     *     1: getfield      #7
     *     4: ireturn
     */
    public int foo() {
        return bar;
    }

    /**
     * public int foo2();
     *   Code:
     *     0: aload_0
     *     1: getfield      #7
     *     4: ireturn
     */
    public int foo2() {
        return this.bar;
    }
}

burada aload_0iter referans ait thisişlemsel yığın daha sonra üzerine almak baralan bu nesnenin.

İşte derleyici bildirilmesinden kadar zeki aload_0( thisüye fonksiyonların durumunda başvuru) mantıksal olamaz null.

Durumunuz aslında eksik bir derleyici optimizasyonu mu?

@Maaartinus cevabına bakınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.