日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Fork / Join框架vs并行流vs.ExecutorService:最终的Fork / Join基准

發(fā)布時(shí)間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Fork / Join框架vs并行流vs.ExecutorService:最终的Fork / Join基准 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Fork / Join框架在不同配置下如何工作?

就像即將上映的《星球大戰(zhàn)》一樣,圍繞Java 8并行性的批評(píng)也充滿了興奮。 并行流的語(yǔ)法糖帶來(lái)了一些炒作,就像我們?cè)陬A(yù)告片中看到的新型光劍一樣。 現(xiàn)在,有了許多使用Java進(jìn)行并行處理的方法,我們希望了解性能優(yōu)勢(shì)和并行處理的危險(xiǎn)。 經(jīng)過(guò)260多次測(cè)試運(yùn)行,從數(shù)據(jù)中獲得了一些新見(jiàn)解,我們希望在本文中與您分享。

分叉/加入:分叉喚醒

ExecutorService與Fork / Join Framework與并行流

很久以前,在一個(gè)遙遠(yuǎn)的星系中……。 我的意思是,大約10年前,并發(fā)只能通過(guò)3rd party庫(kù)在Java中使用。 然后出現(xiàn)了Java 5,并在語(yǔ)言中引入了java.util.concurrent庫(kù),該庫(kù)受到Doug Lea的強(qiáng)烈影響。 ExecutorService可用并為我們提供了處理線程池的直接方法。 當(dāng)然,java.util.concurrent一直在發(fā)展,并且在Java 7中,在ExecutorService線程池的基礎(chǔ)上引入了Fork / Join框架。 借助Java 8流,我們已經(jīng)為使用Fork / Join提供了一種簡(jiǎn)單的方法,對(duì)于許多開(kāi)發(fā)人員而言,它仍然有些神秘。 讓我們找出它們之間的比較。

我們完成了兩項(xiàng)任務(wù),一項(xiàng)是CPU密集型任務(wù),另一項(xiàng)是IO密集型任務(wù),并使用相同的基本功能測(cè)試了4種不同的方案。 另一個(gè)重要因素是我們用于每個(gè)實(shí)現(xiàn)的線程數(shù),因此我們也對(duì)其進(jìn)行了測(cè)試。 我們使用的機(jī)器有8個(gè)內(nèi)核 ,因此我們有4、8、16和32個(gè)線程的變種,以大致了解結(jié)果的發(fā)展方向。 對(duì)于每個(gè)任務(wù),我們還嘗試了一個(gè)單線程解決方案,您不會(huì)在圖中看到它,因?yàn)閳?zhí)行起來(lái)需要花費(fèi)更長(zhǎng)的時(shí)間。 要詳細(xì)了解測(cè)試的運(yùn)行方式,您可以查看下面的基礎(chǔ)部分。 現(xiàn)在,讓我們開(kāi)始吧。

索引580萬(wàn)行文本的6GB文件

在此測(cè)試中,我們生成了一個(gè)巨大的文本文件,并為索引過(guò)程創(chuàng)建了類似的實(shí)現(xiàn)。 結(jié)果如下所示:

文件索引測(cè)試結(jié)果

**單線程執(zhí)行:176,267毫秒,或?qū)⒔?分鐘。
**請(qǐng)注意,圖形開(kāi)始于20000毫秒。

1.更少的線程將使CPU處于未使用狀態(tài),太多的線程將增加開(kāi)銷

您在圖表中注意到的第一件事是結(jié)果開(kāi)始采用的形狀–您僅從這4個(gè)數(shù)據(jù)點(diǎn)就可以了解每個(gè)實(shí)現(xiàn)的行為。 臨界點(diǎn)在8到16個(gè)線程之間,因?yàn)槟承┚€程在文件IO中處于阻塞狀態(tài),并且添加比內(nèi)核更多的線程有助于更好地利用它們。 當(dāng)有32個(gè)線程進(jìn)入時(shí),由于額外的開(kāi)銷,性能會(huì)變差。

