簡體   English   中英

並行編程和C ++

[英]Parallel Programming and C++

我最近寫了很多關於並行計算和編程的文章,我注意到在並行計算方面有很多模式出現。 注意到Microsoft已經發布了一個庫以及Microsoft Visual C ++ 2010社區技術預覽版(名為Parallel Patterns Library),我想知道你一直在使用和遇到的常見並行編程模式有哪些值得記住? 當你用C ++編寫並行程序時,你是否有任何習慣用法和你似乎不斷彈出的模式?

模式:

  • 生產者/消費者

    • 一個線程產生數據
    • One Thread消耗數據
  • 循環並行

    • 如果您可以顯示每個循環是獨立的
      每次迭代都可以在sperate線程中完成
  • 重新繪制線程

    • 其他線程可以工作並更新數據結構,但一個線程重新繪制屏幕。
  • 主事件線程

    • 多個線程可以生成事件
    • 一個線程必須處理事件(因為順序很重要)
    • 應該嘗試分離事件線程/重新繪制線程
      這(幫助)可以防止UI凍結
      但如果不仔細,可能會造成過度的重新吸引。
  • 工作組

    • 一組線程等待que上的作業。
    • 線程從隊列中提取一個工作項(如果沒有,則等待)。
      線程在一個工作項上工作,直到完成
      一旦完成的線程返回隊列。

並行執行模式

具有確定性模式的結構化並行編程是一種高級方法,主要基於一系列循環並行執行模式,通常稱為算法骨架或並行結構,它們抽象程序描述並隱藏低級多線程細節和並行性中固有的許多復雜性來自程序員。

這些可重用模式可自動執行許多與並行范例相關的例程,例如同步,通信,數據分區或任務調度,並在內部處理它們。 這種高級方法嘗試了傳統的低級線程鎖模型,它具有更多的抽象和更簡單的方式來表達並行性,並且可以集中生產力和可編程性而不是性能。

有許多常用的模式,例如:Map-Reduce,Fork-Join,Pipeline或Parallel Loop ......

文件

“具有確定性模式的結構化並行編程”是一篇討論這些模式的論文。 您還可以看到“MHPM:多尺度混合編程模型:靈活的並行化方法”,它描述了這種方法的名為XPU的C ++實現。

圖書館

XPU是一個基於任務的C ++庫,由一組可重用的執行模式組成。 它允許在單個同構編程模型中以幾個粒度級別表達幾種類型的並行性。 它易於使用,並說明了使用模式設計並行程序的興趣。

例如,它允許表達:

  1. 任務並行模式:

    簡單或分層Fork / Join執行模式,具有一些功能,如自動檢測和共享數據保護。

  2. 數據並行模式:

    具有可伸縮數據分區的並行循環模式。

  3. 時間平行模式:

    管道執行模式。

首先,您必須在共享內存計算和無共享計算之間進行選擇。 共享內存更容易,但不能很好地擴展 - 如果你這樣做,你將使用無共享內存

a)擁有一個集群,而不是一個多處理器系統,或者

b)如果你有很多CPU(比如說> 60),並且存在高度不均勻的內存

對於共享內存,常見的解決方案是使用線程; 它們易於理解為概念,易於在API中使用(但難以調試)。

對於無共享,您使用某種消息傳遞。 在高性能計算中,MPI被建立為消息傳遞中間件。

然后,您還需要為並行活動設計架構。 最常見的方法(再次因為它易於理解)是農民工人模式(又名主從)。

你有一些基礎知識可以解決程序部分的並行問題。 C ++ 17獲得了很多(例如foreach,sort,find和friends的平行版本,map_reduce,map,reduce,prefix_sum ...)請參閱C ++擴展並行

然后你有像延續這樣的項目。 想想std :: future但是繼續。 實現這些的方法很少(現在提升有一個很好的方法,因為std沒有下一個(...)或然后(...)方法,但最大的好處是一個人不必等待它要做下一個任務

auto fut = async([]( ){..some work...} ).then( [](result_of_prev ){...more work} ).then... ;
fut.wait( );

后續任務之間缺乏同步非常重要,因為任務/線程/ ...之間的通信會降低並行程序的速度。

因此,基於任務的並行性非常好。 使用任務計划程序,您只需將任務傳遞出去並離開。 他們可能有像信號量一樣的方法來回傳,但這不是強制性的。 英特爾線程構建模塊Microsoft並行模式庫都具有此功能。

之后我們有fork / join模式。 它並不意味着為每個任務創建N個線程。 只是你有這些N,理想獨立的事情要做(分叉),當它們完成時在某處(連接)有一個同步點。

auto semaphore = make_semaphore( num_tasks );
add_task( [&semaphore]( ) {...task1...; semaphore.notify( ); } );
add_task( [&semaphore]( ) {...task2...; semaphore.notify( ); } );
...
add_task( [&semaphore]( ) {...taskN...; semaphore.notify( ); } );
semaphore.wait( );

從上面你可以開始看到這是一個流程圖的模式。 未來是(A >> B >> C >> D)和Fork Join是(A | B | C | D)。 有了它,你可以將它們組合成一個圖形。 (A1 >> A2 | B1 >> B2 >> B3 | C1 | D1 >> D2 >>(E1 >> E2 | F1))其中A1 >> A2表示A1必須在A2之前,A | B表示A和B可以同時運行。 緩慢的部分位於事物相遇的圖形/子圖的末尾。

目標是找到不需要通信的系統的獨立部分。 如上所述,並行算法在幾乎所有情況下都比其順序算法慢,直到工作負載變得足夠高或者規模變得足夠大(假設通信不太繁瑣)。 例如排序。 在4核計算機上,您將獲得大約2.5倍的性能,因為合並很繁瑣,需要大量同步,並且在第一輪合並后不能運行所有核心。 在具有非常大的N的GPU上,可以使用效率較低的排序,比如Bitonic,並且它最終會非常快,因為你有很多工作人員來完成工作並且每個人都在安靜地做自己的事情。

減少通信的一些技巧包括,使用數組作為結果,以便每個任務不會試圖鎖定對象以推送值。 通常以后減少這些結果可以非常快。

但是對於所有類型的並行性,緩慢來自通信。 減少它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM