forkjoin_应用ForkJoin –从最佳到快速
forkjoin
到目前為止,JDK 7已很好地掌握在開發人員手中,并且大多數人都聽說過ForkJoin,但是沒有多少人有時間或機會去嘗試它。
它引起了并且可能仍然引起一些混亂,與普通線程池有什么不同。 [1]
我在本文中的目標是通過一個代碼示例來呈現一個更復雜但仍然簡單的ForkJoin使用示例。
我計時并測量串行 , 線程池和ForkJoin方法的性能 。
這是github的前期內容: https : //github.com/fbunau/javaadvent-forkjoin
實際問題
想象一下,我們的系統中有某種組件可以在每毫秒時間內保持股票的最新價格。
這可以作為整數數組保存在內存中。 (如果我們以bps為單位)
該組件的客戶進行如下查詢:價格最低的是time1和time2之間的時間點?
這可以是自動算法,也可以只是GUI中進行矩形選擇的人。
示例圖像中有7個查詢
讓我們還想象一下,我們從一個Task中批處理的客戶端中獲得了許多這樣的查詢。
可以將它們分批處理,以減少網絡流量和往返時間。
我們有組件可能獲得的不同大小的任務,最多10個查詢(帶有GUI的人),最多100個,..最多1 000 0000個(某種自動化算法)。 我們的組件有很多這樣的客戶,每個客戶都會產生不同大小的任務。 請參閱Task.TaskType
核心問題與解決方案
我們必須解決的核心問題是RMQ問題。 這是維基百科[2] :
“鑒于從一組有序集合(例如數字)中提取的對象數組,從i到j的范圍最小查詢(或RMQ)要求最小元素在子數組A[i, j] ”。
“例如,當A = [0, 5, 2, 5, 4, 3, 1, 6, 3]時,則A[3, 8] = [2, 5, 4, 3, 1, 6]的范圍最小查詢的答案A[3, 8] = [2, 5, 4, 3, 1, 6]為7 ,因為A[7] = 1 ”
存在用于解決此問題的有效數據結構,稱為“細分樹”。
我不會對此進行詳細介紹,因為這篇經典的Topcoder文章[3]對此進行了很好的介紹。 這本身對于這個ForkJoin示例并不重要,我之所以選擇它是因為它比簡單的總和更有趣,并且其本質是基于fork-join的精神。 它劃分要計算的任務,然后將其加入結果!
數據結構具有O(n)初始化時間和O(log N)查詢時間,其中N是每個時間單位值數組的價格中的元素數量。
因此,任務T包含M要進行的查詢。
在學術計算機科學方法中,您只是說我們將使用這種高效的數據結構處理每個任務,而復雜性將是:
您再沒有比這更有效率的了! 是的,在理論上是馮·諾依曼機器上的,但實際上可以。
一個容易引起混淆的是,因為O(n/4) == O(n) ,所以在編寫程序時,常數因子不計算在內,但確實如此!
停下來想一想,等待10或40分鐘/小時/年是否一樣?
平行進行
因此,考慮要解決的問題,我們如何才能使其更快? 由于現在每個計算設備都有更多的計算核心,因此讓我們充分利用它們并立即執行更多操作。
我們可以使用Fork Join框架輕松地做到這一點。
我最初很想嘗試一下RMQ數據結構并并行執行它的操作。 我攻擊了本來已經是log N的東西。但這是一個很大的失敗,對于調度程序來說,管理如此短時間的邏輯開銷太大。
答案是最終攻擊的M_i常數因子。
線程池
在介紹如何應用ForkJoin解決方案之前,讓我們想象一下如何應用線程池。 請參閱: TaskProcessorPool.java
我們可以有一個由4個工作人員組成的池,當我們有一個任務要做時,我們將其添加到隊列中。 一旦有工作人員可用,它將從隊列的開頭檢索待執行的任務,然后執行該任務。
雖然這對于具有相同大小的任務是很好的,并且大小相對中等且可預測,但是當要執行的任務大小不同時,就會遇到問題。 一名工人可能會因長期運行的任務而煩惱,而其他工人則無所事事。
在此圖像中,如果不將更多任務添加到隊列中,則線程池將在4個時間單位內完成16個可能的工作單位中的9個(效率為56%)
叉連接
當您位于問題域中時,可以將要解決的任務拆分為較小的任務,因此前叉聯接很有用。
fork-join池的特殊之處在于它是一個竊取工作的線程池。
每個輔助線程都維護任務的本地出隊。 在執行新任務時,可以執行以下任一操作:
- 將任務拆分為較小的任務
- 如果任務足夠小,則執行任務
當一個線程的出隊中沒有本地線程時,它將“竊取”,從另一個隨機線程的隊列后面彈出任務,并將其放在自己的線程中。 此任務尚未拆分的可能性很高。 因此,他將有很多工作要做。
與線程池相比,它們可以將現有任務拆分為較小的線程,而不是等待其他線程等待某些新工作,并幫助另一個線程處理較大的任務。
這是Doug Lea的原始論文,提供了更詳細的解釋: http : //gee.cs.oswego.edu/dl/papers/fj.pdf
回到我們的示例中,可以將一大批操作分成幾批較少數量的操作。 參見: TaskProcessorFJ.java
大多數問題具有像這樣的線性操作序列,它不一定是特殊的并行問題,對此我們需要應用專門的并行算法來利用處理器上的核心。
你分多少錢? 您拆分任務,直到達到通常不再有意義的閾值為止。 例子:(拆分+獲得工作的線程+上下文切換比實際執行任務要多)
對于大型XXL,我們必須執行1000000個查詢操作。 我們可以將其分為2 500000個操作任務,并并行執行。 500000仍然很大嗎? 是的,我們可以進一步拆分。 我選擇了一組10000個操作作為閾值,在該閾值下沒有任何拆分用途,我們可以在當前線程上執行它們。
Fork join并不會拆分所有任務,而是通過它進行工作。
績效結果
在干凈重啟后,我對i5-2500 CPU @ 3.30GHz上具有4核/ 4線程的i5-2500 CPU的每個處理器實現進行了4次迭代。
結果如下:
結論
即使您選擇了正確的最佳數據結構,它也不會很快,直到您使用所有的資源。 即利用所有核心
在某些問題域中,ForkJoin絕對是對線程池的改進,值得探索在哪里可以應用它,我們將看到越來越多的并行代碼。
您今天可以購買這種處理器 ,即12核/ 24線程。 現在,我們只需要編寫軟件來利用我們擁有的,將在將來獲得的出色硬件即可。
代碼在這里: https : //github.com/fbunau/javaadvent-forkjoin如果您想使用它的話
感謝您的寶貴時間,如果發現任何錯誤或需要添加的內容,請留下一些評論。
翻譯自: https://www.javacodegeeks.com/2013/12/applying-forkjoin-from-optimal-to-fast.html
forkjoin
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的forkjoin_应用ForkJoin –从最佳到快速的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓rar解压工具(安卓rar)
- 下一篇: linux 阻塞 非阻塞 区别(linu