Jak napisano w JEP 280: Wskaż konkatenację ciągów :
Zmień statyczną
String
sekwencję kodu bajtowego -concatenation wygenerowaną przez program,javac
aby używaćinvokedynamic
wywołań funkcji biblioteki JDK. Umożliwi to przyszłą optymalizacjęString
konkatenacji bez konieczności dalszych zmian w kodzie bajtowym emitowanym przezjavac
.
Tutaj chcę zrozumieć, jakie jest użycie invokedynamic
wywołań i czym różni się konkatenacja kodu bajtowego invokedynamic
?
java
string
string-concatenation
java-9
invokedynamic
Mohit Tyagi
źródło
źródło
Odpowiedzi:
„Stary” sposób daje wiele
StringBuilder
zorientowanych operacji. Rozważ ten program:Jeśli skompilujemy to z JDK 8 lub wcześniejszym, a następnie użyjemy
javap -c Example
do wyświetlenia kodu bajtowego, zobaczymy coś takiego:Jak widać, tworzy a
StringBuilder
i używaappend
. Jest to znane jako dość nieefektywne, ponieważ domyślna pojemność wbudowanego buforaStringBuilder
wynosi tylko 16 znaków i nie ma możliwości, aby kompilator wiedział, aby przydzielić więcej z wyprzedzeniem, więc w końcu musi ponownie przydzielić. To także kilka wywołań metod. (Należy jednak pamiętać, że JVM może czasami wykrywać i przepisywać te wzorce wywołań, aby były bardziej wydajne).Spójrzmy, co generuje Java 9:
Ojej, ale to krócej. :-) Wykonuje pojedyncze wywołanie do
makeConcatWithConstants
fromStringConcatFactory
, które mówi w swoim Javadoc:źródło
+=
w pętli for. Powiedziałem im, że to zależy, ale nie zapominajmy, że kiedyś mogą znaleźć lepszy sposób na połączenie concat. Kluczowa linia jest tak naprawdę przedostatnią linią:So by being smart, you have caused a performance hit when Java got smarter than you.
invokedynamic
umożliwia wybranie różnych strategii konkatenacji w czasie wykonywania i powiązanie ich przy pierwszym wywołaniu, bez narzutu wywołania metody i tabeli wysyłania przy każdym wywołaniu; więcej w artykule Nicolai tutaj oraz w JEP .Object
, ale wtedy musiałbyś opakować wszystkie prymitywy ... (które Nicolai opisuje w swoim doskonałym artykule, przy okazji)String.concat(String)
metody, której implementacja tworzy lokalną tablicę wynikowego ciągu. Zaleta staje się dyskusyjna, gdy musimy odwołaćtoString()
się do dowolnych obiektów. Podobnie, podczas wywoływania metody akceptującej tablicę, obiekt wywołujący musi utworzyć i wypełnić tablicę, co zmniejsza ogólną korzyść. Ale teraz nie ma to znaczenia, ponieważ nowe rozwiązanie jest w zasadzie tym, co rozważałeś, z wyjątkiem tego, że nie ma narzutu związanego z boksowaniem, nie wymaga tworzenia tablicy, a zaplecze może generować zoptymalizowane procedury obsługi dla określonych scenariuszy.Zanim przejdziemy do szczegółów
invokedynamic
implementacji używanej do optymalizacji konkatenacji ciągów, moim zdaniem, należy się trochę zapoznać z czym jest wywoływana dynamika i jak z niej korzystać?Prawdopodobnie spróbuję przeprowadzić Cię przez te wszystkie zmiany ze zmianami, które zostały wprowadzone w celu implementacji optymalizacji konkatenacji ciągów.
Definiowanie metody ładowania początkowego: - W Javie9 metody ładowania początkowego dla
invokedynamic
witryn wywołujących obsługują przede wszystkim konkatenację ciągówmakeConcat
imakeConcatWithConstants
zostały wprowadzone wraz zStringConcatFactory
implementacją.Użycie invokedynamic stanowi alternatywę dla wyboru strategii tłumaczenia do czasu wykonania. Strategia tłumaczenia używana w programie
StringConcatFactory
jest podobna do tej,LambdaMetafactory
która została wprowadzona w poprzedniej wersji Java. Dodatkowo jednym z celów JEP wspomnianego w pytaniu jest dalsze rozszerzenie tych strategii.Specifying Constant Pool Entries : - Są to dodatkowe statyczne argumenty
invokedynamic
instrukcji inne niż (1)MethodHandles.Lookup
obiekt będący fabryką do tworzenia uchwytów metod w kontekścieinvokedynamic
instrukcji, (2)String
obiekt, nazwa metody wymieniona w wywołaniu dynamicznym site i (3)MethodType
obiekt, rozpoznana sygnatura typu witryny wywołania dynamicznego.Podczas łączenia kodu są już linki. W czasie wykonywania metoda bootstrap działa i łączy w rzeczywistym kodzie wykonującym konkatenację. Przepisuje
invokedynamic
wywołanie odpowiedniminvokestatic
wywołaniem. Spowoduje to załadowanie stałego ciągu z puli stałej, a statyczne argumenty metody ładowania początkowego są wykorzystywane do przekazywania tych i innych stałych bezpośrednio do wywołania metody ładowania początkowego.Korzystanie z instrukcji invokedynamic : - Zapewnia to ułatwienia dla leniwego łączenia, zapewniając środki do jednorazowego załadowania docelowego wywołania podczas początkowego wywołania. Konkretnym pomysłem na optymalizację jest tutaj zastąpienie całego
StringBuilder.append
tańca prostyminvokedynamic
wywołaniemjava.lang.invoke.StringConcatFactory
, które zaakceptuje wartości wymagające konkatenacji.Propozycja Indify String Concatenation przedstawia na przykładzie benchmarking aplikacji z Java9, gdzie skompilowano podobną metodę, jak współdzielona przez @TJ Crowder, a różnica w kodzie bajtowym jest dość widoczna między różnymi implementacjami.
źródło
Dodam tutaj trochę szczegółów. Główną częścią do uzyskania jest to, że sposób łączenia ciągów znaków jest decyzją wykonawczą, a nie kompilacją . W ten sposób może się zmienić, co oznacza, że skompilowałeś swój kod raz z java-9 i może zmienić podstawową implementację w dowolny sposób, bez potrzeby ponownej kompilacji.
Po drugie, w tej chwili są
6 possible strategies for concatenation of String
:Można wybrać jedną z nich za pomocą parametru:
-Djava.lang.invoke.stringConcat
. Zauważ, żeStringBuilder
nadal jest to opcja.źródło