zoukankan      html  css  js  c++  java
  • 【原】C++11并行计算 — 数组求和

    本文转载请注明出处 —— polobymulberry-博客园

    0x00 - 前言


    最近想优化ORB-SLAM2,准备使用并行计算来提高其中ORB特征提取的速度。之前对并行计算方面一窍不通。借此机会,学习一下基本的并行编程。

    在选择并行编程的工具时,需要考虑以下问题:即该工具尽量不要使用与平台相关的API,如iOS端的GCD(Grand Central Dispatch),因为希望程序具有很强的移植性。一开始我想到的只有两种选择,一个是以TBB和OpenMP为首的第三方线程库,另一个是原生线程库。其中TBB和OpenMP对于Xcode的支持不是很好,原生线程库又依赖于系统平台,所以尝试后都放弃了。后来想到C++11已经从语言层面支持了多线程开发,也就是提供了thread库,于是抱着试一试学一学的态度就入坑了。

    并行计算中一个很经典的案例就是数组求和,网络上有很多介绍C++11的thread使用、源码分析的文章,不过使用C++11进行数组求和并行计算的示例却很少,所以才有了这篇博文。

    0x01 - 代码解析


    在iOS系统使用C++11进行开发。

    //
    //  ViewController.m
    //  TestDispatch
    //
    //  Created by poloby on 2017/1/7.
    //  Copyright © 2017年 polobymulberry. All rights reserved.
    //
    
    #import "ViewController.h"
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    // 作为求和函数的参数
    // 封装了求和函数的输入和输出
    typedef struct ThreadArg {
        long long base;     // 从base~base+length数列求和
        long long length;
        long long sum;      // 将上述数列的和存储在sum中
    }ThreadArg;
    
    void sum(ThreadArg *arg)
    {
        long long begin = arg->base;
        long long end = arg->base + arg->length;
        long long sum = 0;
        // 不要直接使用for(long long i = arg->base; i < arg->base + arg->length)
        // 也不要使用arg->sum += i;
        // 因为指针的读取比普通栈的读取需要多花费一些时间
        for (long long i = begin; i < end; ++i) {
            sum += i;
        }
        arg->sum = sum;
    }
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // 计算1~count数列之和
        const long long count = 1000000000;
        
        // 单线程常用方法
        NSDate *commonMethodDate = [NSDate date];
        long long commonMethodSum = 0;
        for (long long i = 0; i < count; ++i) {
            commonMethodSum += i;
        }
        // 计算单线程使用时间
        double commonMethodDuration = [[NSDate date] timeIntervalSinceDate:commonMethodDate];
    
        NSLog(@"Common method spend time = %fms, sum = %lld", commonMethodDuration * 1000, commonMethodSum);
        
        // 并行计算方法
        // 将1~count数列平均分为threadCount组,求解每组数列之和,再将其相加得到总和
        NSDate *parallelMethodDate = [NSDate date];
        // 设置并行线程数目
        const int threadCount = 2;
        thread threads[threadCount];
        ThreadArg args[threadCount];
        // 初始化线程及其参数
        for (int i = 0; i < threadCount; ++i) {
            long long offset = (count / threadCount) * i;
            args[i].base = offset;
            args[i].length = MIN(count - offset, count / threadCount);
            threads[i] = thread(sum, &args[i]);
        }
        
        // 启动线程并等待线程退出
        for (int i = 0; i < threadCount; ++i) {
            threads[i].join();
        }
        
        long long parallelMethodSum = 0;
        // 将每组数列之和相加得到总和
        for (int i = 0; i < threadCount; ++i) {
            parallelMethodSum += args[i].sum;
        }
        
        // 计算多线程使用时间
        double parallelMethodDuration = [[NSDate date] timeIntervalSinceDate:parallelMethodDate];
        
        NSLog(@"Parallel method spend time = %fms, sum = %lld", parallelMethodDuration * 1000, parallelMethodSum);
    }
    
    @end

    0x02 - 结果分析


    Xcode8.2.1+iPhone7模拟器+1~1000000000数列之和:
    线程数目 2 4 8
    多线程耗时 1921.253026ms 981.853008ms 684.603035ms
    单线程耗时 3171.698034ms 3472.517014ms 3447.206974ms

    Xcode8.2.1+iPhone7模拟器+1~10000数列之和:

    线程数目 2 4 8
    多线程耗时 0.279963ms 0.212014ms 0.297010ms
    单线程耗时 0.038981ms 0.027955ms 0.032008ms
    可见多线程本身也需要消耗一定的资源,所以只有在系统规模较大的情况下才能取得显著的性能提升。

    0x03 - 注意事项


    1. thread调用类的成员函数:

    thread memberFuncThread(&ClassName::MemberFuncName, this, arg1, arg2...);

    2. thread传递引用参数:

    需要使用std::ref进行包装,详见thread - 传递引用参数

  • 相关阅读:
    第三方应用调用应用市场和唤醒APP
    tp5 输入域名即访问指定页面
    反射概述(静态 VS 动态语言)
    自定义注解
    元注解
    内置注解
    什么是注解
    线程小结(代码)
    线程池
    信号灯法
  • 原文地址:https://www.cnblogs.com/polobymulberry/p/6262032.html
Copyright © 2011-2022 走看看