跟我学C++中级篇——STL的并行算法

一、并行算法

在STL的算法中,对于大多数程序员的应用,都是普通的单线程的库。同时,很多开发者也都注意到,在STL的库中很多都是非多线程安全的。而且随着硬件和软件技术的不段的发展,许多库面临着在多核和多线程环境下执行的需求。
因此,在c++新的标准库中,特别是从c++17开始,支持了很多的并行算法库,使得其运行效率得到极大的提升,有的相差甚至可以达到量级的水平。

二、STL常用的并行算法

STL常用的并行算法库主要在和两个头文件中,其中具体的实现在头文件中。在使用并行算法库时,需要指定相关的执行策略,即:

详细说明见下面英文说明
std::execution::sequenced_policy
std::execution::parallel_policy
std::execution::parallel_unsequenced_policy
std::execution::unsequenced_policy  (c++20)

未来还可能包含“未来额外策略包含 std::parallel::cuda 和 std::parallel::opencl ”。
其意义为:

1、 The execution policy type used as a unique type to disambiguate parallel algorithm overloading and require that a parallel algorithm's execution may not be parallelized. The invocations of element access functions in parallel algorithms invoked with this policy (usually specified as std::execution::seq) are indeterminately sequenced in the calling thread.
2、 The execution policy type used as a unique type to disambiguate parallel algorithm overloading and indicate that a parallel algorithm's execution may be parallelized. The invocations of element access functions in parallel algorithms invoked with this policy (usually specified as std::execution::par) are permitted to execute in either the invoking thread or in a thread implicitly created by the library to support parallel algorithm execution. Any such invocations executing in the same thread are indeterminately sequenced with respect to each other.
3、 The execution policy type used as a unique type to disambiguate parallel algorithm overloading and indicate that a parallel algorithm's execution may be parallelized, vectorized, or migrated across threads (such as by a parent-stealing scheduler). The invocations of element access functions in parallel algorithms invoked with this policy are permitted to execute in an unordered fashion in unspecified threads, and unsequenced with respect to one another within each thread.
4、 The execution policy type used as a unique type to disambiguate parallel algorithm overloading and indicate that a parallel algorithm's execution may be vectorized, e.g., executed on a single thread using instructions that operate on multiple data items.
During the execution of a parallel algorithm with any of these execution policies, if the invocation of an element access function exits via an uncaught exception, std::terminate is called, but the implementations may define additional execution policies that handle exceptions differently.

它们对应的库中的指示方式为:

std::execution::seq
std::execution::par
std::execution::par_unseq
std::execution::unseq

在上面的英文说明中也已经有所体现。
其实可以简单理解为:
1、执行可不并行化,数据调用访问即函数访问在调用线程中非顺序
2、执行可以并行,数据调用函数可以并行线程中进行,同一线程中这种调用彼此无顺序相关
3、执行可并行、向量化并允许线程窃取(迁移),线程间和函数调用间均无顺序
4、执行可向量化,单个线程上允许操作多个指令
也就是说,无顺序的执行是允许穿插进行的。而其它情况均不可以。所以在使用这种策略时,要注意不能进行内存分配和释放及相关锁的操作。否则其自动回归到串行执行。
那么使用并行算法和非并行算法有什么不同呢:
首先,处理元素的函数未捕获异常时并行算法会调用std::terminate(),而非并行算法则不会。但并行算法本身一般来说除了std::bad_alloc异常不会抛出其它异常。但非并行算法可以。
其次,非并行算法可以输入和输出迭代器,而并行算法不可以。
再次,非并行算法可以避免意外的副作用和一些额外的辅助功能,但并行算法就不要想了。
在这些算法库中,支持并行的(有的可能需要简单完善)主要有:

none_of,for_each,for_each_n,find,find_if,find_end,find_first_of,adjacent_find,
count,count_if,mismatch,equal,search,search_n,copy,copy_n,copy_if,move,
swap_ranges,transform,replace,replace_if,replace_copy,replace_copy_if,fill,
fill_n,generate,generate_n,remove,remove_if,remove_copy,remove_copy_if,unique,
unique_copy,reverse,reverse_copy,rotate,rotate_copy,is_partitioned,partition,
stable_partition,partition_copy,sort,stable_sort,partial_sort,partial_sort_copy,
is_sorted,is_sorted_until,nth_element,merge,inplace_merge,includes,set_union,
set_intersection,set_difference,set_symmetric_difference,is_heap,is_heap_until,
min_element,max_element,minmax_element,lexicographical_compare,reduce,
transform_reduce,exclusive_scan,inclusive_scan,transform_exclusive_scan,
transform_inclusive_scan和adjacent_difference

不支持并行的:

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

