Czy tworzenie obiektu przy użyciu odbicia zamiast wywoływania konstruktora klasy powoduje jakiekolwiek istotne różnice w wydajności?
java
performance
optimization
reflection
dmanxiii
źródło
źródło
Odpowiedzi:
Tak - absolutnie. Patrzenie w górę klasy poprzez refleksję jest według wielkości droższe.
Cytując dokumentację Java na temat refleksji :
Oto prosty test, który zhakowałem w ciągu 5 minut na moim komputerze, używając Sun JRE 6u10:
Z tymi wynikami:
Należy pamiętać, że wyszukiwanie i tworzenie instancji są wykonywane razem, aw niektórych przypadkach można je ponownie zmienić, ale to tylko podstawowy przykład.
Nawet jeśli tylko utworzysz instancję, nadal otrzymujesz hit wydajnościowy:
Ponownie YMMV.
źródło
Tak, jest wolniejszy.
Ale pamiętajcie o cholernej zasadzie nr 1 - WCZESNA OPTYMALIZACJA JEST KORZENIĄ CAŁEGO ZŁA
(Cóż, może być powiązany z numerem 1 dla DRY)
Przysięgam, gdyby ktoś podszedł do mnie w pracy i zapytał o to, będę bardzo czuwał nad jego kodem przez kilka następnych miesięcy.
Nie możesz nigdy optymalizować, dopóki nie jesteś pewien, że jej potrzebujesz, do tego czasu po prostu napisz dobry, czytelny kod.
Aha, i nie mam na myśli pisania głupiego kodu. Pomyśl tylko o najczystszym sposobie, w jaki możesz to zrobić - bez kopiowania i wklejania itp. (Nadal uważaj na rzeczy takie jak wewnętrzne pętle i korzystaj z kolekcji, która najlepiej pasuje do twoich potrzeb - ignorowanie tego nie jest "niezoptymalizowanym" programowaniem , to „złe” programowanie)
Strasznie mnie przeraża, kiedy słyszę takie pytania, ale potem zapominam, że każdy musi nauczyć się wszystkich zasad, zanim naprawdę je zrozumieją. Otrzymasz go po spędzeniu miesiąca osobowego na debugowaniu czegoś „zoptymalizowanego”.
EDYTOWAĆ:
W tym wątku wydarzyła się ciekawa rzecz. Sprawdź odpowiedź nr 1, to przykład tego, jak potężny jest kompilator w optymalizacji rzeczy. Test jest całkowicie nieważny, ponieważ nieodblaskowa instancja może zostać całkowicie uwzględniona.
Lekcja? NIGDY nie optymalizuj, dopóki nie napiszesz czystego, starannie zakodowanego rozwiązania i nie udowodnisz, że jest zbyt wolne.
źródło
Może się okazać, że A a = new A () jest optymalizowane przez JVM. Jeśli umieścisz obiekty w tablicy, nie będą one działać tak dobrze. ;) Poniższe wydruki ...
To sugeruje, że różnica na moim komputerze wynosi około 150 ns.
źródło
Class.getDeclaredMethod
), a potem zadzwonięMethod.invoke
wiele razy? Czy używam odbicia raz, czy tyle razy, ile go przywołuję? Pytanie uzupełniające, co jeśli zamiastMethod
tego jest a,Constructor
a ja robięConstructor.newInstance
wiele razy?Jeśli naprawdę istnieje potrzeba czegoś szybszego niż odbicie i nie jest to tylko przedwczesna optymalizacja, to generowanie kodu bajtowego z ASM to opcją jest lub biblioteką wyższego poziomu. Generowanie kodu bajtowego za pierwszym razem jest wolniejsze niż zwykłe użycie odbicia, ale po wygenerowaniu kodu bajtowego jest tak szybkie, jak normalny kod Java i zostanie zoptymalizowany przez kompilator JIT.
Kilka przykładów aplikacji wykorzystujących generowanie kodu:
Wywoływanie metod na serwerach proxy generowanych przez CGLIB jest nieco szybsze niż dynamiczne serwery proxy Javy , ponieważ CGLIB generuje kod bajtowy dla swoich serwerów proxy, ale dynamiczne proxy używają tylko odbicia ( zmierzyłem, że CGLIB jest około 10 razy szybszy w wywołaniach metod, ale tworzenie proxy było wolniejsze).
JSerial generuje kod bajtowy do odczytu / zapisu pól obiektów serializowanych, zamiast używać odbicia. Na stronie JSerial jest kilka testów porównawczych .
Nie jestem pewien w 100% (i nie mam teraz ochoty czytać źródła), ale myślę, że Guice generuje kod bajtowy do wstrzykiwania zależności. Popraw mnie, jeśli się mylę.
źródło
„Znaczące” jest całkowicie zależne od kontekstu.
Jeśli używasz odbicia do tworzenia pojedynczego obiektu obsługi na podstawie jakiegoś pliku konfiguracyjnego, a następnie spędzasz resztę czasu na wykonywaniu zapytań do bazy danych, to jest to nieistotne. Jeśli tworzysz dużą liczbę obiektów poprzez odbicie w ciasnej pętli, to tak, to jest ważne.
Ogólnie rzecz biorąc, elastyczność projektowania (w razie potrzeby!) Powinna wpływać na refleksję, a nie wydajność. Jednak aby określić, czy wydajność jest problemem, należy raczej profilować, niż uzyskiwać arbitralne odpowiedzi z forum dyskusyjnego.
źródło
Istnieje trochę narzutów z odbiciem, ale na nowoczesnych maszynach wirtualnych jest on znacznie mniejszy niż kiedyś.
Jeśli używasz odbicia do tworzenia każdego prostego obiektu w swoim programie, coś jest nie tak. Używanie go okazjonalnie, gdy masz dobry powód, nie powinno w ogóle stanowić problemu.
źródło
Tak, podczas korzystania z odbicia występuje spadek wydajności, ale możliwym obejściem optymalizacji jest buforowanie metody:
spowoduje:
[java] Odruchowe wywołanie metody 1000000 razy z wyszukiwaniem zajęło 5618 milisów
[java] Odruchowe wywołanie metody 1000000 razy z pamięcią podręczną zajęło 270 milisekund
źródło
Odbicie jest powolne, chociaż alokacja obiektów nie jest tak beznadziejna, jak inne aspekty refleksji. Osiągnięcie równoważnej wydajności z tworzeniem instancji opartej na odbiciu wymaga napisania kodu, aby jit mógł określić, która klasa jest tworzona. Jeśli nie można określić tożsamości klasy, nie można wstawić kodu alokacji. Co gorsza, analiza ucieczki kończy się niepowodzeniem, a obiektu nie można zaalokować na stosie. Jeśli masz szczęście, profilowanie w czasie wykonywania maszyny JVM może przyjść na ratunek, jeśli ten kod stanie się gorący i może dynamicznie określić, która klasa dominuje i może zostać zoptymalizowana pod kątem tej.
Należy pamiętać, że mikrozlamy w tym wątku są głęboko wadliwe, więc weź je z przymrużeniem oka. Najmniej wadliwy jest zdecydowanie Peter Lawrey: wykonuje rozgrzewki, aby zrzucić metody, i (świadomie) pokonuje analizę ucieczki, aby upewnić się, że alokacje faktycznie mają miejsce. Nawet to ma jednak swoje problemy: na przykład można oczekiwać, że ogromna liczba magazynów macierzy pokona pamięci podręczne i bufory przechowywania, więc będzie to głównie test porównawczy pamięci, jeśli alokacje są bardzo szybkie. (Chwała Peterowi za prawidłowe wyciągnięcie wniosku: różnica wynosi „150 ns”, a nie „2,5x”. Podejrzewam, że zajmuje się takimi rzeczami na życie).
źródło
Co ciekawe, ustawienie setAccessible (true), które pomija kontrole bezpieczeństwa, daje 20% redukcję kosztów.
Bez setAccessible (true)
Z setAccessible (true)
źródło
1000000
wywołań?setAccessible()
może mieć znacznie większą różnicę w ogóle, szczególnie w przypadku metod z wieloma argumentami, więc zawsze należy ją wywołać.Tak, jest znacznie wolniejszy. Uruchomiliśmy kod, który to zrobił i chociaż w tej chwili nie mam dostępnych metryk, efekt końcowy był taki, że musieliśmy refaktoryzować ten kod, aby nie używać odbicia. Jeśli wiesz, czym jest klasa, po prostu zadzwoń bezpośrednio do konstruktora.
źródło
W doReflection () jest narzut spowodowany Class.forName („misc.A”) (który wymagałby przeszukania klasy, potencjalnie skanując ścieżkę klasy w systemie plików), a nie newInstance () wywołanej na klasie. Zastanawiam się, jak wyglądałyby statystyki, gdyby Class.forName ("misc.A") było wykonywane tylko raz poza pętlą for, tak naprawdę nie trzeba tego robić przy każdym wywołaniu pętli.
źródło
Tak, zawsze wolniej będzie tworzyć obiekt przez odbicie, ponieważ JVM nie może zoptymalizować kodu w czasie kompilacji. Więcej informacji można znaleźć w samouczkach dotyczących refleksji Sun / Java .
Zobacz ten prosty test:
źródło
Class.forName()
metodę lookup ( ) od instancjacji (newInstance ()), ponieważ różnią się one znacznie w charakterystyce wydajności i czasami możesz uniknąć wielokrotnego wyszukiwania w dobrze zaprojektowanym systemie.Często możesz użyć Apache commons BeanUtils lub PropertyUtils, które przeprowadzają introspekcję (w zasadzie buforują metadane o klasach, więc nie zawsze muszą używać odbicia).
źródło
Myślę, że zależy to od tego, jak lekka / ciężka jest metoda docelowa. jeśli metoda docelowa jest bardzo lekka (np. getter / setter), może być 1 ~ 3 razy wolniejsza. jeśli metoda docelowa zajmie około 1 milisekundy lub więcej, wydajność będzie bardzo zbliżona. oto test, który przeprowadziłem z Javą 8 i reflektazmem :
Pełny kod testu jest dostępny na GitHub: ReflectionTest.java
źródło