比亞軍快1秒:直接使用Fork / Join

除了語(yǔ)法糖(lambdas!我們沒(méi)有提到lambdas),我們已經(jīng)看到并行流的性能比Fork / Join和ExecutorService實(shí)現(xiàn)的更好。 6GB的文本在24.33秒內(nèi)被索引。 您可以在這里信任Java來(lái)提供最佳結(jié)果。

3.但是...并行流也表現(xiàn)最差:唯一的變化超過(guò)了30秒

這再次提醒了并行流如何使您減速。 假設(shè)這種情況發(fā)生在已經(jīng)運(yùn)行多線程應(yīng)用程序的計(jì)算機(jī)上。 在可用線程數(shù)量較少的情況下,直接使用Fork / Join實(shí)際上比通過(guò)并行流要好-5秒的差異,將這兩個(gè)線程進(jìn)行比較時(shí)大約要付出18%的代價(jià)。

4.不要使用圖片中帶有IO的默認(rèn)池大小

當(dāng)為并行流使用默認(rèn)池大小時(shí),計(jì)算機(jī)上相同數(shù)量的內(nèi)核(此處為8個(gè)內(nèi)核)比16個(gè)線程版本的性能差了近2秒。 如果使用默認(rèn)池大小,則要加收7%的罰款。 發(fā)生這種情況的原因與阻塞IO線程有關(guān)。 還有更多的等待正在進(jìn)行,因此引入更多的線程可以使我們更多地使用所涉及的CPU內(nèi)核,而其他線程則需要等待調(diào)度而不是空閑。

如何更改并行流的默認(rèn)Fork / Join池大小? 您可以使用JVM參數(shù)更改常見(jiàn)的Fork / Join池大小:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=16

(默認(rèn)情況下,所有Fork / Join任務(wù)都使用一個(gè)公共靜態(tài)池,其大小與內(nèi)核數(shù)相同。這樣做的好處是,通過(guò)在不使用期間為其他任務(wù)回收線程,從而減少了資源使用。)

或者…您可以使用此技巧并在自定義的Fork / Join池中運(yùn)行并行流。 這將覆蓋通用的Fork / Join池的默認(rèn)用法,并允許您使用自己設(shè)置的池。 偷偷摸摸。 在測(cè)試中,我們使用了公共池。

5.單線程性能比最佳結(jié)果差7.25倍

并行性提供了7.25倍的改進(jìn),并且考慮到該機(jī)器具有8個(gè)核心,因此非常接近理論上的8倍預(yù)測(cè)! 我們可以將其余的歸因于開(kāi)銷。 話雖如此,即使我們測(cè)試的最慢的并行性實(shí)現(xiàn)(這次是具有4個(gè)線程的并行流(30.24sec))的性能也比單線程解決方案(176.27sec)好5.8倍。

檢查數(shù)字是否為質(zhì)數(shù)

在下一輪測(cè)試中,我們完全消除了IO,并檢查了確定一個(gè)真正大的數(shù)字是否為素?cái)?shù)所需的時(shí)間。 多大? 19位數(shù)字 。 1,530,692,068,127,007,263,或換句話說(shuō):五百一十九億四千三百四十四萬(wàn)億三千三百八十億八千八百三十八萬(wàn)三千三百三十三。 啊,讓我呼吸一下。 無(wú)論如何,除了運(yùn)行到平方根以外,我們沒(méi)有使用任何優(yōu)化,因此即使我們的大數(shù)沒(méi)有除以2只是為了延長(zhǎng)處理時(shí)間,我們也檢查了所有偶數(shù)。 劇透警告:這是首要的,因此每個(gè)實(shí)現(xiàn)都運(yùn)行相同數(shù)量的計(jì)算。

結(jié)果是這樣的:

素?cái)?shù)測(cè)試結(jié)果

**單線程執(zhí)行:118,127毫秒,或?qū)⒔?分鐘。
**請(qǐng)注意,圖形開(kāi)始于20000毫秒