其中reduce是作为accumulate的并行版本引入的,在并行的算法过程中,也是分类似于交换和结合的操作并行的。这个如果对这些细节有要求,一定要小心,特别是一些浮点类型,看上去可能是允许交换和结合的,但实际在应用中是不可确定的,这才是真正的危险所在。

三、例程

下面看一个例子:

#include <algorithm>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <random>
#include <vector>//#define PARALLEL#ifdef PARALLEL
#include <execution>
namespace execution = std::execution;
#else
enum class execution { seq, unseq, par_unseq, par };
#endifvoid measure([[maybe_unused]] auto policy, std::vector<std::uint64_t> v)
{const auto start = std::chrono::steady_clock::now();
#ifdef PARALLELstd::sort(policy, v.begin(), v.end());
#elsestd::sort(v.begin(), v.end());
#endifconst auto finish = std::chrono::steady_clock::now();std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(finish - start)<< '\n';
};int main()
{std::vector<std::uint64_t> v(1'000'000); //设置一个百万大小的容器std::mt19937 gen{ std::random_device{}() };//std::random_device是均匀分布整数随机数生成器,创建随机种子std::ranges::generate(v, gen);//数字填充到容器measure(execution::seq, v);measure(execution::unseq, v);measure(execution::par_unseq, v);measure(execution::par, v);
}

运行结果是:

// not #define PARALLEL
78ms
72ms
88ms
76ms//#define PARALLEL
74ms
85ms
27ms
19ms

这个结果在不同的平台和不同的编译环境下运行产生的效果可能有所不同,大家不要计较。
这个例程使用的是std::sort函数,另外还有不少的算法函数可以使用并行算法。比如常见的for_each,count_if等等。可以参见上一节的说明。

四、发展

在未来的发展中,相信STL库会更多的融入和使用并行库,特别是很有可能把一些优秀的并行框架集成到STL中,这个在前面又不是没有先例。如果一个普通的STL的库函数,可以轻松的使用并行框架并且能够达到一个很优秀的效率的话,估计c++的编程难度会降低不少。
但是目前一个并行框架加入到STL都一直在坎坷的推进着,所以要到库函数级别的轻松应用,估计还得需要一大段时间。毕竟c++的标准是以三年一个迭代周期,这个版本没有,下一个版本就得三年后,再加上编译器的支持,一般来说没个四五年搞不定。且看且行吧。

五、总结

最近工程上可能会优化一些代码用到这些并行的库函数,所以提前分析整理一下,到时候儿看用到哪个就用哪个。“不打无准备之仗”这句话非常有道理,与诸君共勉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/169978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于WSL2+Docker+VScode搭建机器学习(深度学习)开发环境

基于WSL2DockerVScode搭建机器学习(深度学习)开发环境 内容概述&#xff1a;由于最近配发了新的工作电脑但不想装双系统&#xff0c;因此通过本博文来记录基于Windows子系统WSLDocker搭建机器学习与深度学习开发环境的流程步骤&#xff0c;同时记录该过程中所遇到的相关问题及解…

交流充电桩与直流充电桩的区别

1、背景 直流充电桩的学名是非车载充电机&#xff0c;是相对于交流充电桩而言的。交流充电桩是采用传导方式为具备车载充电机的电动汽车提供交流电能的专用装置。 2、交流充电桩和直流充电桩 1.1、交流充电桩 交流充电桩包括单相和三相交流充电桩。 图一是交流充电桩原理框…

使用数据集对SegFormer模型进行微调以改进自动驾驶车辆的车道检测-附源码下载

SegFormer:细分严重影响了高级驾驶辅助系统的开发。它在自动驾驶汽车技术的快速发展中发挥了关键作用。它由多个复杂的组件组成。对于任何在道路上行驶的车辆来说,车道检测至关重要。车道是道路上的标记,有助于区分道路上的可行驶区域和不可行驶区域。当前一代有多种车道检测…

JMeter 设置请求头信息的详细步骤

在使用 JMeter 的过程中&#xff0c;我们会遇到需要设置请求头信息的场景。比如&#xff1a; POST 传过去的 Body 数据是 json 格式的。需要填添加头信息&#xff1a;Content-Type&#xff1a;application/json。 在 header 中用 token 来传用户的认证信息。 下面&#xff0c;…

5.1 Windows驱动开发:判断驱动加载状态

在驱动开发中我们有时需要得到驱动自身是否被加载成功的状态&#xff0c;这个功能看似没啥用实际上在某些特殊场景中还是需要的&#xff0c;如下代码实现了判断当前驱动是否加载成功&#xff0c;如果加载成功, 则输出该驱动的详细路径信息。 该功能实现的核心函数是NtQuerySys…

位图和布隆过滤器

