Fork / Join框架vs并行流vs.ExecutorService:最终的Fork / Join基准
Fork / Join框架在不同配置下如何工作?
就像即將上映的《星球大戰(zhàn)》一樣,圍繞Java 8并行性的批評也充滿了興奮。 并行流的語法糖帶來了一些炒作,就像我們在預(yù)告片中看到的新型光劍一樣。 現(xiàn)在,有了許多使用Java進行并行處理的方法,我們希望了解性能優(yōu)勢和并行處理的危險。 經(jīng)過260多次測試運行,從數(shù)據(jù)中獲得了一些新見解,我們希望在本文中與您分享。
分叉/加入:分叉喚醒
ExecutorService與Fork / Join Framework與并行流
很久以前,在一個遙遠的星系中……。 我的意思是,大約10年前,并發(fā)只能通過3rd party庫在Java中使用。 然后出現(xiàn)了Java 5,并在語言中引入了java.util.concurrent庫,該庫受到Doug Lea的強烈影響。 ExecutorService可用并為我們提供了處理線程池的直接方法。 當然,java.util.concurrent一直在發(fā)展,并且在Java 7中,在ExecutorService線程池的基礎(chǔ)上引入了Fork / Join框架。 借助Java 8流,我們已經(jīng)為使用Fork / Join提供了一種簡單的方法,對于許多開發(fā)人員而言,它仍然有些神秘。 讓我們找出它們之間的比較。
我們完成了兩項任務(wù),一項是CPU密集型任務(wù),另一項是IO密集型任務(wù),并使用相同的基本功能測試了4種不同的方案。 另一個重要因素是我們用于每個實現(xiàn)的線程數(shù),因此我們也對其進行了測試。 我們使用的機器有8個內(nèi)核 ,因此我們有4、8、16和32個線程的變種,以大致了解結(jié)果的發(fā)展方向。 對于每個任務(wù),我們還嘗試了一個單線程解決方案,您不會在圖中看到它,因為執(zhí)行起來需要花費更長的時間。 要詳細了解測試的運行方式,您可以查看下面的基礎(chǔ)部分。 現(xiàn)在,讓我們開始吧。
索引580萬行文本的6GB文件
在此測試中,我們生成了一個巨大的文本文件,并為索引過程創(chuàng)建了類似的實現(xiàn)。 結(jié)果如下所示:
文件索引測試結(jié)果
**單線程執(zhí)行:176,267毫秒,或?qū)⒔?分鐘。
**請注意,圖形開始于20000毫秒。
1.更少的線程將使CPU處于未使用狀態(tài),太多的線程將增加開銷
您在圖表中注意到的第一件事是結(jié)果開始采用的形狀–您僅從這4個數(shù)據(jù)點就可以了解每個實現(xiàn)的行為。 臨界點在8到16個線程之間,因為某些線程在文件IO中處于阻塞狀態(tài),并且添加比內(nèi)核更多的線程有助于更好地利用它們。 當有32個線程進入時,由于額外的開銷,性能會變差。
比亞軍快1秒:直接使用Fork / Join
除了語法糖(lambdas!我們沒有提到lambdas),我們已經(jīng)看到并行流的性能比Fork / Join和ExecutorService實現(xiàn)的更好。 6GB的文本在24.33秒內(nèi)被索引。 您可以在這里信任Java來提供最佳結(jié)果。
3.但是...并行流也表現(xiàn)最差:唯一的變化超過了30秒
這再次提醒了并行流如何使您減速。 假設(shè)這種情況發(fā)生在已經(jīng)運行多線程應(yīng)用程序的計算機上。 在可用線程數(shù)量較少的情況下,直接使用Fork / Join實際上比通過并行流要好-5秒的差異,將這兩個線程進行比較時大約要付出18%的代價。
4.不要使用圖片中帶有IO的默認池大小
當為并行流使用默認池大小時,計算機上相同數(shù)量的內(nèi)核(此處為8個內(nèi)核)比16個線程版本的性能差了近2秒。 如果使用默認池大小,則要加收7%的罰款。 發(fā)生這種情況的原因與阻塞IO線程有關(guān)。 還有更多的等待正在進行,因此引入更多的線程可以使我們更多地使用所涉及的CPU內(nèi)核,而其他線程則需要等待調(diào)度而不是空閑。
如何更改并行流的默認Fork / Join池大小? 您可以使用JVM參數(shù)更改常見的Fork / Join池大小:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=16(默認情況下,所有Fork / Join任務(wù)都使用一個公共靜態(tài)池,其大小與內(nèi)核數(shù)相同。這樣做的好處是,通過在不使用期間為其他任務(wù)回收線程,從而減少了資源使用。)
或者…您可以使用此技巧并在自定義的Fork / Join池中運行并行流。 這將覆蓋通用的Fork / Join池的默認用法,并允許您使用自己設(shè)置的池。 偷偷摸摸。 在測試中,我們使用了公共池。
5.單線程性能比最佳結(jié)果差7.25倍
并行性提供了7.25倍的改進,并且考慮到該機器具有8個核心,因此非常接近理論上的8倍預(yù)測! 我們可以將其余的歸因于開銷。 話雖如此,即使我們測試的最慢的并行性實現(xiàn)(這次是具有4個線程的并行流(30.24sec))的性能也比單線程解決方案(176.27sec)好5.8倍。
檢查數(shù)字是否為質(zhì)數(shù)
在下一輪測試中,我們完全消除了IO,并檢查了確定一個真正大的數(shù)字是否為素數(shù)所需的時間。 多大? 19位數(shù)字 。 1,530,692,068,127,007,263,或換句話說:五百一十九億四千三百四十四萬億三千三百八十億八千八百三十八萬三千三百三十三。 啊,讓我呼吸一下。 無論如何,除了運行到平方根以外,我們沒有使用任何優(yōu)化,因此即使我們的大數(shù)沒有除以2只是為了延長處理時間,我們也檢查了所有偶數(shù)。 劇透警告:這是首要的,因此每個實現(xiàn)都運行相同數(shù)量的計算。
結(jié)果是這樣的:
素數(shù)測試結(jié)果
**單線程執(zhí)行:118,127毫秒,或?qū)⒔?分鐘。
**請注意,圖形開始于20000毫秒
1. 8和16個線程之間的差異較小
與IO測試不同,這里沒有IO調(diào)用,因此8個線程和16個線程的性能基本相似,除了Fork / Join解決方案。 實際上,我們已經(jīng)進行了多組測試,以確保由于這種“異常”而在這里獲得良好的結(jié)果,但事實證明,一次又一次地非常相似。 我們很高興在下面的評論部分中聽到您對此的想法。
2.所有方法的最佳結(jié)果相似
我們看到所有實現(xiàn)都共享大約28秒的相似最佳結(jié)果。 無論我們嘗試采用哪種方法,結(jié)果都是一樣的。 這并不意味著我們對使用哪種方法都無所謂。 查看下一個見解。
3.并行流比其他實現(xiàn)更好地處理線程重載
這是更有趣的部分。 通過該測試,我們再次看到運行16個線程的最高結(jié)果來自使用并行流。 而且,在此版本中,使用并行流是線程號的所有變體的一個好方法。
4.單線程性能比最佳結(jié)果低4.2倍
另外,在運行計算密集型任務(wù)時使用并行性的好處幾乎比使用文件IO的IO測試要差2倍。 這是有道理的,因為它是CPU密集型測試,與之前的測試不同,我們可以通過減少內(nèi)核等待被IO阻塞的線程的時間來獲得額外的好處。
結(jié)論
我建議您去參考源代碼,以了解有關(guān)何時使用并行流的更多信息,并在您使用Java進行并行化時隨時進行仔細的判斷。 最好的方法是在登臺環(huán)境中運行與這些測試類似的測試,在該環(huán)境中,您可以嘗試并更好地了解要面對的挑戰(zhàn)。 您必須要注意的因素當然是運行的硬件(和要測試的硬件)以及應(yīng)用程序中的線程總數(shù)。 這包括公用的Fork / Join池和團隊中其他開發(fā)人員正在處理的代碼。 因此,在添加自己的并行性之前,請嘗試檢查它們并獲得應(yīng)用程序的完整視圖。
基礎(chǔ)工作
為了運行此測試,我們使用了具有8個vCPU和15GB RAM的EC2 c3.2xlarge實例。 vCPU意味著存在超線程,因此實際上我們在這里有4個物理內(nèi)核,每個物理內(nèi)核都像2個內(nèi)核一樣工作。就OS調(diào)度程序而言,我們在這里有8個內(nèi)核。 為了盡可能使它公平,每個實現(xiàn)運行了10次,并且我們采用了運行2到9的平均運行時間。這是260次測試運行,! 另一重要的是處理時間。 我們選擇的任務(wù)要花20秒鐘以上的時間,因此差異更容易發(fā)現(xiàn),并且不受外部因素的影響。
下一步是什么?
原始結(jié)果可在此處獲得 ,代碼在GitHub上 。 請隨時修改它,并讓我們知道您得到什么樣的結(jié)果。 如果您對我們錯過的結(jié)果有更多有趣的見解或解釋,我們很樂意閱讀并添加到帖子中。
翻譯自: https://www.javacodegeeks.com/2015/01/forkjoin-framework-vs-parallel-streams-vs-executorservice-the-ultimate-forkjoin-benchmark.html
總結(jié)
以上是生活随笔為你收集整理的Fork / Join框架vs并行流vs.ExecutorService:最终的Fork / Join基准的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cad2013序列号和密钥激活码使用方法
- 下一篇: yy怎么播放电脑声音(电脑yy怎么放歌)