1. 8和16個(gè)線程之間的差異較小

與IO測(cè)試不同,這里沒(méi)有IO調(diào)用,因此8個(gè)線程和16個(gè)線程的性能基本相似,除了Fork / Join解決方案。 實(shí)際上,我們已經(jīng)進(jìn)行了多組測(cè)試,以確保由于這種“異常”而在這里獲得良好的結(jié)果,但事實(shí)證明,一次又一次地非常相似。 我們很高興在下面的評(píng)論部分中聽(tīng)到您對(duì)此的想法。

2.所有方法的最佳結(jié)果相似

我們看到所有實(shí)現(xiàn)都共享大約28秒的相似最佳結(jié)果。 無(wú)論我們嘗試采用哪種方法,結(jié)果都是一樣的。 這并不意味著我們對(duì)使用哪種方法都無(wú)所謂。 查看下一個(gè)見(jiàn)解。

3.并行流比其他實(shí)現(xiàn)更好地處理線程重載

這是更有趣的部分。 通過(guò)該測(cè)試,我們?cè)俅慰吹竭\(yùn)行16個(gè)線程的最高結(jié)果來(lái)自使用并行流。 而且,在此版本中,使用并行流是線程號(hào)的所有變體的一個(gè)好方法。

4.單線程性能比最佳結(jié)果低4.2倍

另外,在運(yùn)行計(jì)算密集型任務(wù)時(shí)使用并行性的好處幾乎比使用文件IO的IO測(cè)試要差2倍。 這是有道理的,因?yàn)樗荂PU密集型測(cè)試,與之前的測(cè)試不同,我們可以通過(guò)減少內(nèi)核等待被IO阻塞的線程的時(shí)間來(lái)獲得額外的好處。

結(jié)論

我建議您去參考源代碼,以了解有關(guān)何時(shí)使用并行流的更多信息,并在您使用Java進(jìn)行并行化時(shí)隨時(shí)進(jìn)行仔細(xì)的判斷。 最好的方法是在登臺(tái)環(huán)境中運(yùn)行與這些測(cè)試類似的測(cè)試,在該環(huán)境中,您可以嘗試并更好地了解要面對(duì)的挑戰(zhàn)。 您必須要注意的因素當(dāng)然是運(yùn)行的硬件(和要測(cè)試的硬件)以及應(yīng)用程序中的線程總數(shù)。 這包括公用的Fork / Join池和團(tuán)隊(duì)中其他開(kāi)發(fā)人員正在處理的代碼。 因此,在添加自己的并行性之前,請(qǐng)嘗試檢查它們并獲得應(yīng)用程序的完整視圖。

基礎(chǔ)工作

為了運(yùn)行此測(cè)試,我們使用了具有8個(gè)vCPU和15GB RAM的EC2 c3.2xlarge實(shí)例。 vCPU意味著存在超線程,因此實(shí)際上我們?cè)谶@里有4個(gè)物理內(nèi)核,每個(gè)物理內(nèi)核都像2個(gè)內(nèi)核一樣工作。就OS調(diào)度程序而言,我們?cè)谶@里有8個(gè)內(nèi)核。 為了盡可能使它公平,每個(gè)實(shí)現(xiàn)運(yùn)行了10次,并且我們采用了運(yùn)行2到9的平均運(yùn)行時(shí)間。這是260次測(cè)試運(yùn)行,! 另一重要的是處理時(shí)間。 我們選擇的任務(wù)要花20秒鐘以上的時(shí)間,因此差異更容易發(fā)現(xiàn),并且不受外部因素的影響。

下一步是什么?

原始結(jié)果可在此處獲得 ,代碼在GitHub上 。 請(qǐng)隨時(shí)修改它,并讓我們知道您得到什么樣的結(jié)果。 如果您對(duì)我們錯(cuò)過(guò)的結(jié)果有更多有趣的見(jiàn)解或解釋,我們很樂(lè)意閱讀并添加到帖子中。

翻譯自: 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基准的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。