精华内容
下载资源
问答
  • C++并行编程实战

    2018-09-24 22:09:31
    C++并行编程实战,英文版,重点讲解C++ 2011里面的thread mutex lock,原子类型等等
  • C++并行程序设计

    2018-09-08 18:32:02
    C++并行程序设计 仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
  • c++并行开发,c++11新标准介绍并行开发编程,c++程序员四部曲之一。里面包含两个文件,解压就可以了,一个是英文原版,一个是中文翻译版
  • C++ Concurrency In Action的中文翻译,称作C++并行编程实践,基于c++11新标准的并发和多线程编程指南。
  • [深入C++系列]C++并行与分布式编程_肖和平2004译.rar [深入C++系列]C++并行与分布式编程_肖和平2004译.rar
  • c++ 并行编程

    万次阅读 2017-05-02 17:30:52
    本博客将看C++并行编程的例子 首先看如何看 #include #include using namespace std; int main() { clock_t startTime,endTime; startTime = clock(); for (int i = 0; i ; i++) { i++; }

    本博客将看C++并行编程的例子

    1. 线程进程原理

    线程是轻量级的进程,一个进程可以拥有多个线程。

    编译多线程程序加入 <pthread.h> 

    g++ -lphread

    2. openmp库加速

    2.1 openmp库加速配置及hello,world

    事实上有个openmp库,可以实现单台cpu的加速

    Windows下使用vs
    Configuration Properties->C/C++->Language->OpenMP Support,在下拉菜单里选择Yes。然后才能使用OpenMP
    并行代码为例,只需要把相应的行注释掉,就是串行代码了
    默认情况下,并行区内线程数=系统中核的个数

    在Linux下使用g++即可

    #include <stdio.h>
    #include "omp.h"
    int main()
    {
      #pragma omp parallel
      printf( "Hello world!\n" );
      return 0;
    }

    g++ test.cpp
    ./a.out
    输出一个
    g++ -fopenmp  test.cpp
    ./a.out
    输出12个,默认为cpu的逻辑核数(=cpu块数*cpu物理核数*(开启超线程*2))
    export OMP_NUM_THREADS=6
    ./a.out
    输出6个

    一旦采用了OpenMP,线程数量就将由编译器来决定(而不是您),因此无论线程数量如何,重要的是使程序能够正常运行

    2. 实战测试

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <math.h>
    
    //一个简单的耗时任务
    double Sum0(double* data,long data_count);
    
    int main()
    {
        long data_count=200000;
        double* data=new double[data_count];
        long i;
    
        //初始化测试数据 
        for (i=0;i<data_count;++i)
            data[i]=(double)(rand()*(1.0/RAND_MAX));
    
        const long test_count=200*2;//为了能够测量出代码执行的时间,让函数执行多次
        double sumresult=0;
        double runtime=(double)clock();
        for( i=0; i<test_count; ++i ) 
        {
            sumresult+=Sum0(data,data_count);
        }
        runtime=((double)clock()-runtime)/CLOCKS_PER_SEC;
        printf ("< Sum0 >  ");
        printf ("  最后结果     = %10.4f  ",sumresult);
        printf ("  执行时间(秒) = %f  ",runtime);
    
        delete [] data;
        return 0;
    }
    
    
    double Sum0(double* data,long data_count)
    {
        double result=0;
        #pragma omp parallel for schedule(static) reduction(+: result)
        for (long i=0;i<data_count;++i)
        {
            data[i]=(double)sin(cos(data[i]));
            result+=data[i];
        }
        return  result;
    }
    
    在windows上

    是否 OMP_NUM_TREADS 时间

    无                     3.437s

    有        2           1.586

    有        4            1.349

    有        8             1.46

    在linux上

    无           2.9036

    有   12   13.9541

    有    6      6.792




    可以看到windows上做了将近三倍的加速,但是在linux上速度降低。可能是编程的方式不对。

    下面将对其进行分析,希望能实现12倍加速



    首先看如何看时间

    #include <time.h>
    #include <iostream>
    using namespace std;
    
    
    int main()
    {
        clock_t startTime,endTime;
        startTime = clock();
        for (int i = 0; i < 10000000; i++)
        {
            i++;
        }
        endTime = clock();
        cout << "Totle Time : " <<(double)(endTime - startTime) / (CLOCKS_PER_SEC*1000) << "ms" << endl;
        return 0;
    }
    

    在linux上运行能看到结果 

    [1] linux下查看核数

    [2] windows下查看核数

    [3] windows下openmp的配置 

    [4] linux下openmp编程基础 

    [5] c++并行与分布式编程[书籍]

    展开全文
  • 内附C++并行与分布式编程_肖和平2004译.pdf
  • C++并行与分布式编程英文版

    热门讨论 2010-02-26 15:03:20
    C++并行与分布式编程 英文版 C++并行与分布式编程 英文版
  • c++并行编程》是中文版的名字,原名为"C++ Concurrency in Action",第一本讲述c++ 11标准中多线程并行编程的书籍,作者又是boost库多线程的重要贡献者,C++11的多线程标准大多来自于boost库,因此,此书具有很高的...
  • 微软C++并行库 pplx 的基本用法

    万次阅读 2019-01-31 13:34:58
    并行计算库充分利用多核的优势,通过并行运算提高程序效率,业界有两个知名的c++并行库,一个是intel开发的TBB,一个是微软开发的PPL。 TBB(Intel® Threading Building Blocks ) TBB是intel用标准c++写的一个开源...

    前言

    并行计算库充分利用多核的优势,通过并行运算提高程序效率,业界有两个知名的c++并行库,一个是intel开发的TBB,一个是微软开发的PPL

    TBB(Intel® Threading Building Blocks )

    TBBintel用标准c++写的一个开源的并行计算库。它的目的是提升数据并行计算的能力,可以在其官网下载最新的库和文档。TBB主要功能:并行算法、任务调度、并行容器、同步原语、内存分配器。

    PPL(Parallel Patterns Library)

    PPL是微软开发的并行计算库,它的功能和TBB是差不多的,主要是在windows上使用。二者在并行算法的使用上基本上是一样的, 但还是有些差异:TBBtask没有PPLtask强大,PPLtask可以链式连续执行还可以组合任务,TBBtask则不行。

    PPL C++ 库与 C# 并行库TaskParallelLibrary的设计理念、基本框架以及接口使用上非常类似,熟悉C#并行库的朋友上手C++版的PPL非常容易。下面我将介绍微软跨平台PPL的一个简易实现pplx,该库是附在微软的开源项目 cpprestsdk 中的。

    pplx 并行库

    C++ REST SDK 是 Microsoft 的一个开源跨平台项目, 其使用大量现代异步 C++ API 实现了一个基于 HTTP / HTTPS 协议的 B/S 组件,使用该组件,可以方便地进行高性能RESTfulHTTP / HTTPS 服务器、客户端开发,且可以在WindowsLinuxOSXiOSAndroid各平台下使用。

    当然今天我要介绍的主角是该项目中的并行库PPLX。下面先介绍如何编译安装cpprestsdk,然后介绍如何使用并行库PPLX。以下都是在 Ubuntu 系统上进行。

    编译安装

    有两种方式可以安装cpprestsdk,一种是直接用 apt-get 安装,另一种是从源码安装。

    通过 apt-get 安装

    sudo apt-get install libcpprest-dev
    

    从 source 编译安装

    ref: How to build for Linux

    1, 系统要求: Ubuntu 16.04 及之后的版本

    2, 安装必要的工具:boost库,ninja 用于编译,

    sudo apt-get install g++ git libwebsocketpp-dev openssl libssl-dev ninja-build
    
    sudo apt-get install libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev 
    

    3, 下载代码

    git clone https://github.com/Microsoft/cpprestsdk.git casablanca
    

    4, 编译:

    cd casablanca
    mkdir build.release
    cd build.release
    cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release
    ninja
    

    如果想编译成 debug 版本,把上面代码中的 release/Release 修改为 debug/Debug 即可。

    5, 编译完成之后,跑一下 test_runner 测试验证一下:

    cd Release/Binaries
    ./test_runner *_test.so
    

    或者运行 bing 搜索示例:

    cd Release/Binaries
    ./BingRequest kesalin kesalin.html
    

    6, 安装:

    sudo ninja install
    sudo ldconfig
    

    7, 编译单个文件的参数:

    g++ -std=c++11 my_file.cpp -o my_file -lboost_system -lcrypto -lssl -lcpprest
    ./my_file
    

    使用 pplx 并行库

    创建并运行任务

    可以通过多种途径创建任务:

    //构造函数
    auto task = pplx::task<int>([](){
        return 10;
    });
     
    //lambda
    auto task = []()->pplx::task<int>{
        return pplx::task_from_result(10);
    };
     
    //create_task
    auto task = pplx::create_task([](){
        return 10;
    });
     
    //create_task 创建延迟任务
    pplx::task_completion_event<int> tce;// task_completion_event 需按值传递
    auto task = pplx::create_task(tce);
    

    也可以创建任务链:

    pplx::task<std::string> create_print_task(const std::string& init_value)
    {
        return pplx::create_task([init_value]() {
            std::cout <<"Current value:" << init_value << std::endl;
            return std::string("Value 2");
        })
    
        .then([](std::string value) {
            std::cout << "Current value:" << value << std::endl;
            return std::string("Value 3");
        })
    
        .then([](std::string value) {
            std::cout << "Current value:" << value << std::endl;
            return std::string("Value 4");
        });
    }
    

    使用task.get()或者task.wait()执行任务:

    • 阻塞方式get(): 阻塞直到任务执行完成,并返回任务结果,当任务取消时,抛出task_canceled异常,发生其它异常也会被抛出;
    • 非阻塞方式wait():等待任务到达终止状态,然后返回任务状态:completedcanceled,如果发生异常会被抛出。
    void test_task_chain()
    {
        auto task_chain = create_print_task("Value 1");
        task_chain.then([](std::string value) {
            std::cout << "Result value: " << value << std::endl;
            return value;
        })
    
        // process exception
        .then([](pplx::task<std::string> previousTask) {
            try {
                previousTask.get();
            }
            catch (const std::exception& e) {
                std::cout << "exception: " << e.what() << std::endl;
            }
        })
    
        .wait();
    }
    

    组任务

    可以创建和执行一组任务,根据需要来选择是全部执行再返回,还是执行任一任务就返回。

    • when_all:返回组任务,只有当所有任务都完成时组任务才会返回成功;如果任一任务被取消或者抛出异常,则组任务会完成并处理取消状态,在组任务get()或者wait()时抛出异常。如果任务类型为task<T>,则组任务类型为task<vector<T>>
    • when_any:返回组任务,当任一任务完成时组任务就会返回成功;如果所有任务都被取消或者抛出异常,则组任务会完成并处理取消状态,并且如果任何任务发生异常,在组任务get或者wait时抛出异常。如果任务类型为task<T>,则组任务类型为task<T, size_t>size_t 返回完成任务的索引。
    void test_group_tasks()
    {
        auto sleep_print = [](int seconds, const std::string& info) {
            if (seconds > 0) {
                sleep(seconds);
            }
    
            std::cout << info << std::endl;
        };
    
        auto/*std::array<pplx::task<int>, 3>*/ tasks = {
            pplx::create_task([sleep_print]() -> int { sleep_print(2, "Task 1"); return 1; }),
            pplx::create_task([sleep_print]() -> int { sleep_print(2, "Task 2"); return 2; }),
            pplx::create_task([sleep_print]() -> int { sleep_print(4, "Task 3"); return 3; })
        };
    
        {
            std::cout << "=== when_all ===" << std::endl;
    
            auto joinTask = pplx::when_all(std::begin(tasks), std::end(tasks));
            auto result = joinTask.wait();
            std::cout << "All joined thread. result: " << result << std::endl;
        }
    
        {
            std::cout << "=== when_any ===" << std::endl;
    
            auto joinTask = pplx::when_any(std::begin(tasks), std::end(tasks))
            .then([](std::pair<int, size_t> result) {
                std::cout << "First task to finish returns "
                      << result.first
                      << " and has index "
                      << result.second << std::endl;
            });
    
            auto result = joinTask.wait();
            std::cout << "Any joined thread. result: " << result << std::endl;
        }
    }
    

    取消任务

    cancellation_token_source 通过封装一个 cancellation_token 指针来提供取消操作,通过cancellation_token.is_canceled()在执行任务的过程中判断任务是否要被取消。

    示例中的任务会循环执行,直到显式取消任务:

    void test_cancellation()
    {
        pplx::cancellation_token_source cts;
        std::cout << "Creating task..." << std::endl;
    
        auto task = pplx::create_task([cts]{
            bool moreToDo = true;
            while (moreToDo) {
               if (cts.get_token().is_canceled()) {
                   return;
               }
               else {
                   moreToDo = []()->bool {
                       std::cout << "Performing work at " << now() << std::endl;
                       sleep(1);
                       return true;
                   }();
               }
            }
        });
    
        sleep(3);
    
        std::cout << "Canceling task... " << now() << std::endl;
        cts.cancel();
    
        std::cout << "Waiting for task to complete... " << now() << std::endl;
        task.wait();
    
        std::cout << "Done. " << now() << std::endl;
    }
    

    当要在异步任务链中支持取消时,需要将cancellation_token作为构造task的参数传递,然后结合task.wait()判断是否要取消:

    void test_cancellation_async()
    {
        pplx::cancellation_token_source cts;
        auto task = pplx::task<void>([cts]() {
            std::cout << "Cancel continue_task" << std::endl;
            cts.cancel();
        })
    
        .then([]() {
            std::cout << "This will not run" << std::endl;
        }, cts.get_token());
    
        try {
            if (task.wait() == pplx::task_status::canceled) {
                std::cout<<"Taks has been canceled"<<std::endl;
            }
            else {
                task.get();
            }
        }
        catch (const std::exception& e) {
            std::cout << "exception: " << e.what() << std::endl;
        }
    }
    

    处理异常

    之前说过如果任务发生异常,会在get或者wait时抛出,但是如果希望在异步任务链中判定之前执行是否发生异常做出操作时,可以采用另外的方式。
    当使用task.then时一般是这样写的:

    task<T>.then([](T t){
         //处理任务结果t
    })
    

    这时候进入then时之前的任务已经执行完成了,task.then有另外一种写法,能够在then时并没有执行任务:

    task<T>.then([](task<T> task){
           try 
           {
                  task.get(); //使用get或者wait执行任务
           }
           catch(...)
           {
               //处理异常
           }
    })
    

    示例:

    void test_task_exception()
    {
        auto task_chain = create_print_task("Value 1");
        task_chain.then([](std::string value) {
            // uncomment this line to throw an exception.
            throw std::runtime_error("An exception happened!");
            std::cout << "Result value: " << value << std::endl;
            return value;
        })
    
        // process exception
        .then([](pplx::task<std::string> previousTask) {
            try {
                previousTask.get();
            }
            catch (const std::exception& e) {
                std::cout << "exception: " << e.what() << std::endl;
            }
        })
    
        .wait();
    }
    

    本文完整代码

    // g++ -std=c++11 pplxdemo.cpp -o pplxdemo -lboost_system -lcrypto -lssl -lcpprest
    
    #include <pplx/pplxtasks.h>
    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <functional>
    #include <iomanip>
    #include <ctime>
    #include <thread>
    #include <chrono>
    #include <stdexcept>
    
    std::string now()
    {
        auto t = std::time(nullptr);
        auto tm = *std::localtime(&t);
    
        std::ostringstream oss;
        oss << std::put_time(&tm, "%Y-%m-%d %H-%M-%S");
        auto str = oss.str();
        return str;
    }
    
    void sleep(int seconds)
    {
        std::this_thread::sleep_for(std::chrono::seconds(seconds));
    }
    
    pplx::task<std::string> create_print_task(const std::string& init_value)
    {
        return pplx::create_task([init_value]() {
            std::cout <<"Current value:" << init_value << std::endl;
            return std::string("Value 2");
        })
    
        .then([](std::string value) {
            std::cout << "Current value:" << value << std::endl;
            return std::string("Value 3");
        })
    
        .then([](std::string value) {
            std::cout << "Current value:" << value << std::endl;
            return std::string("Value 4");
        });
    }
    
    void test_task_chain()
    {
        auto task_chain = create_print_task("Value 1");
        task_chain.then([](std::string value) {
            // uncomment this line to throw an exception.
            // throw std::runtime_error("An exception happened!");
            std::cout << "Result value: " << value << std::endl;
            return value;
        })
    
        // process exception
        .then([](pplx::task<std::string> previousTask) {
            try {
                previousTask.get();
            }
            catch (const std::exception& e) {
                std::cout << "exception: " << e.what() << std::endl;
            }
        })
    
        .wait();
    }
    
    void test_group_tasks()
    {
        auto sleep_print = [](int seconds, const std::string& info) {
            if (seconds > 0) {
                sleep(seconds);
            }
    
            std::cout << info << std::endl;
        };
    
        auto/*std::array<pplx::task<int>, 3>*/ tasks = {
            pplx::create_task([sleep_print]() -> int { sleep_print(2, "Task 1"); return 1; }),
            pplx::create_task([sleep_print]() -> int { sleep_print(2, "Task 2"); return 2; }),
            pplx::create_task([sleep_print]() -> int { sleep_print(4, "Task 3"); return 3; })
        };
    
        {
            std::cout << "=== when_all ===" << std::endl;
    
            auto joinTask = pplx::when_all(std::begin(tasks), std::end(tasks));
            auto result = joinTask.wait();
            std::cout << "All joined thread. result: " << result << std::endl;
        }
    
        {
            std::cout << "=== when_any ===" << std::endl;
    
            auto joinTask = pplx::when_any(std::begin(tasks), std::end(tasks))
            .then([](std::pair<int, size_t> result) {
                std::cout << "First task to finish returns "
                      << result.first
                      << " and has index "
                      << result.second << std::endl;
            });
    
            auto result = joinTask.wait();
            std::cout << "Any joined thread. result: " << result << std::endl;
        }
    }
    
    void test_cancellation()
    {
        pplx::cancellation_token_source cts;
        std::cout << "Creating task..." << std::endl;
    
        auto task = pplx::create_task([cts]{
            bool moreToDo = true;
            while (moreToDo) {
               if (cts.get_token().is_canceled()) {
                   return;
               }
               else {
                   moreToDo = []()->bool {
                       std::cout << "Performing work at " << now() << std::endl;
                       sleep(1);
                       return true;
                   }();
               }
            }
        });
    
        sleep(3);
    
        std::cout << "Canceling task... " << now() << std::endl;
        cts.cancel();
    
        std::cout << "Waiting for task to complete... " << now() << std::endl;
        task.wait();
    
        std::cout << "Done. " << now() << std::endl;
    }
    
    void test_cancellation_async()
    {
        pplx::cancellation_token_source cts;
        auto task = pplx::task<void>([cts]() {
            std::cout << "Cancel continue_task" << std::endl;
            cts.cancel();
        })
    
        .then([]() {
            std::cout << "This will not run" << std::endl;
        }, cts.get_token());
    
        try {
            if (task.wait() == pplx::task_status::canceled) {
                std::cout<<"Taks has been canceled"<<std::endl;
            }
            else {
                task.get();
            }
        }
        catch (const std::exception& e) {
            std::cout << "exception: " << e.what() << std::endl;
        }
    }
    
    int main(int argc, char* args[])
    {
        std::cout << "==== pplx demo ====" << std::endl;
        test_task_chain();
        test_group_tasks();
        test_cancellation();
        test_cancellation_async();
        return 0;
    }
    
    展开全文
  • 目录C++ 并行计算 MPI参考 C++ 并行计算 MPI 在处理一个任务的时候,并行程序间需要通信。MPI 是一个跨语言的通信协议,MPICH 是 MPI 的一种实现,类似的实现还有 Open MPI,Intel MPI 等。 参考 [1]...


    简介

    早些年时,大多数的并发程序只出现在科学和研究的领域,最广为接受的模型就是消息传递模型:程序通过在进程间传递消息(消息可以理解成带有一些信息和数据的一个数据结构)来完成某些任务。在 1992 年大会上定义了消息传递接口的标准,也就是 MPIMPI 是一个跨语言的通信协议,MPICHMPI 的一种实现,类似的实现还有 Open MPIIntel MPI 等。

    入门前须知的设计

    通讯器(communicator)
    通讯器定义了一组能够互相发消息的进程

    秩(rank)
    这组进程中每个进程分配到的一个序号,进程间显性地通过指定秩来进行通信

    标签(tag)
    一个进程可以通过指定另一个进程的秩以及一个独一无二的消息标签来发送消息给另一个进程。接受者可以发送一个接收特定标签标记的消息的请求(或者也可以完全不管标签,接收任何消息),然后依次处理接收到的数据

    点对点(point-to-point)
    涉及一个发送者以及一个接受者的通信被称作的通信

    集体性(collective)
    某个进程可能需要跟所有其他进程通信,帮我们处理这类所有进程间的叫集体性通信

    Hello World

    #include <mpi.h>
    #include <stdio.h>
    
    int main(int argc, char** argv) {
        // 初始化 MPI 环境,当前来说无用
        MPI_Init(NULL, NULL);
    
        // 通过调用以下方法来得到所有可以工作的进程数量
        int world_size;
        MPI_Comm_size(MPI_COMM_WORLD, &world_size);
    
        // 得到当前进程的秩
        int world_rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    
        // 会得到当前进程实际跑的时候所在的处理器节点名字
        char processor_name[MPI_MAX_PROCESSOR_NAME];
        int name_len;
        MPI_Get_processor_name(processor_name, &name_len);
    
        // 打印一条带有当前进程名字,秩以及
        // 整个 communicator 的大小的 hello world 消息。
        printf("Hello world from processor %s, rank %d out of %d processors\n",
               processor_name, world_rank, world_size);
    
        // 释放 MPI 的一些资源
        MPI_Finalize();
    }
    

    集群运行事项

    在集群上运行 MPI 进程时,可以在本地运行过程或运行它作为本地和远程节点的组合,不能只指定远程节点,所以 host 文件中需要要有本地节点。若节点处理器具有多核,在 host 文件给节点名字的后面加一个冒号和处理器有的核数就行了,就可以在指定节点运行指定数量的进程。

    >>> cat host_file
    cetus1:2
    cetus2:2
    cetus3:2
    cetus4:2
    
    $ mpirun -n 4 -f host_file ./mpi_hello_world
    Hello world from processor cetus1, rank 0 out of 4 processors
    Hello world from processor cetus2, rank 2 out of 4 processors
    Hello world from processor cetus2, rank 3 out of 4 processors
    Hello world from processor cetus1, rank 1 out of 4 processors
    

    参考

    MPI 教程
    MPI,OpenMPI 与深度学习

    展开全文
  • C++并行编程

    2017-06-08 20:32:55
    深入理解计算机系统中并行编程部分
  • C++并行与分布式编程_肖和平2004译.pdf
  • c++并行计算库TBB和PPL的基本用法 并行库充分利用多核的优势,通过并行运算提高程序效率,本文主要介绍c++中两个知名的并行库,一个是intel开发的TBB,一个是微软开发的PPL。本文只介绍其基本的常用用法:并行算法...

    c++并行计算库TBB和PPL的基本用法

    并行库充分利用多核的优势,通过并行运算提高程序效率,本文主要介绍c++中两个知名的并行库,一个是intel开发的TBB,一个是微软开发的PPL。本文只介绍其基本的常用用法:并行算法和任务。

    TBB(Intel® Threading Building Blocks )

    TBB是intel用标准c++写的一个开源的并行计算库。它的目的是提升数据并行计算的能力,可以在他的官网上下载最新的库和文档。TBB主要功能:

      1. 并行算法
      2. 任务调度
      3. 并行容器
      4. 同步原语
      5. 内存分配器

    TBB并行算法

    parallel_for:并行方式遍历一个区间。

    parallel_for(1, 20000, [](int i){cout << i << endl; });

    parallel_for(blocked_range<size_t>(0, 20000), [](blocked_range<size_t>& r)

    {

        for (size_t i = r.begin(); i != r.end(); ++i)

            cout << i << endl;

    });

    parallel_do和parallel_for_each:将算法应用于一个区间

    vector<size_t> v;

    parallel_do(v.begin(), v.end(), [](size_t i){cout << i << endl; });

    parallel_for_each(v.begin(), v.end(), [](size_t i){cout << i << endl; });

     parallel_reduce

      类似于map_reduce,但是有区别。它先将区间自动分组,对每个分组进行聚合(accumulate)计算,每组得到一个结果,最后将各组的结果进行汇聚(reduce)。这个算法稍微复杂一点,parallel_reduce(range,identity,func,reduction),第一个参数是区间范围,第二个参数是计算的初始值,第三个参数是聚合函数,第四个参数是汇聚参数。

    float ParallelSum(float array [], size_t n) {

        return parallel_reduce(

            blocked_range<float*>(array, array + n),

            0.f,

            [](const blocked_range<float*>& r, float value)->float {

                return std::accumulate(r.begin(), r.end(), value);

        },

            std::plus<float>()

            );

    }

    这个对数组求和的例子就是先自动分组然后对各组中的元素进行聚合累加,最后将各组结果汇聚相加。

    parallel_pipeline:并行的管道过滤器

      数据流经过一个管道,在数据流动的过程中依次要经过一些过滤器的处理,其中有些过滤器可能会并行处理数据,这时就可以用到并行的管道过滤器。举一个例子,比如我要读入一个文件,先将文件中的数字提取出来,再将提取出来的数字做一个转换,最后将转换后的数字输出到另外一个文件中。其中读文件和输出文件不能并兴去做,但是中间数字转换的环节可以并行去做的。parallel_pipeline的原型:

    parallel_pipeline( max_number_of_live_tokens,

                       make_filter<void,I1>(mode0,g0) &

                       make_filter<I1,I2>(mode1,g1) &

                       make_filter<I2,I3>(mode2,g2) &

                       ...

                       make_filter<In,void>(moden,gn) );

      第一个参数是最大的并行数,我们可以通过&连接多个filter,这些filter是顺序执行的,前一个filter的输出是下一个filter的输入。

    float RootMeanSquare( float* first, float* last ) {

        float sum=0;

        parallel_pipeline( /*max_number_of_live_token=*/16,      

            make_filter<void,float*>(

                filter::serial,

                [&](flow_control& fc)-> float*{

                    if( first<last ) {

                        return first++;

                     } else {

                        fc.stop();

                        return NULL;

                    }

                }   

            ) &

            make_filter<float*,float>(

                filter::parallel,

                [](float* p){return (*p)*(*p);}

            ) &

            make_filter<float,void>(

                filter::serial,

                [&](float x) {sum+=x;}

            )

        );

        return sqrt(sum);

    }

      第一个filter生成数据(如从文件中读取数据等),第二个filter对产生的数据进行转换,第三个filter是对转换后的数据做累加。其中第二个filter是可以并行处理的,通过filter::parallel来指定其处理模式。

    parallel_sort:并行排序

    const int N = 1000000;

    float a[N];

    float b[N];

    parallel_sort(a, a + N);

    parallel_sort(b, b + N, std::greater<float>());

    parallel_invoke:并行调用,并行调用多个函数

    void f();

    extern void bar(int);

     

    void RunFunctionsInParallel() {

        tbb::parallel_invoke(f, []{bar(2);}, []{bar(3);} );

    }

    TBB任务

    task_group表示可以等待或者取消的任务集合

    task_group g;

    g.run([]{TestPrint(); });

    g.run([]{TestPrint(); });

    g.run([]{TestPrint(); });

    g.wait();

     

    PPL(Parallel Patterns Library)

      PPL是微软开发的并行计算库,它的功能和TBB是差不多的,但是PPL只能在windows上使用。二者在并行算法的使用上基本上是一样的, 但还是有差异的。二者的差异:

    1. parallel_reduce的原型有些不同,PPL的paraller_reduce函数多一个参数,原型为parallel_reduce(begin,end,identity,func,reduction), 比tbb多了一个参数,但是表达的意思差不多,一个是区间,一个是区间迭代器。
    2. PPL中没有parallel_pipeline接口。
    3. TBB的task没有PPL的task强大,PPL的task可以链式连续执行还可以组合任务,TBB的task则不行。

    PPL任务的链式连续执行then

    int main()

    {

        auto t = create_task([]() -> int

        {

            return 0;

        });

     

        // Create a lambda that increments its input value.

        auto increment = [](int n) { return n + 1; };

     

        // Run a chain of continuations and print the result.

        int result = t.then(increment).then(increment).then(increment).get();

        cout << result << endl;

    }

    /* Output:

        3

    */

    PPL任务的组合

      1.when_all可以执行一组任务,所有任务完成之后将所有任务的结果返回到一个集合中。要求该组任务中的所有任务的返回值类型都相同。

    array<task<int>, 3> tasks =

    {

        create_task([]() -> int { return 88; }),

        create_task([]() -> int { return 42; }),

        create_task([]() -> int { return 99; })

    };

     

    auto joinTask = when_all(begin(tasks), end(tasks)).then([](vector<int> results)

    {

        cout << "The sum is "

              << accumulate(begin(results), end(results), 0)

              << '.' << endl;

    });

     

    // Print a message from the joining thread.

    cout << "Hello from the joining thread." << endl;

     

    // Wait for the tasks to finish.

    joinTask.wait();

    2.when_any任务组中的某一个任务执行完成之后,返回一个pair,键值对是结果和任务序号。

    array<task<int>, 3> tasks = {

            create_task([]() -> int { return 88; }),

            create_task([]() -> int { return 42; }),

            create_task([]() -> int { return 99; })

        };

     

        // Select the first to finish.

        when_any(begin(tasks), end(tasks)).then([](pair<int, size_t> result)

        {

            cout << "First task to finish returns "

                  << result.first

                  << " and has index "

                  << result.second<<endl;

        }).wait();

    //output: First task to finish returns 42 and has index 1.

     

    总结:

      ppl和tbb两个并行运算库功能相似,如果需要跨平台则选择tbb,  否则选择ppl。ppl在任务调度上比tbb强大,tbb由于设计上的原因不能做到任务的连续执行以及任务的组合,但是tbb有跨平台的优势。

     

    展开全文
  • C++ 并行编程之原子操作的内存顺序 1. C++原子操作的内存顺序概述 memory order主要有以下几种: memory_order_relaxed 只提供对单个atomic变量的原子读/写,不和前后语句有任何memory order的约束关系。 ...
  • C++并行计算

    千次阅读 2016-10-26 09:43:45
    并行计算

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,620
精华内容 1,448
关键字:

c++并行

c++ 订阅