Próbuję uzyskać jak największą wydajność z jakiejś metody wewnętrznej.
Kod Java to:
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
}
W moim programie do profilowania zauważyłem, że na procesor przeznaczono 1% java.util.Objects.requireNonNull
, ale nawet tego nie nazywam. Podczas sprawdzania kodu bajtowego zobaczyłem to:
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
Tak więc kompilator generuje to (bezużyteczne?) Sprawdzenie. Pracuję na prymitywach, które i tak nie mogą być null
, więc dlaczego kompilator generuje tę linię? Czy to błąd? Czy „normalne” zachowanie?
(Mogę pracować z maską bitową, ale jestem po prostu ciekawy)
[AKTUALIZACJA]
Wydaje się, że operator nie ma z tym nic wspólnego (patrz odpowiedź poniżej)
Używając kompilatora Eclipse (wersja 4.10) otrzymuję ten bardziej rozsądny wynik:
public getParent (I) Zgłasza wyjątek java / io / IOException L0 LINENUMBER 77 L0 ILOAD 1 ICONST_4 IREM ISTORE 2 L1 LINENUMBER 78 L.
To jest bardziej logiczne.
INVOKESTATIC
javac
tego nie generuje.openjdk version "11.0.6" 2020-01-14
na 64-bitowym Ubuntu.Odpowiedzi:
Dlaczego nie?
Zarozumiały
połączenie takie jak
c.test()
gdziec
jest zadeklarowane jakoC
musi zostać rzucone, kiedyc
jestnull
. Twoja metoda jest równoważnaponieważ pracujesz tylko ze stałymi. Ponieważ
test
jest niestatyczna, kontrola musi zostać wykonana. Normalnie byłoby to zrobione niejawnie, gdy dostęp do pola lub wywołanie metody niestatycznej zostanie wywołane, ale ty tego nie robisz. Potrzebna jest więc wyraźna kontrola. Jedną z możliwości jest dzwonienieObjects.requireNonNull
.Kod bajtowy
Nie zapominaj, że kod bajtowy jest w zasadzie nieistotny dla wydajności. Zadaniem
javac
jest stworzenie pewnego kodu bajtowego, którego wykonanie koresponduje z kodem źródłowym. To nie oznaczało, aby zrobić jakieś optymalizacje, jak zoptymalizowany kod jest zazwyczaj dłuższe i trudniejsze do analizy, natomiast kod bajtowy jest faktycznie kod źródłowy dla kompilatora JIT optymalizacji.javac
Oczekuje się więc, że będzie to proste ....Wydajność
Najpierw obwiniłbym profilera. Profilowanie Java jest dość trudne i nigdy nie można oczekiwać doskonałych rezultatów.
Prawdopodobnie powinieneś spróbować ustawić metodę na statyczną. Z pewnością powinieneś przeczytać ten artykuł o zerowych czekach .
źródło
this
nie jest on niestabilnynull
. Jak sam powiedziałeś, wywołanie typuc.test()
musi zakończyć się niepowodzeniem, gdyc
jest,null
i musi zakończyć się niepowodzeniem natychmiast, zamiast wprowadzania metody. Redzietest()
,this
nigdy nie może byćnull
(w przeciwnym razie nie byłoby to błąd JVM). Więc nie trzeba sprawdzać. Rzeczywistą poprawką powinna być zmiana polataxos
nastatic
, ponieważ nie ma sensu rezerwowanie pamięci w każdym przypadku na stałą czasową kompilacji. Zatem, czytest()
jeststatic
to nieistotne.Wygląda na to, że moje pytanie było „nie tak”, ponieważ nie ma nic wspólnego z operatorem, a raczej z samym polem. Nadal nie wiem dlaczego ...
Co zamienia się w:
źródło
this
odniesienianull
? Czy to byłoby możliwe?Integer
jakiś sposób skompiluje pole , a to jest wynikiem autoboxowania?ALOAD 0
dotyczythis
? Byłoby więc sensowne (nie do końca), aby kompilator dodał nullcheckthis
? Świetnie: /javac
aby sprawdzić jutro; a jeśli to również pokazuje takie zachowanie, myślę, że może to być błąd javac?Po pierwsze, oto minimalny, powtarzalny przykład tego zachowania:
Zachowanie wynika z tego, jak kompilator Java optymalizuje stałe czasu kompilacji .
Zauważ, że w kodzie bajtów
foo()
nie ma dostępu do odwołania do obiektu, aby uzyskać wartośćbar
. Jest tak, ponieważ jest to stała czasowa kompilacji, dlatego JVM może po prostu wykonaćiconst_5
operację, aby zwrócić tę wartość.Zmieniając
bar
się w stałą czasową, która nie jest kompilowana (przez usunięciefinal
słowa kluczowego lub nie inicjowanie w deklaracji, ale w konstruktorze) otrzymujesz:gdzie
aload_0
pcha odniesienie sięthis
na stosie argumentu aby następnie uzyskaćbar
pola tego obiektu.Tutaj kompilator jest wystarczająco sprytny, aby zauważyć, że
aload_0
(this
odniesienie w przypadku funkcji składowych) logicznie nie może byćnull
.Czy twoja sprawa faktycznie nie zawiera optymalizacji kompilatora?
Zobacz odpowiedź @maaartinus.
źródło