STL的并行遍历:for_each(依赖TBB)和omp parallel

news2024/11/20 2:44:18

文章目录

    • OMP parallel
      • OpenMP安装
      • OpenMP示例
        • 1) OMP Hello World
        • 2) OMP for 并行
        • 3. OMP 官方示例
        • 4) map使用OMP遍历
    • TBB的安装和使用
      • Gcc9的安装
      • TBB 安装
      • TBB使用

在图像处理等应用中,我们经常需要对矩阵,大数量STL对象进行遍历操作,因此并行化对算法的加速也非常重要。
除了使用opencv提供的**parallel_for_**函数可对普通STL容器进行并行遍历,如vector。
参见 https://blog.csdn.net/weixin_41469272/article/details/126617752
本文介绍其他两种并行办法。 TBB和OMP

OMP parallel

OpenMP安装

sudo apt install libomp-dev

OpenMP示例

1) OMP Hello World

OMP是相对使用较为简洁的并行工具,仅需在需要并行的语句前加入#pragma omp parallel,便可实现并行。

      #pragma omp parallel
      {
         每个线程都会执行大括号里的代码
      }

说明:以下出现c++代码c的写法
参考:https://blog.csdn.net/ab0902cd/article/details/108770396
https://blog.csdn.net/zhongkejingwang/article/details/40350027
omp_test.cpp

#include <omp.h>

int main(){
    printf("The output:\n");
    #pragma omp parallel     /* define multi-thread section */
    {
        printf("Hello World\n");
    }
    /* Resume Serial section*/
    printf("Done\n");
}
g++ omp_test.cpp -fopenmp -o omptest
./test

Result:

The output:
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Done

2) OMP for 并行

当需要将for循环并行,则可在for语句之前加上:#pragma omp parallel for

int main(int argc, char *argv[]) {
  int length = 6;
  float *buf = new float[length];
  #pragma omp parallel for num_threads(3)
     for(int i = 0; i < length; i++) {
       int tid = omp_get_thread_num();

       printf("i:%d is handled on thread %d\n", i, tid);
       buf[i] = i;
     }
}

其中num_threads用于指定线程个数。
Result

i:0 is handled on thread 0
i:1 is handled on thread 0
i:4 is handled on thread 2
i:5 is handled on thread 2
i:2 is handled on thread 1
i:3 is handled on thread 1

3. OMP 官方示例

#include <stdlib.h>   //malloc and free
#include <stdio.h>    //printf
#include <omp.h>      //OpenMP

// Very small values for this simple illustrative example
#define ARRAY_SIZE 8     //Size of arrays whose elements will be added together.
#define NUM_THREADS 4    //Number of threads to use for vector addition.

/*
 *  Classic vector addition using openMP default data decomposition.
 *
 *  Compile using gcc like this:
 *      gcc -o va-omp-simple VA-OMP-simple.c -fopenmp
 *
 *  Execute:
 *      ./va-omp-simple
 */
int main (int argc, char *argv[])
{
    // elements of arrays a and b will be added
    // and placed in array c
    int * a;
    int * b;
    int * c;

    int n = ARRAY_SIZE;                 // number of array elements
    int n_per_thread;                   // elements per thread
    int total_threads = NUM_THREADS;    // number of threads to use  
    int i;       // loop index

        // allocate spce for the arrays
    a = (int *) malloc(sizeof(int)*n);
    b = (int *) malloc(sizeof(int)*n);
    c = (int *) malloc(sizeof(int)*n);

        // initialize arrays a and b with consecutive integer values
    // as a simple example
        for(i=0; i<n; i++) {
            a[i] = i;
        }
        for(i=0; i<n; i++) {
            b[i] = i;
        }

    // Additional work to set the number of threads.
    // We hard-code to 4 for illustration purposes only.
    omp_set_num_threads(total_threads);

    // determine how many elements each process will work on
    n_per_thread = n/total_threads;

        // Compute the vector addition
    // Here is where the 4 threads are specifically 'forked' to
    // execute in parallel. This is directed by the pragma and
    // thread forking is compiled into the resulting exacutable.
    // Here we use a 'static schedule' so each thread works on  
    // a 2-element chunk of the original 8-element arrays.
    #pragma omp parallel for shared(a, b, c) private(i) schedule(static, n_per_thread)
        for(i=0; i<n; i++) {
        c[i] = a[i]+b[i];
        // Which thread am I? Show who works on what for this samll example
        printf("Thread %d works on element%d\n", omp_get_thread_num(), i);
        }

    // Check for correctness (only plausible for small vector size)
    // A test we would eventually leave out
    printf("i\ta[i]\t+\tb[i]\t=\tc[i]\n");
        for(i=0; i<n; i++) {
        printf("%d\t%d\t\t%d\t\t%d\n", i, a[i], b[i], c[i]);
        }

        // clean up memory
        free(a);  free(b); free(c);

    return 0;
}

