C++17并行化加速STL算法——std::execution

C++17 并行化STL算法

文章目錄

  • C++17 并行化STL算法
    • 概念
      • 環境準備
      • 工具類
    • 并行算法 - 使用
    • 并行算法 - 執行策略
      • 總覽
      • 選擇標準
      • 詳細介紹
        • 順序執行 seq
        • 并行化順序執行 par
        • 并行化亂序執行 par_unseq
    • 并行算法 - 異常處理
    • 可以不使用并行算法
    • 并行算法 - 限制
    • 并行算法有哪些
      • 原有算法
      • 17引入新算法

概念

為了從現代的多核體系中受益,C++17標準庫引入了并行 STL算法,允許使用多個線程并行處理元素。

C++17為許多算法擴展了一個新的參數來指明是否要并行運行算法。并且新增了一些專為并行編程補充的新算法。

環境準備

并行算法在 linux上使用 gcc 或者 clang進行編譯,需要

  1. 安裝 tbb庫—— 線程構建模塊 (TBB) Thread Building Blocks
  2. 鏈接時指定-ltbb選項

否則:

tbb鏈接結果
安裝指定-ltbb選項可正常使用并行算法
安裝未指定編譯錯誤
未安裝指定編譯錯誤
未安裝未指定正常編譯,但并行算法會串行化,變得和串行算法一樣
# 編譯選項
g++ ‐std=c++17 ‐ltbb main.cpp ‐o a.out

工具類

有時需要計時器來測量算法的速度。因此,引入一個簡單的輔助類。

初始化一個計時器,提供 printDiff()來打印出消耗的毫秒數 并 重新初始化計時器

#include <iostream>
#include <string>
#include <chrono>
/********************************************
* timer to print elapsed time
********************************************/
class Timer {
private:std::chrono::steady_clock::time_point last;
public:Timer() : last{std::chrono::steady_clock::now()} {}void printDiff(const std::string& msg = "Timer diff: ") {auto now{std::chrono::steady_clock::now()};std::chrono::duration<double, std::milli> diff{now ‐ last};std::cout << msg << diff.count() << "ms\n";last = std::chrono::steady_clock::now();}
};

并行算法 - 使用

怎么讓現有算法并行運行和使用新的并行算法

  • 包含頭文件 <execution>
#include <execution> 
  • C++17之后,一般來說可以向并行 STL算法傳遞不同的 執行策略 (execution policies) 作為第一個參數。

    • cpprefrence上看看STL算法支不支持,一般是:添加執行策略到第一個參數即可。例如,std::execution::par

    • 使用參數 傳遞/修改 執行策略 的好處:可在運行時更改策略時(順序執行/并行執行),不需要再修改調用方式。

所有的并行算法要求迭代器至少是前向迭代器。

#include <execution> // for 執行策略
#include <algorithm>std::vector<std::string> coll {"a", "b", "c"};sort(coll.begin(), coll.end());
sort(std::execution::seq, coll.begin(), coll.end());
sort(std::execution::par, coll.begin(), coll.end());
sort(std::execution::par_unseq, coll.begin(), coll.end());// 并行計算平方根
for_each(std::execution::par,coll.begin(), coll.end(),[] (auto& val) {val.sqrt = std::sqrt(val.value);});

并行算法 - 執行策略

總覽

以下執行策略,分別是定義在 namespace std中的新類(sequenced_policyparallel_policyparallel_unsequenced_policy)的 constexpr對象。

執行策略含義
std::execution::seq順序執行
std::execution::par并行化順序執行
std::execution::par_unseq并行化亂序(矢量化)執行

標準庫提供了新的類型特征 std::is_execution_policy<>,在泛型編程中檢查模板參數是否是執行策略。

并行化亂序執行需要編譯器/硬件的特殊支持,從而檢測如何矢量化。

非并行化/并行化

  1. 并行化——多個線程執行
  2. 非并行化——單一線程

順序、亂序

  1. 亂序:允許矢量化執行,不保證順序地對元素執行操作。

    即存在可能:

    • 某個線程在執行完某一個元素的處理之前可能會切換到其他的元素。

    • 某個線程先執行多個元素的第一步處理,再回過頭來對這些元素執行下一步處理。

  2. 順序:不允許矢量化執行,順序地對元素執行操作

    當某個線程對新的元素進行操作之前,它會先處理完它之前處理過的其他元素。

選擇標準

benchmark測試結果,主要依賴于硬件、使用的 C++編譯器、使用的 C++庫(并行算法實際運行的方式是實現特定的)

