[c++] c++异步处理和clock函数踩坑

举报
lessIsBetter 发表于 2020/04/06 04:12:19 2020/04/06
【摘要】 c++异步处理和clock函数踩坑c++异步处理和clock函数踩坑1. 异步处理1.1 确认或者封装需要异步处理的函数1.2 调用异步处理函数std::async()2. clock()函数的坑参考最近在c++异步处理的地方踩了个坑,提前剧透一下,坑来自clock()计时器函数。1. 异步处理异步处理,在本文,只是为了多线程加速程序运行速度。异步处理一般需要有如下步骤,1.1 确认或者封装...

c++异步处理和clock函数踩坑

c++异步处理和clock函数踩坑1. 异步处理1.1 确认或者封装需要异步处理的函数1.2 调用异步处理函数std::async()2. clock()函数的坑参考

最近在c++异步处理的地方踩了个坑,提前剧透一下,坑来自clock()计时器函数。

1. 异步处理

异步处理,在本文,只是为了多线程加速程序运行速度。

异步处理一般需要有如下步骤,

1.1 确认或者封装需要异步处理的函数

这个过程可以理解为,将一个运行速度慢,需要加速的部分,拆解为很多小模块,每个小模块自身比较快。那么,在每个线程中,只运行这个小块,多个线程同时工作的时候,总体的时间就会比较少。

1.2 调用异步处理函数std::async()

多线程有很多中方法,windows下的多线程有CreateThread方法,c++11里面也包含了std::thread()方法,std::async()方法,这里只介绍std::async()。原因是,笔者认为多线程并行加速的时候,这个方法将底层很多处理都封装了,用起来简单、便捷。

#include <iostream>
#include <future>
#include <ctime>
#include <thread>
using namespace std;
bool pause_thread(int n) {
    double res = 0.;
    for (double i = 0; i < 10.e9; i += 1.0) {
        res += i * i;
    }
    return true;
}
void func1() {
    future<bool> f1 = async(launch::async, pause_thread, 3);
    future<bool> f2 = async(launch::async, pause_thread, 2);
    future<bool> f3 = async(launch::async, pause_thread, 1);
    f1.wait();
    f2.wait();
    f3.wait();
}
int main() {
    cout << "---------------------func1: multi-thread" << endl;
    func1();
}

启动线程:当std::async()函数被调用的时候,启动了一个新的线程。上面的代码中,启动了三个线程。这三个线程分别在不同的cpu核心执行自身的任务。

运行结果汇总:线程分别运行的时候,并没有受到主程序的控制。但是有了future<T>::wait()这个函数,就形成了在这个函数调用的节点,要一直等待对应的线程运行结束。正上面的例子中,连续三个异步函数对应的wait()函数就相当于将三个线程运行的结果,在此处等待运行结束,起到了汇总的作用。

相对正式一些的描述是,wait()函数增加了一个主程序的阻塞点,直到子线程执行完毕,阻塞结束。

子线程启动时间:这里需要注意的是std::launch::async参数,网上很多例子中,不强调这个参数,或者是采用缺省这个参数的方式。但是实际上,如果没有这个参数,那么异步处理基本上就无法起到多线程加速的作用。

如果不采用这个函数,那么,子进程将在阻塞点,也就是wait()函数的位置,才启动这个函数。也就是说,执行顺序变成了:线程f1执行,f1阻塞,f1阻塞结束,f2执行,f2阻塞,f2阻塞结束,f3执行,f3阻塞,f3阻塞结束。这样做其实相当于各个任务串行执行,没有起到加速的作用。对于更多的细节,推荐参考《Effective Modern C++》。

2. clock()函数的坑

其实,上面的例子说完,对于异步方式实现多线程加速的过程就结束了。但是在这个过程中,笔者踩到了一个坑,就是在计算各个线程使用时间的时候,使用的计时函数clock(),不吐槽一下难以平复我的心情。

先上代码,就是在上面的代码中,增加了计时器,看看各个线程消耗的时间。

笔者傻乎乎地用clock()计时,运行结果让人一脸懵*。

