In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
The main content of this article is to explain "how to use Java auto-packing performance". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to use the Java auto-packing feature.
The basic data types of Java (int, double, char) are not objects. But because much of the Java code needs to deal with objects (Object), Java provides wrapper classes (Integer, Double, Character) for all basic types. With automatic boxing, you can write the following code
Character boxed = 'a'; char unboxed = boxed
The compiler automatically converts it to
Character boxed = Character.valueOf ('a'); char unboxed = boxed.charValue ()
However, the Java virtual machine does not always understand such processes, so it is critical to avoid unnecessary packing in order to achieve good system performance. This is also the reason why special types such as OptionalInt and IntStream exist. In this article, I will outline one reason why it is difficult for JVM to eliminate autoboxing.
Example
For example, we want to calculate the editing distance (Levenshtein distance) for any type of data, as long as the data can be seen as a sequence:
Public class Levenshtein {private final Function > asList; public Levenshtein (Function > asList) {this.asList = asList;} public int distance (T a, T b) {/ / Wagner-Fischer algorithm, with two active rows List aList = asList.apply (a); List bList = asList.apply (b); int bSize = bList.size (); int [] row0 = new int [bSize + 1]; int [] row1 = new int [bSize + 1]; for (int I = 0; I row0 [I] = I } for (int I = 0; I < bSize; + + I) {U ua = aList.get (I); row1 [0] = row0 [0] + 1; for (int j = 0; j < bSize; + + j) {U ub = bList.get (j); int subCost = row0 [j] + (ua.equals (ub)? 0: 1); int delCost = row0 [j + 1] + 1; int insCost = row1 [j] + 1 Row1 [j + 1] = Math.min (subCost, Math.min (delCost, insCost));} int [] temp = row0; row0 = temp;} return row0 [bSize];}}
This class can calculate the editing distance of two objects as long as they can be treated as List. If you want to calculate the distance of type String, you need to convert String to type List:
Public class StringAsList extends AbstractList {private final String str; public StringAsList (String str) {this.str = str;} @ Override public Character get (int index) {return str.charAt (index); / / Autoboxing!} @ Override public int size () {return str.length ();}}. Levenshteinlev = new Levenshtein (StringAsList::new); lev.distance ("autoboxing is fast", "autoboxing is slow"); / / 4
Because of the way Java generics are implemented, there can be no List types, so List and boxing operations are provided. (note: in Java10, this restriction may be removed. )
To see the results on the code hot path (hot path), JMH integrates the Linux tool perf, which allows you to view the JIT compilation results of the hottest code blocks. To view the assembly code, you need to install the hsdis plug-in. I provide downloads on AUR, and Arch users can get them directly. Add the-prof perfasm command to the JMH command line, and you can see the result:
To test the performance of the distance () method, you need to do a benchmark test. Microbenchmarking in Java is difficult to guarantee accuracy, but fortunately OpenJDK provides JMH (Java Microbenchmark Harness), which can help us solve most of the problems. If you are interested, it is recommended that you read the documentation and examples; it will appeal to you. The following is the benchmark test:
@ State (Scope.Benchmark) public class MyBenchmark {private Levenshtein lev = new Levenshtein (StringAsList::new); @ Benchmark @ BenchmarkMode (Mode.AverageTime) @ OutputTimeUnit (TimeUnit.NANOSECONDS) public int timeLevenshtein () {return lev.distance ("autoboxing is fast", "autoboxing is slow");}}
Returns the result of the method so that JMH can do something to make the system think that the return value will be used to prevent redundant code from affecting the result. )
Here are the results:
$java- jar target/benchmarks.jar-f 1-wi 8-I 8 # JMH 1.10.2 (released 3 days ago) # VM invoker: / usr/lib/jvm/java-8-openjdk/jre/bin/java # VM options: # Warmup: 8 iterations, 1 s each # Measurement: 8 iterations, 1 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.tavianator.boxperf.MyBenchmark.timeLevenshtein # Run progress: 0.00% complete ETA 00:00:16 # Fork: 1 of 1 # Warmup Iteration 1: 1517.495 ns/op # Warmup Iteration 2: 1503.096 ns/op # Warmup Iteration 3: 1402.069 ns/op # Warmup Iteration 4: 1480.584 ns/op # Warmup Iteration 5: 1385.345 ns/op # Warmup Iteration 6: 1474.657 ns/op # Warmup Iteration 7: 1436.749 ns/op # Warmup Iteration 8: 1463.526 ns/op Iteration 1: 1446.033 ns/op Iteration 2: 1420.199 ns/op Iteration 3 : 1383.017 ns/op Iteration 4: 1443.775 ns/op Iteration 5: 1393.142 ns/op Iteration 6: 1393.313 ns/op Iteration 7: 1459.974 ns/op Iteration 8: 1456.233 ns/op Result "timeLevenshtein": 1424.461 ±(99.9) 59.574 ns/op [Average] (min Avg, max) = (1383.017, 1424.461, 1459.974), stdev = 31.158 CI (99.9%): [1364.887, 1484.034] (assumes normal distribution) # Run complete. Total time: 00:00:16 Benchmark Mode Cnt Score Error Units MyBenchmark.timeLevenshtein avgt 8 1424.461 ±59.574 ns/op
Analysis.
To see the results on the code hot path (hot path), JMH integrates the Linux tool perf, which allows you to view the JIT compilation results of the hottest code blocks. To view the assembly code, you need to install the hsdis plug-in. I provide downloads on AUR, and Arch users can get them directly. Add the-prof perfasm command to the JMH command line, and you can see the result:
$java-jar target/benchmarks.jar-f 1-wi 8-I 8-prof perfasm... Cmp $0x7fcmp% eax jg 0x00007fde989a6148; * if_icmpgt;-java.lang.Character::valueOf@3 (line 4570);-com.tavianator.boxperf.StringAsList::get@8 (line 14);-com.tavianator.boxperf.StringAsList::get@2; (line 5);-com.tavianator.boxperf.Levenshtein::distance@121 (line 32) cmp $0x80 jae 0x00007fde989a6103% Eax jae 0x00007fde989a6103; * aaload;-java.lang.Character::valueOf@ 10 (line 4571) -com.tavianator.boxperf.StringAsList::get@8 (line 14);-com.tavianator.boxperf.StringAsList::get@ 2 (line 5);-com.tavianator.boxperf.Levenshtein::distance@121 (line 32).
There is a lot of output, but the above shows that the packing has not been optimized. Why compare it with the content of 0x7f/0 × 80? The reason is that the value of Character.valueOf () comes from:
Private static class CharacterCache {private CharacterCache () {} static final Character cache [] = new Character [127th + 1]; static {for (int I = 0; I < cache.length; I +) cache [I] = new Character ((char) I);}} public static Character valueOf (char c) {if (c return CharacterCache.cache [(int) c];} return new Character (c);}
As you can see, the Java syntax standard states that the Character objects of the first 127th char are placed in the buffer pool, and when the result of Character.valueOf () is in it, the objects of the buffer pool are returned directly. The goal is to reduce memory allocation and garbage collection, but in my opinion this is premature optimization. And it interferes with other optimizations. JVM cannot determine Character.valueOf (c). CharValue () = = c because it does not know the contents of the buffer pool. So JVM takes a Character object from the buffer pool and reads its value, and the result is the same content as c.
Solution method
The solution is simple:
@ @-11 index 7 + 11 public class StringAsList extends AbstractList {@ Override public Character get (int index) {- return str.charAt (index); / / Autoboxing! + return new Character (str.charAt (index));}
@ Override
Using explicit boxing instead of autoboxing avoids calling Character.valueOf (), so that JVM can easily understand the code:
Private final char value; public Character (char value) {this.value = value;} public char charValue () {return value;}
Although a memory allocation is added to the code, JVM understands the meaning of the code and fetches char characters directly from String. The performance improvement is significant:
$java-jar target/benchmarks.jar-f 1-wi 8-I 8... # Run complete. Total time: 00:00:16 Benchmark Mode Cnt Score Error Units MyBenchmark.timeLevenshtein avgt 8 1221.151 ±58.878 ns/op
The speed has increased by 14%. The-prof perfasm command shows that after the improvement, the char value is taken directly from the String and compared in the register:
Movzwl 0x10 (% rsi,%rdx,2),% r11d; * caload
;-java.lang.String::charAt@27 (line 648)
;-com.tavianator.boxperf.StringAsList::get@9 (line 14)
;-com.tavianator.boxperf.StringAsList::get @ 2 (line 5)
;-com.tavianator.boxperf.Levenshtein::distance@121 (line 32)
Cmp% r11ddepartment% r10d
Je 0x00007faa8d404792; * if_icmpne
;-java.lang.Character::equals@18 (line 4621)
;-com.tavianator.boxperf.Levenshtein::distance@137 (line 33)
At this point, I believe you have a deeper understanding of "how to use the Java auto-packing performance". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.