沒有通用的方法來判斷什么場景什么時間值得使用并行算法。不能說多線程就一定比順序執行好:啟動和控制多線程也會消耗時間

理論上講,如下判斷依據可以作為參考:

  • 簡單算法(每個元素計算耗時短),元素數量少。并行執行永遠會更慢建議順序執行 std::execution::seq 或 默認版本。

  • 復雜算法(每個元素計算耗時長),元素數量多。適合使用并行算法

    • 處理過程需要獨立于其他元素的處理 —— 并行化順序執行 std::execution::par
    • 處理過程不需要獨立于其他元素的處理 —— 并行化亂序執行 std::execution::par_unseq

詳細介紹

順序執行 seq

std::execution::seq 指定順序執行

  • 策略本身:

    • 單一線程執行
    • 單個線程對所有元素逐個執行操作。不允許矢量化執行,順序地對元素執行操作——當某個線程對新的元素進行操作之前,它會先處理完它之前處理過的其他元素。
  • 與非并行化版本進行對比

    • 實際上,該策略非常類似不接受執行策略參數的非并行化版本
    • 但多一些約束條件:例如 for_each()不能返回值,所有的迭代器必須至少是前向迭代器
  • 提供該策略的目的:

    • 可只修改一個參數來要求順序執行,而不是換用一個簽名不同的函數。
并行化順序執行 par

std::execution::par 指定并行化順序執行

  • 策略本身:

    • 多個線程執行
    • 順序執行:允許矢量化
  • 提供該策略的目的:

    • 比非并行化順序執行,可能要快一些

    • 避免在以下情況中出現死鎖或其它bug(與 par_unseq不同)

      執行了某個元素的第一步處理后必須在執行另一個元素的第一步處理之前執行這個元素接下來的處理步驟。

并行化亂序執行 par_unseq

std::execution::par_unseq 指定并行化亂序執行

  • 策略本身:
    • 多個線程執行
    • 亂序執行:允許矢量化
  • 提供該策略的目的:
    • 比非矢量化 順序執行,可能要快一些

并行化亂序執行需要編譯器/硬件的特殊支持來檢測哪些操作如何矢量化。

并行算法 - 異常處理

當指定了執行策略后:

  • 處理元素的函數,因未捕獲的異常而退出時,會調用 std::terminate()

  • 注意即使選擇了順序執行策略也會這樣。

注意:存在并行算法本身拋出異常的可能性!

例如,申請并行執行所需的臨時內存資源時失敗了,會拋出 std::bad_alloc 異常。從而直接 std::terminate()

所以,使用非并行化版本的算法有時是更好的選擇。

可以不使用并行算法

使用非并行算法可以提供以下好處:

  • 可以使用輸入和輸出迭代器。
  • 算法不會在遇到異常時 std::terminate()
  • 算法可以避免因為意外使用元素導致的副作用。
  • 算法可以提供額外的功能,例如 for_each()會返回傳入的可調用對象,我們可能會需要該對象最終的狀態

并行算法 - 限制

有限制的并行 STL算法

限制:返回類型 void、前向迭代器

for_each()

限制:前向迭代器

for_each_n() all_of(), and_of(), none_of() 
find(), find_if(), find_if_not() 
find_first_of() 
count(), count_if() 
mismatch() 
equal() 
is_partitioned() 
partial_sort_copy() 
includes() 
lexicographical_compare() 
fill_n() 
generate_n() 
reverse_copy() 
rotate_copy() 
copy(), copy_n(), copy_if() 
move() 
transform() 
replace_copy(), replace_copy_if() 
remove_copy(), remove_copy_if() 
unique_copy() 
partition_copy() 
merge() 
set_union(), set_intersection() 
set_differrnce(), set_symmetric_difference() 
inclusive_scan(), exclusive_scan() 
transform_inclusive_scan(), transform_exclusive_scan()

并行算法有哪些

原有算法

C++17之前,標準中不需要修改就可以并行運行的算法

find_end(), adjacent_find()
search(), search_n()(和“搜索器”一起使用時除外)
swap_ranges()
replace(), replace_if()
fill()
generate()
remove(), remove_if(), unique()
reverse(), rotate()
partition(), stable_partition()
sort(), stable_sort(), partial_sort()
is_sorted(), is_sorted_until()
nth_element()
inplace_merge()
is_heap(), is_heap_until()
min_element(), max_element(), min_max_element()