Result:
在这里插入图片描述
其中,shared括号中说明所有线程公用的变量名,private括号中的变量为各个线程均独立的变量。
schedule()用于指定循环的线程分布策略,默认为static。
具体不同:
schedule(kind [, chunk_size])

kind:
• static: Iterations are divided into chunks of size chunk_size. Chunks are assigned to threads in the team in round-robin fashion in order of thread number.
• dynamic: Each thread executes a chunk of iterations then requests another chunk until no chunks remain to be distributed.
• guided: Each thread executes a chunk of iterations then requests another chunk until no chunks remain to be assigned. The chunk sizes start large and shrink to the indicated chunk_size as chunks are scheduled.
• auto: The decision regarding scheduling is delegated to the compiler and/or runtime system.
• runtime: The schedule and chunk size are taken from the run-sched-var ICV

static: OpenMP会给每个线程分配chunk_size次迭代计算。这个分配是静态的,线程分配规则根据for的遍历的顺序。
dynamic:动态调度迭代的分配是依赖于运行状态进行动态确定的,当需要分配新线程时,已有线程结束,则直接使用完成的线程,而不开辟新的线程。
guided:循环迭代划分成块的大小与未分配迭代次数除以线程数成比例,然后随着循环迭代的分配,块大小会减小为chunk值。chunk的默认值为1。开始比较大,以后逐渐减小。
runtime:将调度决策推迟到指定时开始,这选项不能指定块大小?(暂未测试)

参考:
https://blog.csdn.net/gengshenghong/article/details/7000979
https://blog.csdn.net/yiguagua/article/details/107053043

4) map使用OMP遍历

关于invalid controlling predicate的问题
OMP不支持终止条件为“!=”或者“==”的for循环,因为无法判断循环的数量。

int main()
{
  map<int,int> mii;
  map<int, string> mis;
  for (int i = 0; i < 50; i++) {mis[i] = to_string(i);}

  clock_t start,end;
  start = clock();

#if 1
  mutex m;
  auto it = mis.begin();
  #pragma omp parallel for num_threads(3) shared(it)
  //Error '!=" can not be used in omp: invalid controlling predicate
  for (int i = 0; i < mis.size(); i++)
  {
    int tid = omp_get_thread_num();
    m.lock();
    mii[it->first] = atoi(it->second.c_str());
    cout << "Thread " << tid << " handle " << it->first << endl;
    m.unlock();
    it++;
  }

#else
  for (auto it : mis)
  {
    int tid = omp_get_thread_num();
    mii[it.first] = atoi(it.second.c_str());
    cout << "Thread " << tid << " handle " << it.first << endl;
  }
#endif

  end = clock();
  cout<<"time = "<<double(end-start)/CLOCKS_PER_SEC<<"s"<<endl;


  for (auto it = mii.begin(); it != mii.end(); it++)
  {
    cout << "it->first: " << it->first << " it->second: " << it->second << endl;
  }
}

Result:

加OMP:time = 0.000877s
不加并行:time = 0.001862s

TBB的安装和使用

关于Intel的oneTBB工具与g++版本相互制约,因此在安装时较为麻烦
以下测试选择的工具版本:
TBB:v2020.0
Gcc:9.4

Gcc9的安装

sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install gcc-9 g++-9

TBB 安装

wget https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2020.0.tar.gz
tar zxvf v2020.0.tar.gz
cd oneTBB

cp build/linux.gcc.inc build/linux.gcc-9.inc
修改 build/linux.gcc-9.inc 15,16 行:
CPLUS ?= g++-9
CONLY ?= gcc-9 

#build
make compiler=gcc-9 stdver=c++17 -j20 DESTDIR=install tbb_build_prefix=build

#***************************** TBB examples build *****************************************
#build test code:
g++-9 -std=c++17  -I ~/Download/softpackages/oneTBB/install/include/ -L/home/niebaozhen/Download/    softpackages/oneTBB/install/lib/ std_for_each.cpp -ltbb -Wl,-rpath=/home/niebaozhen/Download/soft    packages/oneTBB/install/lib/

参考链接:https://blog.csdn.net/weixin_32207065/article/details/112270765

Tips:
当TBB版本大于v2021.1.1时,cmake被引入,但是该版本TBB不支持gcc9/10
但是gcc版本高等于9时,才支持对TBB的使用,且编译标准建议为c++17。

说明链接
v2021.1.1版本编译命令

#tbb version >= v2021.1.1: cmake employed, however,
#libc++9&10 are incompatible with TBB version > 2021.xx

mkdir build install
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install/ -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_COMPILER=/usr/bin/g++-9  -DTBB_TEST=OFF ..cmake -DCMAKE_INSTALL_PREFIX=../install/  -DTBB_TEST=OFF ..
make -j30
make install

