成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java 8 的 JVM 有多快?Fork-Join 性能基準測試

Euphoria / 1252人閱讀

摘要:這減輕了手動重復執(zhí)行相同基準測試的痛苦,并簡化了獲取結(jié)果的流程。處理項目的代碼并從標有注釋的方法處生成基準測試程序。用和運行該基準測試得到以下結(jié)果。同時,和的基線測試結(jié)果也有略微的不同。

Java 8 已經(jīng)發(fā)布一段時間了,許多開發(fā)者已經(jīng)開始使用 Java 8。本文也將討論最新發(fā)布在 JDK 中的并發(fā)功能更新。事實上,JDK 中已經(jīng)有多處java.util.concurrent 改動,但本文重點將是 Fork-Join 框架的改進。我們將討論一點 Fork-Join,然后實現(xiàn)一個簡單的基準測試以比較 FJ 在 Java 7 和Java 8 中的性能。

你可能對Fork/Join在意的地方

ForkJoin 是一個通常用于并行計算遞歸任務的框架。它最早被引入Java 7 中,從那時起它就能很好地完成目標任務。原因在于,許多大型任務本質(zhì)上都可以遞歸表示。

以最有名的 MapReduce 編程為例:對一篇文章中不同詞的出現(xiàn)次數(shù)進行統(tǒng)計。很顯然,可以將文檔分為很多部分,逐項地記錄字數(shù),最后再合并成結(jié)果。誠然,F(xiàn)orkJoin其實是 MapReduce 基本法則的一種實現(xiàn),區(qū)別在于,所有的 worker 都是同一個虛擬機中的線程,而不是一組機器。

ForkJoin 框架的核心部分是 ForkJoinPool ,它是一個 ExecutorService, 能夠接收異步任務,返回Future對象,因此可用于跟蹤執(zhí)行中的計算狀態(tài)。

使 ForkJoinPool 不同于其他 ExecutorServices 的是,在當下并不執(zhí)行任務的工作線程會檢查其伙伴的工作狀態(tài),并向他們借取任務。這種技術(shù)稱為 work-stealing 。那么,work-stealing 有什么妙用呢?

work-stealing 是一種分散式的工作量管理方法,無需將工作單元分配給所有可用的工作線程,而是每個線程自己管理其任務隊列。關(guān)鍵在于高效地管理這些隊列。

關(guān)于讓每個工作進程處理自己的隊列,有兩個主要問題:

外部提交的任務去哪里了?

我們怎樣組織 work-stealing 以有效訪問隊列

本質(zhì)上來說,在執(zhí)行大型任務時,外部提交任務和由工作線程創(chuàng)建的任務之間區(qū)別不大。他們都有類似的執(zhí)行要求并提供結(jié)果。然而,運作方式是不同的。最主要的區(qū)別在于由工作進程創(chuàng)建的任務可以被竊取。這意味著即便被放進了一個工作進程的任務隊列中,他們?nèi)钥赡鼙黄渌ぷ鬟M程執(zhí)行。

ForkJoin 框架處理它的方法很簡單,每個工作線程都有2個任務隊列,一個用于外部任務,另一個用于實現(xiàn)竊取工作進程的運作。當外部提交任務時,會將任務添加至隨機的工作隊列中。當一個任務被分為更小的任務時,工作線程將他們添加到自己的任務隊列中,并希望其他工作線程來幫忙。

竊取任務的想法基于以下事實:工作線程在它任務隊列末尾添加任務。在正常的執(zhí)行過程中,每個工作線程試著去從任務隊列的隊首拿任務,當其個人隊列的任務為空時,這一操作就會失敗,轉(zhuǎn)而竊取別的工作線程的任務隊列末尾的任務。這有效避免了多數(shù)任務隊列的互鎖問題,提高了性能。

另一個使 ForkJoin 池工作更快的訣竅是當一個工作線程竊取任務時,它留下了它在哪里取得任務的線索,這樣原始的工作線程可以找到它并且?guī)椭摴ぷ骶€程,因此父任務的的工作進展會更快。

