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.mzph.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…