TBB使用

std_for_each.cpp

#include <iostream>
#include <unistd.h>
#include <map>
#include <algorithm>
#include <chrono>

#define __MUTEX__ 0
#if __MUTEX__
#include <mutex>
#endif

#if __GNUC__ >= 9
#include <execution>
#endif

using namespace std; 


int main ()
{
  //cout << "gnu version: " << __GNUC__ << endl;
  int a[] = {0,1,3,4,5,6,7,8,9};
  
  map<int, int> mii;
  #if __MUTEX__
  mutex m;
  #endif  
 
  auto tt1 = chrono::steady_clock::now();

  #if __GNUC__ >= 9
  for_each(execution::par, begin(a), std::end(a), [&](int i) {
  #else
  for_each(begin(a), std::end(a), [&](int i) {
  #endif
    #if __MUTEX__
    lock_guard<mutex> guard(m);
    #endif
    mii[i] = i*2+1;
    //sleep(1);
    //cout << "Sleep one second" << endl;
  }); 

  auto tt2 = chrono::steady_clock::now();
  auto dt = chrono::duration_cast<chrono::duration<double>>(tt2 - tt1);

  cout << "time = " << dt.count() << "s" <<endl;

  for(auto it = mii.begin(); it != mii.end(); it++) {
    cout << "mii[" << it->first << "] = " << it->second << endl;
  }   
}

build:

g++ std_for_each.cpp
或:
g++-9 -std=c++17  -I ~/Download/softpackages/oneTBB/install/include/ -L/home/niebaozhen/Download/softpackages/oneTBB/install/lib/ std_for_each.cpp -ltbb -Wl,-rpath=/home/niebaozhen/Download/softpackages/oneTBB/install/lib/

Result:
在这里插入图片描述
可以看出,当遍历所操作的工作比较少时,并行反而会带来更多的耗时。
当遍历的操作较多,这里sleep来模拟较多的工作,并行体现出优势。
总之:

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

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

相关文章

R语言与作物模型(以DSSAT模型为例)融合应用

随着基于过程的作物生长模型&#xff08;Process-based Crop Growth Simulation Model&#xff09;的发展&#xff0c;R语言在作物生长模型和数据分析、挖掘和可视化中发挥着越来越重要的作用。想要成为一名优秀的作物模型使用者与科研团队不可或缺的人才&#xff0c;除了掌握对…

MySQL存储过程 if、case、while、loop、游标、变量、条件处理程序

存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合&#xff0c;调用存储过程可以简化很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。 存储过程思想上很简单&#xff0c;就是数据库 SQL 语言层面的代…

barret reduction原理详解及硬件优化

背景介绍 约减算法&#xff0c;通常应用在硬件领域&#xff0c;因为模运算mod是一个除法运算&#xff0c;在硬件中实现速度会比乘法慢的多&#xff0c;并且还会占用大量资源&#xff0c;因此需要想办法用乘法及其它简单运算来替代模运算。模约减算法可以利用乘法、加法和移位等…

怎么评价2023年第十三届MathorCup高校数学建模挑战赛?

文章目录赛题思路选题建议1 竞赛信息2 竞赛时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; 选题建议 首先要注意&#xff0c;A、B题为研究生组可选题目&#xff0c;A…

还在用 if else 做参数校验?快来学习高级参数校验吧

文章目录一、前言二、自定义校验2.1 定义 GenderArrayValuable 接口2.2 定义性别 GenderEnum 枚举类2.3 自定义 GenderCheck 自定义约束注解2.4 自定义约束的校验器 GenderValidator2.5 定义 UserUpdateGenderDTO2.6 定义一个对外访问接口2.7 请求接口 进行验证三、总结一、前言…

从C出发 17 --- 函数调用

从表面上来看&#xff0c;函数就是一个代码片段&#xff0c;只不过说这个代码片段可以反复利用&#xff0c;通过调用的方式反复利用&#xff0c;通过函数调用&#xff0c;我们可以将参数传到函数所对应的代码片段里面&#xff0c;然后代码片段去处理这些参数&#xff0c;得到一…

Linux下Samba服务器的安装与配置(简单实用)

为了可以实现Linux与windows之间实现文件的共享&#xff0c;方便文件可以直接修改&#xff0c;而不是像以前需要拷贝文件再进行修改&#xff0c;samba的诞生是为了实现现在的这些需求。我们知道Linux之间可以使用NFS服务器来实现文件的共享&#xff0c;samba的诞生就是为了使wi…

Spring Cloud Security

Spring Cloud Security Spring Cloud Security用于构建微服务的安全应用程序和服务&#xff0c;它可以轻松实现微服务系统架构下的统一安全认证与授权。 Spring Cloud Security 有以下组件。 spring-cloud-security&#xff1a;为Zuul、Feign、Oauth 2.0 的Resource Serve…

ChatGPT5是否会影响人类的发展和工作?

目录前言ChatGPT5是什么ChatGPT5 的潜在影响挑战与风险总结前言 ChatGPT的普及也带来了大量的讨论&#xff0c;关于它是否会影响人类的发展和工作。本文将讨论 ChatGPT5 如何可能改变人类的工作和发展&#xff0c;以及潜在的利弊和挑战。在话题开始之前&#xff0c;让我们先从…

QxOrm的使用-数据操作--增删改查

文章目录QxOrm的使用-数据操作使用QxOrm对数据库进行增删改查新增数据删除数据修改数据查询数据QxOrm的使用-数据操作 上一篇我们讲了QxOrm的基本的数据映射操作&#xff0c;这里面再补充一点东西 数据类型映射 Qt/C类型数据库类型boolSMALLINTqx_boolSMALLINTshortSMALLINT…

【谷粒商城之整合阿里云OSS对象存储】

本笔记内容为尚硅谷谷粒商城整合阿里云OSS对象存储部分 目录 一 、简介 二、云存储开通与使用 1、开通阿里云对象存储服务 2、创建bucket 3、创建子用户&#xff08;获取密钥访问OSS服务器&#xff09; 给该子账户添加权限 4、阿里云对象存储上传方式 三、整合 1、…

BUUCTF--Web篇详细wp

BUUCTF--Web篇详细wp[极客大挑战 2019]EasySQL[极客大挑战 2019]Havefun[HCTF 2018]WarmUp[ACTF2020 新生赛]Include[ACTF2020 新生赛]Exec[强网杯 2019]随便注[GXYCTF2019]Ping Ping Ping[SUCTF 2019]EasySQL[极客大挑战 2019]Secret File[极客大挑战 2019]LoveSQL[极客大挑战…

MySQL 分布式数据库实现:无需修改代码,轻松实现分布式能力

这个项目做什么 ShardingSphere-Proxy&#xff0c;可以让用户像使用原生数据库一样使用 Apache ShardingSphere。 了解一项技术的开始&#xff0c;一般从官网开始。先来看一看官网对 ShardingSphere-Proxy 的定义是什么样的&#xff1a; 定位为透明化的数据库代理端&#xff…

异配图神经网络——Graph Transformer Networks

一.论文概述 作者提出了Graph Transformer Network (GTN)用来在异配图&#xff08;heterogeneous graph&#xff09;上学习节点表示。通过Graph Transformer层&#xff0c;模型能将异构图转换为由meta-path定义的多个新图&#xff0c;这些meta-paths具有任意的边类型和长度&am…

运行Spring Boot项目时[ java: 错误: 不支持发行版本 17 ]

项目场景&#xff1a; 使用IDEA的Spring Initializr构建的Spring boot项目在构建完成后运行出错 问题描述&#xff1a; 用Spring Initializr创建了Spring Boot 项目后&#xff0c;运行时报错&#xff1a; “错误:java: 错误: 不支持发行版本 17”根据错误信息得知&#xff…

Vue学习笔记(5. 计算属性,监视器(侦听器))

1. 计算属性&#xff08;computed&#xff09; (1) get方式 初期显示 改变值后&#xff08;hello -> hello1&#xff09;计算属性allStr跟随变更 (2) get set方式 页面初期显示 改变值&#xff08;hello -> hello1&#xff09;计算属性的get会监控到变更&#xff0c;使…

国产SSD、内存卷哭国外大厂,三星宣布减产涨价在路上了

PC 圈有一句话是这么说的&#xff1a;论价格屠夫还得看国产品牌&#xff01; 可不是嘛&#xff0c;国产长鑫、长江算是彻底将全球存储芯片市场搅局者这一「骂名」坐实了&#xff01; 不说特别早期&#xff0c;前几年吧&#xff0c;普通单条 8G DDR4 内存都能卖到六七百元&…

C++ 红黑树

1.红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;因…

D. Many Perfect Squares

题目链接&#xff1a;Problem - D - Codeforces 题意&#xff1a;给你一个数组&#xff0c;大小不超过50个。 问你让他们全部加上一个x&#xff0c;构造出来最多能够有多少个完全平方数。 思路&#xff1a; 先对数组排个序&#xff0c;首先它最少一定是有一个的&#xff0c…

Git配置SSH步骤

一、git 配置 &#xff08;1&#xff09;打开 git 命令窗口 &#xff08;2&#xff09;配置用户名&#xff08;填自己的姓名&#xff09; git config --global user.name “linjiaxiaozhu” &#xff08;3&#xff09;配置用户邮箱&#xff08;填自己的邮箱&#xff09; git…