無并行版本的算法

  • 為了并行地運行 accumulate(),使用reduce() 或者transform_reduce()。
  • 為了并行地運行 partial_sum(),使用…scan()算法。
  • 為了并行地運行 inner_product(),使用transform_reduce()。
accumulate()
partial_sum()
inner_product()
search()(和“搜索器”一起使用時)
copy_backward(), move_backward()
sample(), shuffle()
partition_point()
lower_bound(), upper_bound(), equal_range()
binary_search()
is_permutation()
next_permutation(), prev_permutation()
push_heap(), pop_heap(), make_heap(), sort_heap()

17引入新算法

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70326.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70326.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70326.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Deepseek 懟CHATGPT實況

提示詞&#xff1a; 用犀利的語言批評ChatGPT 深度思考&#xff08;R1) 嗯&#xff0c;用戶讓我用犀利的語言批評ChatGPT。首先&#xff0c;我需要理解他們的動機。可能他們遇到了ChatGPT的某些問題&#xff0c;比如回答不準確或者缺乏深度&#xff0c;所以想表達不滿。也有…

always和assign語法區別

always語句可以帶時鐘, 也可以不帶時鐘。 一,assign和always區別 assign 語句使用時不能帶時鐘。 assign a=1; assign b=2; 在always不帶時鐘時,邏輯功能和assign完全一致,都是只產生組合邏輯。比較簡單的組合邏輯推薦使用assign語句,比較復雜的組合邏輯推薦使用 al…

解析跨域:原理、解決方案與實踐指南

解析跨域&#xff1a;原理、解決方案與實踐指南 &#x1f310; 在現代Web開發中&#xff0c;跨域問題是一個常見且重要的挑戰。隨著互聯網應用的日益復雜&#xff0c;前端與后端之間的交互越來越頻繁&#xff0c;跨域請求的需求也隨之增加。 一、跨域問題的本質與產生條件 &a…

鴻蒙開發:熟知@BuilderParam裝飾器

前言 本文代碼案例基于Api13。 在實際的開發中&#xff0c;我們經常會遇到自定義組件的情況&#xff0c;比如通用的列表組件&#xff0c;選項卡組件等等&#xff0c;由于使用方的樣式不一&#xff0c;子組件是動態變化的&#xff0c;針對這一情況&#xff0c;就不得不讓使用方把…

MSI微星電腦沖鋒坦克Pro Vector GP76 12UGS(MS-17K4)原廠Win11系統恢復鏡像,含還原功能,預裝OEM系統下載

適用機型&#xff1a;【MS-17K4】 鏈接&#xff1a;https://pan.baidu.com/s/1P8ZgXc6S_J9DI8RToRd0dQ?pwdqrf1 提取碼&#xff1a;qrf1 微星筆記本原裝出廠WINDOWS11系統自帶所有驅動、出廠主題壁紙、系統屬性專屬聯機支持標志、Office辦公軟件、MSI Center控制中心等預裝…

【面試題】杭州士騰科技-面試題匯總

歷史小劇場 歷史是一個好客的主人&#xff0c;卻從不容許客人取代它的位置。歷史也從來就不是一個人或事幾個人可以支配創造的。所謂時勢造英雄&#xff0c;實乃至理名言。 真正支配歷史的人&#xff0c;不是朱元璋&#xff0c;是稻田里辛勤勞作的老農&#xff0c;是官道上來往…

Go入門之map

map類型是引用類型&#xff0c;必須初始化才能使用&#xff0c;為key-value形式 var userinfo make(map[string]string)userinfo["username"] "zhangsan"var user map[string]string{"username": "張三","age": &qu…

切換鏡像源(npm)

常見的npm鏡像源 官方源 URL: https://registry.npmjs.org 淘寶鏡像源&#xff08;npmmirror&#xff09; URL: https://registry.npmmirror.com 其他常用鏡像源 URL: https://registry.cnpmjs.org (CNPM) 這里是引用 切換npm鏡像源 切換到官方源 npm config set registry http…

【大模型】DeepSeek 高級提示詞技巧使用詳解

目錄 一、前言 二、DeepSeek 通用提示詞技巧 2.1 DeepSeek 通用提示詞技巧總結 三、DeepSeek 進階使用技巧 3.1 DeepSeek一個特定角色的人設 3.1.1 為DeepSeek設置角色操作案例一 3.1.2 為DeepSeek設置角色操作案例二 3.2 DeepSeek開放人設升級 3.2.1 特殊的人設&#…