總而言之,這是一套極其復雜的系統(tǒng),需要大量的背景知識使其順利運行。并且,系統(tǒng)的屬性和性能與具體實現(xiàn)的方式關(guān)系很大。因此筆者懷疑,若不進行重大的重構(gòu),系統(tǒng)會徹底改變。

Java 7 中 ForkJoin 有什么問題?

在 Java 7 中引入 ForkJoin 框架之后,它運行良好。然而它并沒有停止進步。在 Java 8 的并發(fā)性更新中, ForkJoin 得到改善。從這次的 Java 增強方案中,我們可以了解改善的內(nèi)容。

增加了 ForkJoinPools 的功能并提高其性能,使其應用在用戶希望的日益廣泛的應用中,且效率更高。新特性包括對最適于 IO-bound 使用的 completion-based 設(shè)計的支持等。

另一個消息來源當然是與改進作者的對話,例如,Doug Lea 早前曾提到的更新有:

當大量的用戶提交大量任務時,吞吐量能大幅度提高。其原理是將外部提交者與工作線程相似地對待——均使用隨機任務隊列和竊取任務。當所有任務都為異步,且被提交至 pool 而不是 forked 時,能極大地提高吞吐量。

然而找出究竟什么被改變了、哪些場景被影響了并不簡單。因此,讓我們換一種方式解決。筆者會創(chuàng)建一個基準測試程序以模仿簡單的 ForkJoin 計算,并測量 ForkJoin 處理任務與單個線程依次完成任務各自所需時間,希望這種方法能幫我們找出改善的具體內(nèi)容。

Java 8 和 Java 7 性能的比較

筆者創(chuàng)建了一個基準測試程序以探索 Java 7 和 Java 8 之間的區(qū)別是否真的明顯。如果你想查看源碼,或者親自嘗試,這里是其 Github repo 。

由于Oracle工程師的努力,OpenJDK現(xiàn)在已經(jīng)包含 Java Microbenchmark Harness (JMH)項目,該項目專用于創(chuàng)建基準測試程序,且不容易出現(xiàn)常見的微基準測試問題與錯誤。

JMH 還附帶了 Maven 原型項目。因此,將一切設(shè)置好其實很簡單。

  org.openjdk.jmh
  mh-core
  0.4.1
    

在寫本文時,JMH core 的最新版本是 0.4.1 ,包括了 @Param 注釋,可用一系列的參數(shù)化輸入運行基準測試程序。這減輕了手動重復執(zhí)行相同基準測試的痛苦,并簡化了獲取結(jié)果的流程。

現(xiàn)在,每個基準測試迭代會獲得自己的 ForkJoinPool 實例,這也減少了常用 ForkJoinPool 實例化在 Java 8 與其之前版本中的區(qū)別。

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 20, time = 3, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Benchmark)
public class FJPBenchmark {

  @Param({ "200", "400", "800", "1600"})
  public int N;

  public List tasks;
  public ForkJoinPool pool = new ForkJoinPool();

  @Setup
  public void init() {
    Random r = new Random();
    r.setSeed(0x32106234567L);
    tasks = new ArrayList(N * 3);

    for (int i = 0; i < N; i++) {
      tasks.add(new Sin(r.nextDouble()));
      tasks.add(new Cos(r.nextDouble()));
      tasks.add(new Tan(r.nextDouble()));
    }
  }

  @GenerateMicroBenchmark
  public double forkJoinTasks() {
    for (RecursiveTask task : tasks) {
      pool.submit(task);
    }
    double sum = 0;
    Collections.reverse(tasks);
    for (RecursiveTask task : tasks) {
      sum += task.join();
    }
    return sum;
  }

  @GenerateMicroBenchmark
  public double computeDirectly() {
    double sum = 0;
    for (RecursiveTask task : tasks) {
      sum += ((DummyComputableThing) task).dummyCompute();
    }
    return sum;
  }
}

Sin 、CosTanRecursiveTask 的實例,實際上 Sin 和 Cos 并不遞歸,但會分別計算 Math.sin(input)Math.cos(input) 的值 。Tan 的任務實際上會遞歸為一組 Sin 和 Cos ,并返回兩者的除法結(jié)果。

JMH 處理項目的代碼并從標有 @GenerateMicroBenchmark 注釋的方法處生成基準測試程序。你在該類上方看到的其他注釋指定了基準測試的選項:迭代次數(shù),計入最終結(jié)果的迭代次數(shù),是否 fork 另一個 JVM 進程用于基準測試以及測量哪些值。測量值可以是代碼的吞吐量,或這些方法在一段時間內(nèi)的執(zhí)行次數(shù)。

@Param 指定運行基準測試程序時幾個輸入的大小??偠灾?,JMH非常簡單,創(chuàng)建基準測試程序不需要手動處理迭代、定時或整理結(jié)果。

用 Java 7 和 8 運行該基準測試得到以下結(jié)果。筆者分別使用的是1.7.0_40 and 1.8.0.版本。

shelajev@shrimp ~/repo/blogposts/fork-join-blocking-perf ? java -version
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)

Benchmark                   (N)   Mode   Samples         Mean   Mean error    Units
o.s.FJPB.computeDirectly    200  thrpt        20       27.890        0.306   ops/ms
o.s.FJPB.computeDirectly    400  thrpt        20       14.046        0.072   ops/ms
o.s.FJPB.computeDirectly    800  thrpt        20        6.982        0.043   ops/ms
o.s.FJPB.computeDirectly   1600  thrpt        20        3.481        0.122   ops/ms
o.s.FJPB.forkJoinTasks      200  thrpt        20       11.530        0.121   ops/ms
o.s.FJPB.forkJoinTasks      400  thrpt        20        5.936        0.126   ops/ms
o.s.FJPB.forkJoinTasks      800  thrpt        20        2.931        0.027   ops/ms
o.s.FJPB.forkJoinTasks     1600  thrpt        20        1.466        0.012   ops/ms


shelajev@shrimp ~/repo/blogposts/fork-join-blocking-perf ? java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)

Benchmark                   (N)   Mode   Samples         Mean   Mean error    Units
o.s.FJPB.computeDirectly    200  thrpt        20       27.680        2.050   ops/ms
o.s.FJPB.computeDirectly    400  thrpt        20       13.690        0.994   ops/ms
o.s.FJPB.computeDirectly    800  thrpt        20        6.783        0.548   ops/ms
o.s.FJPB.computeDirectly   1600  thrpt        20        3.364        0.304   ops/ms
o.s.FJPB.forkJoinTasks      200  thrpt        20       15.868        0.291   ops/ms
o.s.FJPB.forkJoinTasks      400  thrpt        20        8.060        0.222   ops/ms
o.s.FJPB.forkJoinTasks      800  thrpt        20        4.006        0.024   ops/ms
o.s.FJPB.forkJoinTasks     1600  thrpt        20        1.968        0.043   ops/ms

為了便于查看結(jié)果,下面以圖表形式進行展示。

我們可以看到 JDK 7 與 8 間的基線結(jié)果(直接用同一線程運行程序的吞吐量)差異并不大。然而,若加入管理遞歸任務的時間,使用 ForkJoin 來執(zhí)行,則 Java 8 的速度更快。這個簡單的基準測試表明,在最新版的 Java 中,管理 ForkJoin 任務的效率有了 35% 左右的性能提高。

基線和 FJ 計算之間的結(jié)果差異是因為我們刻意創(chuàng)建的遞歸任務非常單薄。該任務實質(zhì)上只是調(diào)用一個優(yōu)化后的數(shù)學類。因此,直接進行數(shù)學運算會快得多。一個更強壯的任務必將改變這一情況,但是它們會減輕 ForkJoin 管理的開銷,而這是我們起初就想測量的目標。不過,一般而言,執(zhí)行遞歸任務比多次執(zhí)行同個方法調(diào)用要高效得多。