目录 一. 位图 1.题目&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中&#xff1f; 2.解析题目&#xff1a; 3.位图 4.代码以及测试 5.其他题目 二.布隆过滤器 1.介绍 2.实现 …

异步组件与函数式组件

在异步组件中&#xff0c;“异步”二字指的是&#xff0c;以异步的方式加载并渲染一个组件。这在代码分割、服务端下发组件等场景中尤为重要。而函数式组件允许使用一个普通函数定义组件&#xff0c;并使用该函数的返回值作为组件要渲染的内容。函数式组件的特点是&#xff1a;…

Java如何使用jwt进行登录拦截和权限认证

登录如下 登录拦截 拦截如下 权限认证

electron 设置开机自启动后 托盘图标无法显示

问题描述 electron 设置开机自启动后 托盘图标无法显示 问题解决 tray new Tray(path.join(__dirname, ./public/logo.png)); //必须是绝对路径和扩展名&#xff0c;像.png等我的问题是图标之前设置为相对路径&#xff0c;而导致无法显示。将Tray的图标路径设定为绝对路径后…

【Docker】从零开始:12.容器数据卷

【Docker】从零开始&#xff1a;12.容器数据卷 1.什么是容器数据库卷2.数据的覆盖问题3.为什么要用数据卷4.Docker提供了两种卷&#xff1a;5.两种卷的区别6.bind mount7.Docker managed volumevolume 语法volume 操作参数 1.什么是容器数据库卷 卷 就是目录或文件&#xff0c…

数据结构之栈与队列习题详解解析

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.前言 2.概念题…

python_selenium自动化测试框架

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

springboot程序启动成功后执行的方法

//实现该接口&#xff0c;run方法既程序启动成功后将要执行的方法 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) //package org.springframework.boot;FunctionalInterface public interface CommandLineRunner {…

nginx的n种用法(nginx安装+正向代理+反向代理+透明代理+负载均衡+静态服务器)

nginx的安装 一、安装依赖 # 一键安装四个依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel二、安装nginx yum install nginx三、检查是否安装成功 nginx -v四、启动/停止nginx /etc/init.d/nginx start /etc/init.d/nginx stop五、编辑配置文件…

重生之我是一名程序员 41 ——字符串函数(2)

哈喽啊大家晚上好&#xff01;今天呢我们延续昨天的内容&#xff0c;给大家带来第二个字符串函数——strcat函数。 首先呢&#xff0c;还是先带大家认识一下它。strcat函数是C语言中用于将两个字符串连接起来的函数&#xff0c;其函数原型为&#xff1a; char *strcat(char *…

JAVA毕业设计112—基于Java+Springboot+Vue的宠物领养社区小程序(源码+数据库)

基于JavaSpringbootVue的宠物领养社区小程序(源码数据库)112 一、系统介绍 本系统前后端分离带小程序 小程序&#xff08;用户端&#xff09;&#xff0c;后台管理系统&#xff08;管理员&#xff09; 小程序&#xff1a; 登录、注册、宠物领养、发布寻宠、发布领养、宠物社…

算法设计与实现--分治篇

什么是分治算法 分治算法是一种常见的问题解决方法&#xff0c;它将一个复杂的问题划分为多个相同或相似的子问题&#xff0c;然后递归地解决这些子问题&#xff0c;最后将子问题的解合并得到原问题的解。 分治算法通常包含三个步骤&#xff1a; 分解&#xff08;Divide&…

MongoDB的部署

MongoDB部署 基于Linux平台 前置要求 在Centos7 64位系统中安装64位的MongoDB的安装包&#xff0c;通过访问MongoDB官网https://www.mongodb.com/download-center/community进入MongoDB下载页面。Version&#xff1a;指定MongoDB版本&#xff0c;MongoDB的版本分为稳定版和开发…

5.3每日一题(不确定正负号的级数敛散性:和一个正项级数比较判定)

比较判别法和比较判别法的极限形式是对正项级数而言的&#xff0c;若一个级数和p级数比较&#xff0c;结果>0&#xff0c;则同敛散&#xff1b;若结果<0&#xff0c;则结果乘以-1 结果又同敛散了&#xff1b;所以只要比值不等于0&#xff0c;则同敛散&#xff1b; 所以当…

JVM虚拟机:G1垃圾回收器的日志分析

本文重点 本文我们将学习G1垃圾回收器的日志 使用 执行命令 java -Xms20M -Xmx20M -XX:PrintGCDetails -XX:UseG1GC 类名 分析 前面我们学习了G1垃圾回收器&#xff0c;它的回收有三种可能&#xff1a; YGC FGC MixedGC GC pause表示STW,Evacuation表示复制对象&#xff0c;…