#include <iostream>
#include <future>
#include <ctime>
#include <thread>
using namespace std;
bool pause_thread(int n) {
    auto start_clock = clock();
    double res = 0.;
    for (double i = 0; i < 10.e9; i += 1.0) {
        res += i * i;
    }
    cout << n << " thread,clock: " << clock() - start_clock << endl;
    return true;
}
void func1() {
    auto start_clock = clock();
    future<bool> f1 = async(launch::async, pause_thread, 3);
    future<bool> f2 = async(launch::async, pause_thread, 2);
    future<bool> f3 = async(launch::async, pause_thread, 1);
    f1.wait();
    f2.wait();
    f3.wait();
    cout << "all cost: clock: " << clock() - start_clock << endl;
}
void func2() {
    auto start_clock = clock();
    pause_thread(3);
    pause_thread(2);
    pause_thread(1);
    cout << "all cost: clock: " << clock() - start_clock << endl;
}
int main() {
    cout << "---------------------func1: multi-thread" << endl;
    func1();
    cout << "---------------------func2: one-thread" << endl;
    func2();
    return 0;
}

运行结果:

---------------------func1: multi-thread
3 thread,clock: 29769532
1 thread,clock: 29777345
2 thread,clock: 29779234
all cost: clock: 29779355
---------------------func2: one-thread
3 thread,clock: 10091302
2 thread,clock: 9427059
1 thread,clock: 9277793
all cost: clock: 28796198

看到这个结果瞬间感觉自己瞎了,异步多线程用了29779355个时钟时间,单线程串行用了28796198个时钟时间。

经过了n久各种查,此处省略一万匹***。发现,居然是clock()函数的锅。这个函数不是计时用的,准确来说,是计算消耗了多少处理器时间。官方的解释是

clock_t clock (void);

Clock program

Returns the processor time consumed by the program.

The value returned is expressed in clock ticks, which are units of time of a constant but system-specific length (with a relation of CLOCKS_PER_SEC clock ticks per second).

The epoch used as reference by clock varies between systems, but it is related to the program execution (generally its launch). To calculate the actual processing time of a program, the value returned by clock shall be compared to a value returned by a previous call to the same function.

从运行的结果上看,这个函数计算的时间,包括了此程序所有相关处理器的运行时间。至于为什么是这样,这段简短的描述了看不出来,这里暂时不深究了。下面看一下采用chrono::system_clock::now()来计时。

#include <iostream>
#include <future>
#include <ctime>
#include <thread>
#include <chrono>
using namespace std;
bool pause_thread(int n) {
    auto start_time = chrono::system_clock::now();
    auto start_clock = clock();
    double res = 0.;
    for (double i = 0; i < 10.e9; i += 1.0) {
        res += i * i;
    }
    cout << n << " thread,clock: " << clock() - start_clock << endl;
    cout << n << " thread,sec_cost: " << (chrono::system_clock::now() - start_time).count() << endl;
    return true;
}
void func1() {
    auto start_time = chrono::system_clock::now();
    auto start_clock = clock();
    future<bool> f1 = async(launch::async, pause_thread, 3);
    future<bool> f2 = async(launch::async, pause_thread, 2);
    future<bool> f3 = async(launch::async, pause_thread, 1);
    f1.wait();
    f2.wait();
    f3.wait();
    cout << "all cost: clock: " << clock() - start_clock << endl;
    cout << "all cost: sec_cost: " << (chrono::system_clock::now() - start_time).count() << endl;
}
void func2() {
    auto start_time = chrono::system_clock::now();
    auto start_clock = clock();
    pause_thread(3);
    pause_thread(2);
    pause_thread(1);
    cout << "all cost: clock: " << clock() - start_clock << endl;
    cout << "all cost: sec_cost: " << (chrono::system_clock::now() - start_time).count() << endl;
}
int main() {
    cout << "---------------------func1: multi-thread" << endl;
    func1();
    cout << "---------------------func2: one-thread" << endl;
    func2();
    return 0;
}

运行结果:

---------------------func1: multi-thread
1 thread,clock: 29656671
1 thread,sec_cost: 2 thread,clock: 9895839
29660707
2 thread,sec_cost: 9895911
3 thread,clock: 29670203
3 thread,sec_cost: 9901171
all cost: clock: 29670278
all cost: sec_cost: 9901279
---------------------func2: one-thread
3 thread,clock: 9534692
3 thread,sec_cost: 9538701
2 thread,clock: 9259199
2 thread,sec_cost: 9262052
1 thread,clock: 9248157
1 thread,sec_cost: 9250516
all cost: clock: 28042079
all cost: sec_cost: 28051276

可以看到,在异步多线程并行的情况下,采用chrono::system_clock::now()计时的效果是正确的。单线程28051276毫秒(28秒)计算完成的,在异步多线程的情况下耗时9901279毫秒(10秒)。

参考

  1. 《Effective Modern C++》

  2. http://www.cplusplus.com/reference/ctime/clock/


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。