本文转载请注明出处 ——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 - 注意事项
- thread调用类的成员函数:
thread memberFuncThread(&ClassName::MemberFuncName, this, arg1, arg2...);
- thread传递引用参数:
需要使用std::ref进行包装,详见thread - 传递引用参数。
原文链接: https://www.cnblogs.com/polobymulberry/p/6262032.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/247210
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!