【原】C++11並行計算 — 數組求和

本文轉載請註明出處 —— polobymulberry-博客園html

0x00 - 前言


最近想優化ORB-SLAM2,準備使用並行計算來提升其中ORB特徵提取的速度。以前對並行計算方面一竅不通。藉此機會,學習一下基本的並行編程。

在選擇並行編程的工具時,須要考慮如下問題:即該工具儘可能不要使用與平臺相關的API,如iOS端的GCD(Grand Central Dispatch),由於但願程序具備很強的移植性。一開始我想到的只有兩種選擇,一個是以TBB和OpenMP爲首的第三方線程庫,另外一個是原生線程庫。其中TBB和OpenMP對於Xcode的支持不是很好,原生線程庫又依賴於系統平臺,因此嘗試後都放棄了。後來想到C++11已經從語言層面支持了多線程開發,也就是提供了thread庫,因而抱着試一試學一學的態度就入坑了。ios

並行計算中一個很經典的案例就是數組求和,網絡上有不少介紹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 - 傳遞引用參數函數

相關文章
相關標籤/搜索