同時,Java 7 和 Java 8 的基線測試結(jié)果也有略微的不同。這個差異是可以忽視的,但很可能不是因為 Java 7 和 8 中數(shù)學類的實現(xiàn)差異造成的。而是一個測量假象,JMH 努力抵消卻還是無法避免。

免責聲明:當然,這些結(jié)果是模擬所得的,你應該持保留態(tài)度。然而,除了討論 Java 性能,筆者也想展示 JMH 創(chuàng)建基準測試程序是如何簡單,且能避免一些常見基準測試問題,比如沒有提前預熱 JVM 。如果基準測試本身存在缺陷,熱身也無濟于事,但是肯定還是有所裨益。因此,如果你看到以上代碼中的邏輯缺陷,請一定告訴筆者。

總結(jié):

首先,F(xiàn)orkJoinPool, ForkJoinPool.WorkQueue 和ForkJoinTask 類的源碼并不容易閱讀,它包含許非安全原理,因此你可能沒法在15分鐘完全理解ForkJoin 框架。

然而,這些類的文檔豐富,并且包含許多內(nèi)部注釋。它也可能學習挖掘JDK最有趣的地方。

另一個相關(guān)的發(fā)現(xiàn)是 ForkJoinPool 在 Java8 中的性能更好,至少在一些用例中是這樣的。雖然筆者不能精確地描述這背后的原因,但如果我在代碼中用到 ForkJoin ,我一定會升級 Java 版本。

原文地址:http://zeroturnaround.com/rebellabs/is-java-8-the-fastest-jvm-ever-performance-benchmarking-of-fork-join/ 本文作者:Oleg Shelajev 系 OneAPM 工程師編譯整理。

OneAPM 為您提供端到端的 Java 應用性能解決方案,我們支持所有常見的 Java 框架及應用服務器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監(jiān)控從來沒有如此簡單。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方技術(shù)博客。
本文轉(zhuǎn)自 OneAPM 官方博客

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65330.html

相關(guān)文章

  • 【PHP7源碼分析】PHP7到底多快,基準測試與特性分析告訴你

    摘要:我們修改上面代碼,再來看下返回值類型限制的情況運行結(jié)果這段代碼我們額外聲明了返回值的類型為型。對函數(shù)返回值的聲明做了擴充,可以定義其返回值為,無論是否開啟嚴格模式,只要函數(shù)中有以外的其他語句都會報錯。 順風車運營研發(fā)團隊 王坤 發(fā)表至21CTO公眾號(https://mp.weixin.qq.com/s/ph...) showImg(https://segmentfault.c...

    Towers 評論0 收藏0
  • Flink 源碼解析 —— 深度解析 Flink 是如何管理好內(nèi)存?

    摘要:減少垃圾收集壓力因為所有長生命周期的數(shù)據(jù)都是在的管理內(nèi)存中以二進制表示的,所以所有數(shù)據(jù)對象都是短暫的,甚至是可變的,并且可以重用。當然,并不是唯一一個基于且對二進制數(shù)據(jù)進行操作的數(shù)據(jù)處理系統(tǒng)。 showImg(https://segmentfault.com/img/remote/1460000020044119?w=1280&h=853); 前言 如今,許多用于分析大型數(shù)據(jù)集的開源系...

    Edison 評論0 收藏0
  • Java HotSpot VM中JIT編譯

    摘要:如同其它虛擬機,虛擬機為字節(jié)碼提供了一個運行時環(huán)境。編譯是一個混合模式的虛擬機,也就是說它既可以解釋字節(jié)碼,又可以將代碼編譯為本地機器碼以更快的執(zhí)行。解決此問題一般是在進程啟動后,對代碼進行預熱以使它們被強制編譯。 Java HotSpot虛擬機是Oracle收購Sun時獲得的,JVM和開源的OpenJDK都是以此虛擬機為基礎(chǔ)發(fā)展的。如同其它虛擬機,HotSpot虛擬機為字節(jié)碼提供了一...

    caozhijian 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<