Archive

Archive for September, 2014

C++抛exception会主动让出CPU吗?

September 10th, 2014 No comments

虽然我自己并不是很喜欢使用exception.但是在有的场合用exception比return code让代码更具可阅读性.一直以来,我都认为throw exception不会大幅度降低程序的性能, 但是最近线上发生的一件事情却把exception这个东西牵扯了进来.
事情很简单, 现场报说有的请求timeout(500ms超时). 查看log以后发现一部分代码可能造成这个问题,但是当时并无法肯定是哪一个调用造成的耗时长. 后来做了一系列的实验以后发现居然是一个查找部分的代码会很耗时(最长可达1s), 但是这个查找的代码的实现及其的简单.基本上就是这个样子:

void findXXX( ) {
    find in a std::map<std::string, std::string> instance;
    if not found {
        throw InvalidParameter();
    }
}

而当时的情况是, map中是没有数据的,也就是说一定会出发抛出异常. 开始我怎么都觉得throw exception会那么耗时.可是多次的测试下来却发现总是在这个点上可能耗时很长.那么无论如何这个抛异常看起来都有点问题.
于是我做了一个小程序,就是很简单的抛异常100w次.但是没有发现耗时很长的情况发生(至少和我预期是一样的). 那么为何线上程序总是在这个抛异常的地方出现问题呢? 于是对比了自己的模拟程序和线上程序,发现了一个很大的不同点,那就是线上程序是多线程的,而且线程相当多(500+),而我的测试程序是单线程的.那么有没有可能是抛异常会导致线程切换,最终引发耗时长呢?
于是我修改了一下测试程序,加入线程,线程run里面就是不断的++一个long long,过一会儿sleep 30ms. 我一共new了800个线程(我也是够猛的). 这次测试程序我做了两个版本,一个是抛异常的,另外一个是不抛异常,取而代之的是循环++10次,然后return的.
从这两个版本的测试结果来看,在同一个环境(CentOS5, 4core cpu, 16G mem)上测试10次,平均下来,抛异常的版本运行时间为26s,而不抛异常的版本运行时间为1.5s左右. 同时抛异常的版本中能监测到有很50+次调用函数耗时达1ms以上的.而不抛异常的版本平均只有2次不到.
基于这些测试, 我猜测很可能抛异常会引发context switch. 同时由于线程数量巨大,线程切换十分频繁. 最终造成这个简单的findXXX耗时很大.
当然了,这个只是我的猜测,没有证实. 希望以后可以继续看看是不是真的造成了主动的线程切换.

PS: google了好一阵, 还没有直接证据可以证明throw exception可能导致线程上下文切换. 以后有空倒是可以看看libstdc++的实现.

Categories: programming Tags: ,