日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Java自動(dòng)裝箱性能

Java 的基本數(shù)據(jù)類型(int、double、 char)都不是對(duì)象。但由于很多Java代碼需要處理的是對(duì)象(Object),Java給所有基本類型提供了包裝類(Integer、Double、Character)。有了自動(dòng)裝箱,你可以寫如下的代碼

西烏珠穆沁網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)成立于2013年到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。

 
 
  1. Character boxed = 'a';
  2. char unboxed = boxed;

編譯器自動(dòng)將它轉(zhuǎn)換為

 
 
  1. Character boxed = Character.valueOf('a');
  2. char unboxed = boxed.charValue();

然而,Java虛擬機(jī)不是每次都能理解這類過程,因此要想得到好的系統(tǒng)性能,避免不必要的裝箱很關(guān)鍵。這也是 OptionalInt 和 IntStream 等特殊類型存在的原因。在這篇文章中,我將概述JVM很難消除自動(dòng)裝箱的一個(gè)原因。

實(shí)例

例如,我們想要計(jì)算任意一類數(shù)據(jù)的編輯距離(Levenshtein距離),只要這些數(shù)據(jù)可以被看作一個(gè)序列:

 
 
  1. public class Levenshtein{
  2. private final Function> asList;
  3. public Levenshtein(Function> asList) {
  4. this.asList = asList;
  5. }
  6. public int distance(T a, T b) {
  7. // Wagner-Fischer algorithm, with two active rows
  8. List aList = asList.apply(a);
  9. List bList = asList.apply(b);
  10. int bSize = bList.size();
  11. int[] row0 = new int[bSize + 1];
  12. int[] row1 = new int[bSize + 1];
  13. for (int i = 0; i row0[i] = i;
  14. }
  15. for (int i = 0; i < bSize; ++i) {
  16. U ua = aList.get(i);
  17. row1[0] = row0[0] + 1;
  18. for (int j = 0; j < bSize; ++j) {
  19. U ub = bList.get(j);
  20. int subCost = row0[j] + (ua.equals(ub) ? 0 : 1);
  21. int delCost = row0[j + 1] + 1;
  22. int insCost = row1[j] + 1;
  23. row1[j + 1] = Math.min(subCost, Math.min(delCost, insCost));
  24. }
  25. int[] temp = row0;
  26. row0 = row1;
  27. row1 = temp;
  28. }
  29. return row0[bSize];
  30. }
  31. }