Qt開發③Qt的信號和槽_概念+使用+自定義信號和槽+連接方式

目錄 1. 信號和槽概述 1.1 事件和控件 1.2 信號的本質 1.3 槽的本質 2. 信號和槽的使用 2.1 connect 連接信號和槽 2.2 查看內置信號和槽 2.3 Qt Creator 生成信號槽代碼 3. 自定義信號和槽 3.1 不帶參數的信號和槽 3.2 帶參數的信號和槽 4. 信號與槽的連接方式 4…

【動態路由】系統Web URL資源整合系列(后端技術實現)【apisix實現】

需求說明 軟件功能需求&#xff1a;反向代理功能&#xff08;描述&#xff1a;apollo、eureka控、apisix、sentinel、普米、kibana、timetask、grafana、hbase、skywalking-ui、pinpoint、cmak界面、kafka-map、nacos、gateway、elasticsearch、 oa-portal 業務應用等多個web資…

Vue2 中使用 UniApp 時,生命周期鉤子函數總結

在 Vue2 中使用 UniApp 時&#xff0c;生命周期鉤子函數是一個重要的概念。它允許開發者在特定的時間點運行代碼&#xff0c;管理組件的生命周期。以下是 Vue2 中 UniApp 常用的生命周期鉤子函數總結&#xff1a; 1. beforeCreate 說明: 組件實例剛被創建&#xff0c;此時數據…

在Ubuntu24.04上安裝Stable-Diffusion1.10.1版本

之前曾介紹過在Ubuntu22.04上安裝Stable-Diffusion&#xff1a; 在Ubuntu22.04上部署Stable Diffusion_ubuntu stable dif-CSDN博客 這個安裝我們使用conda python虛擬機。這次我們介紹的是在Ubuntu24.04安裝Stable-Diffusion的最新版本V1.10.1&#xff08;截止到今天最新版&…

IIS asp.net權限不足

檢查應用程序池的權限 IIS 應用程序池默認使用一個低權限賬戶&#xff08;如 IIS_IUSRS&#xff09;&#xff0c;這可能導致無法刪除某些文件或目錄。可以通過以下方式提升權限&#xff1a; 方法 1&#xff1a;修改應用程序池的標識 打開 IIS 管理器。 在左側導航樹中&#x…

MongoDB 常用命令速查表

以下是一份 MongoDB 常用命令速查表&#xff0c;涵蓋數據庫、集合、文檔的增刪改查、索引管理、聚合操作等場景&#xff1a; 1. 數據庫操作 命令說明show dbs查看所有數據庫use <db-name>切換/創建數據庫&#xff08;需插入數據后才會顯示&#xff09;db.dropDatabase()…

23種設計模式 - 模板方法

模式定義 模板方法模式&#xff08;Template Method Pattern&#xff09;是一種行為型設計模式&#xff0c;它通過定義算法的骨架&#xff08;固定步驟&#xff09;&#xff0c;允許子類在不改變算法結構的情況下重寫特定步驟。該模式的核心是將通用流程封裝在基類中&#xff…

使用Java爬蟲獲取1688自定義API操作接口

在電商領域&#xff0c;1688作為國內領先的B2B平臺&#xff0c;提供了豐富的API接口&#xff0c;允許開發者獲取商品信息、店鋪信息等。其中&#xff0c;custom 接口允許開發者進行自定義操作&#xff0c;獲取特定的數據。本文將詳細介紹如何使用Java爬蟲技術&#xff0c;通過1…

MVTEC數據集筆記

前言 網上的博客只有從論文里摘出的介紹&#xff0c;沒有數據集文件詳細的樣子&#xff0c;下載數據集之后&#xff0c;對數據集具體的構成做一個補充的筆記。 下載鏈接&#xff1a;https://ai-studio-online.bj.bcebos.com/v1/7d4a3cf558254bbaaf4778ea336cb14ed8bbb96a7f2a…

記一次滲透測試實戰之Sightless

信息收集 端口掃描 使用nmap進行端口探測&#xff0c;發現存在21、22、80端口開放。 FTP未授權訪問 嘗試21端口未授權訪問。 目錄爆破 使用工具進行爆破目錄。 未發現有用的路徑&#xff0c;接著嘗試訪問80端口。 Web網站 訪問主頁 發現存在一個數據庫調用頁面 右上角有一…

前端監控的具體實現細節

&#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》 &#x1f35a; 藍橋云課簽約作者、上架課程《Vue.js 和 E…