只要兩個(gè)對(duì)象可以被看作List,這個(gè)類就可以計(jì)算它們的編輯距離。如果想計(jì)算String類型的距離,那么就需要把String轉(zhuǎn)變?yōu)長(zhǎng)ist類型:

 
 
  1. public class StringAsList extends AbstractList{
  2. private final String str;
  3. public StringAsList(String str) {
  4. this.str = str;
  5. }
  6. @Override
  7. public Character get(int index) {
  8. return str.charAt(index); // Autoboxing! }
  9. @Override
  10. public int size() {
  11. return str.length();
  12. }
  13. }
  14. ...
  15. Levenshteinlev = new Levenshtein<>(StringAsList::new);
  16. lev.distance("autoboxing is fast", "autoboxing is slow"); // 4

由于Java泛型的實(shí)現(xiàn)方式,不能有List類型,所以要提供List和裝箱操作。(注:Java10中,這個(gè)限制也許會(huì)被取消。)

為了查看代碼熱路徑(hot path)上的結(jié)果,JMH集成了Linux工具perf,可以查看最熱代碼塊的JIT編譯結(jié)果。(要想查看匯編代碼,需要安裝hsdis插件。我在 AUR上提供了下載,Arch用戶可以直接獲取。)在JMH命令行添加 -prof perfasm 命令,就可以看到結(jié)果:

為了測(cè)試 distance() 方法的性能,需要做基準(zhǔn)測(cè)試。Java中微基準(zhǔn)測(cè)試很難保證準(zhǔn)確,但幸好OpenJDK提供了JMH(Java Microbenchmark Harness),它可以幫我們解決大部分難題。如果感興趣的話,推薦大家閱讀文檔和實(shí)例;它會(huì)很吸引你。以下是基準(zhǔn)測(cè)試:

 
 
  1. @State(Scope.Benchmark)
  2. public class MyBenchmark {
  3. private Levenshtein lev = new Levenshtein<>(StringAsList::new);
  4. @Benchmark
  5. @BenchmarkMode(Mode.AverageTime)
  6. @OutputTimeUnit(TimeUnit.NANOSECONDS)
  7. public int timeLevenshtein() {
  8. return lev.distance("autoboxing is fast", "autoboxing is slow");
  9. }
  10. }

(返回方法的結(jié)果,這樣JMH就可以做一些操作讓系統(tǒng)認(rèn)為返回值會(huì)被使用到,防止冗余代碼消除影響了結(jié)果。)

以下是結(jié)果:

 
 
  1. $ java -jar target/benchmarks.jar -f 1 -wi 8 -i 8
  2. # JMH 1.10.2 (released 3 days ago)
  3. # VM invoker: /usr/lib/jvm/java-8-openjdk/jre/bin/java
  4. # VM options:
  5. # Warmup: 8 iterations, 1 s each
  6. # Measurement: 8 iterations, 1 s each
  7. # Timeout: 10 min per iteration
  8. # Threads: 1 thread, will synchronize iterations
  9. # Benchmark mode: Average time, time/op
  10. # Benchmark: com.tavianator.boxperf.MyBenchmark.timeLevenshtein
  11. # Run progress: 0.00% complete, ETA 00:00:16
  12. # Fork: 1 of 1
  13. # Warmup Iteration 1: 1517.495 ns/op
  14. # Warmup Iteration 2: 1503.096 ns/op
  15. # Warmup Iteration 3: 1402.069 ns/op
  16. # Warmup Iteration 4: 1480.584 ns/op
  17. # Warmup Iteration 5: 1385.345 ns/op
  18. # Warmup Iteration 6: 1474.657 ns/op
  19. # Warmup Iteration 7: 1436.749 ns/op
  20. # Warmup Iteration 8: 1463.526 ns/op
  21. Iteration 1: 1446.033 ns/op
  22. Iteration 2: 1420.199 ns/op
  23. Iteration 3: 1383.017 ns/op
  24. Iteration 4: 1443.775 ns/op
  25. Iteration 5: 1393.142 ns/op
  26. Iteration 6: 1393.313 ns/op
  27. Iteration 7: 1459.974 ns/op
  28. Iteration 8: 1456.233 ns/op
  29. Result "timeLevenshtein":
  30. 1424.461 ±(99.9%) 59.574 ns/op [Average]
  31. (min, avg, max) = (1383.017, 1424.461, 1459.974), stdev = 31.158
  32. CI (99.9%): [1364.887, 1484.034] (assumes normal distribution)
  33. # Run complete. Total time: 00:00:16
  34. Benchmark Mode Cnt Score Error Units
  35. MyBenchmark.timeLevenshtein avgt 8 1424.461 ± 59.574 ns/op

分析

為了查看代碼熱路徑(hot path)上的結(jié)果,JMH集成了Linux工具perf,可以查看最熱代碼塊的JIT編譯結(jié)果。(要想查看匯編代碼,需要安裝hsdis插件。我在 AUR上提供了下載,Arch用戶可以直接獲取。)在JMH命令行添加 -prof perfasm 命令,就可以看到結(jié)果:

 
 
  1. $ java -jar target/benchmarks.jar -f 1 -wi 8 -i 8 -prof perfasm
  2. ...
  3. cmp $0x7f,%eax
  4. jg 0x00007fde989a6148 ;*if_icmpgt
  5. ; - java.lang.Character::valueOf@3 (line 4570)
  6. ; - com.tavianator.boxperf.StringAsList::get@8 (line 14)
  7. ; - com.tavianator.boxperf.StringAsList::get@2; (line 5)
  8. ; - com.tavianator.boxperf.Levenshtein::distance@121 (line 32)
  9. cmp $0x80,%eax
  10. jae 0x00007fde989a6103 ;*aaload
  11. ; - java.lang.Character::valueOf @ 10 (line 4571)
  12. ; - com.tavianator.boxperf.StringAsList::get@8 (line 14)
  13. ; - com.tavianator.boxperf.StringAsList::get @ 2 (line 5)
  14. ; - com.tavianator.boxperf.Levenshtein::distance@121 (line 32)
  15. ...

輸出內(nèi)容很多,但上面的一點(diǎn)內(nèi)容就說明裝箱沒有被優(yōu)化。為什么要和0x7f/0×80的內(nèi)容做比較呢?原因在于Character.valueOf()的取值來源:

 
 
  1. private static class CharacterCache {
  2. private CharacterCache(){}
  3. static final Character cache[] = new Character[127 + 1];
  4. static {
  5. for (int i = 0; i < cache.length; i++)
  6. cache[i] = new Character((char)i);
  7. }
  8. }
  9. public static Character valueOf(char c) {
  10. if (c return CharacterCache.cache[(int)c];
  11. }
  12. return new Character(c);
  13. }

可以看出,Java語法標(biāo)準(zhǔn)規(guī)定前127個(gè)char的Character對(duì)象放在緩沖池中,Character.valueOf()的結(jié)果在其中 時(shí),直接返回緩沖池的對(duì)象。這樣做的目的是減少內(nèi)存分配和垃圾回收,但在我看來這是過早的優(yōu)化。而且它妨礙了其他優(yōu)化。JVM無法確定 Character.valueOf(c).charValue() == c,因?yàn)樗恢谰彌_池的內(nèi)容。所以JVM從緩沖池中取了一個(gè)Character對(duì)象并讀取它的值,結(jié)果得到的就是和 c 一樣的內(nèi)容。

解決方法

解決方法很簡(jiǎn)單:

 
 
  1. @ @ -11,7 +11,7 @ @ public class StringAsList extends AbstractList {
  2. @Override
  3. public Character get(int index) {
  4. - return str.charAt(index); // Autoboxing!
  5. + return new Character(str.charAt(index));
  6. }

@Override

用顯式的裝箱代替自動(dòng)裝箱,就避免了調(diào)用Character.valueOf(),這樣JVM就很容易理解代碼:

 
 
  1. private final char value;
  2. public Character(char value) {
  3. this.value = value;
  4. }
  5. public char charValue() {
  6. return value;
  7. }

雖然代碼中加了一個(gè)內(nèi)存分配,但JVM能理解代碼的意義,會(huì)直接從String中獲取char字符。性能提升很明顯:

 
 
  1. $ java -jar target/benchmarks.jar -f 1 -wi 8 -i 8
  2. ...
  3. # Run complete. Total time: 00:00:16
  4. Benchmark Mode Cnt Score Error Units
  5. MyBenchmark.timeLevenshtein avgt 8 1221.151 ± 58.878 ns/op

速度提升了14%。用 -prof perfasm 命令可以顯示,改進(jìn)以后是直接從String中拿到char值并在寄存器中比較的:

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 %r11d,%r10d
je 0x00007faa8d404792 ;*if_icmpne
; - java.lang.Character::equals@18 (line 4621)
; - com.tavianator.boxperf.Levenshtein::distance@137 (line 33)

總結(jié)

裝箱是HotSpot的一個(gè)弱項(xiàng),希望它能做到越來越好。它應(yīng)該多利用裝箱類型的語義,消除裝箱操作,這樣以上的解決辦法就沒有必要了。

以上的基準(zhǔn)測(cè)試代碼都可以在GitHub上訪問。


本文標(biāo)題:Java自動(dòng)裝箱性能
網(wǎng)站網(wǎng)址:http://www.5511xx.com/article/dhpgdep.html