dynamic 锁竞争等系统开销分析

来源:互联网 发布:详情页制作软件 编辑:程序博客网 时间:2024/06/13 01:25
1. dynamic_cast

#include <iostream>#include <ctime>using namespace std;class Base{public:virtual void show(){};};class Derived : public Base{public:virtual void show(){};};int main(int argc, char* argv[]){Base* pTmpBase = new Derived();Derived* pTmpDerived = NULL;time_t begin = time(NULL);// 10亿次int count = 1000*1000*1000;for (int i=0; i<count; i++){// 20 秒//pTmpDerived = dynamic_cast<Derived*>(pTmpBase);// 2秒//pTmpDerived = static_cast<Derived*>(pTmpBase);// 3秒//pTmpBase->show();// 空循环,运行时间也是2~3秒内}time_t end = time(NULL);printf("Using seconds %d.\n ", end - begin);system("pause");  return 0;}



10亿次操作,空循环、static_cast、虚函数消耗都不大,只有3秒左右,dynamic_cast有20秒左右。说dynamic_cast系统开销大都是相对的,一般情况下,对于少量的操作,那么用dynamic_cast影响也不是很大,对于简单的类操作(双核cpu),如下面,1秒钟可以处理五千万次。从汇编角度来看,dynamic_cast消耗的原因是因为翻译成汇编后的汇编语句条数较多,从而cpu指令会变多。


pTmpDerived = dynamic_cast<Derived*>(pTmpBase); 对应的汇编


00401091   push        000401093   push        offset Derived `RTTI Type Descriptor' (004150d8)00401098   push        offset Base `RTTI Type Descriptor' (004150c0)0040109D   push        00040109F   mov         ecx,dword ptr [pTmpBase]004010A2   push        ecx004010A3   call        ___RTDynamicCast (00404af3)004010A8   add         esp,14h004010AB   mov         dword ptr [pTmpDerived],eax


___RTDynamicCast 这个子程序里面有几百行,而且还有很多的call


pTmpBase->show();对应的汇编,加起来没有10行


00401091   mov         ecx,dword ptr [pTmpBase]00401094   mov         edx,dword ptr [ecx]00401096   mov         ecx,dword ptr [pTmpBase]00401099   call        dword ptr [edx]


call dword ptr 汇编为:

004011A0   push        ebp004011A1   mov         ebp,esp004011A3   push        ecx004011A4   mov         dword ptr [ebp-4],ecx004011A7   mov         esp,ebp004011A9   pop         ebp004011AA   ret


2. 锁竞争与线程切换

#include <iostream>#include <ctime>#include "ace/Task.h"using namespace std;class MyTask : public ACE_Task_Base{public:MyTask(){begin = time(NULL);nCount = 0;};virtual int svc (void){while(1){//ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex, obj, m_lock, -1);ACE_READ_GUARD_RETURN(ACE_RW_Thread_Mutex, obj, m_rwLock, -1);nCount++;// 1亿次if (nCount >= 1000*1000*100){time_t end = time(NULL);printf("Using seconds %d.\n ", end - begin);Sleep(1000);}}}private:ACE_Recursive_Thread_Mutex m_lock;ACE_RW_Thread_Mutex m_rwLock;time_t begin;unsigned long nCount;};int main(int argc, char* argv[]){MyTask task;int threadNum = 2;task.activate(THR_NEW_LWP | THR_JOINABLE |THR_INHERIT_SCHED, threadNum);system("pause");  return 0;}




测试数据:
/*
双核机器 


1千万数据 单线程,  带锁: 0秒


1亿数据
单线程,  带锁: 7秒   与没有锁比,消耗在锁的获取与释放。
双线程,  带锁: 14秒
 3线程,  带锁: 14秒
10线程,  带锁: 12秒
100线程, 带锁: 11秒
1000线程,带锁: 11秒 cpu大概50到60 线程竞争同一个锁,没有竞争到的会排队进入sleep状态。

1亿数据,双线程,读写锁 27秒
对于读写锁,性能上看比互斥锁差2到3倍,所以读写锁的使用条件并不只是“当读远远次数多于写的时候”,当读的操作耗时较长的时候,而且写的次数较少的时候,才更适用。


1亿数据 单线程,  不带锁:0秒
1亿数据 一百线程,不带锁:0秒


100亿数据 单线程   不带锁 2秒
100亿数据 双线程   不带锁 3秒
100亿数据 10线程   不带锁 10秒 cpu负载99%
100亿数据 100线程  不带锁 31秒 100个线程,cpu负载99%
100亿数据 1000线程 不带锁      同上,cpu负载99%,严重影响系统其他功能
*/


1)当锁竞争与线程切换的次数不是很大的时候,这种开销可以忽略,从“双线程,带锁:14秒”来分析,多线程1千万次的锁竞争,从时间上看话费大概1秒钟多一点。
2)线程切换、锁竞争主要是消耗cpu,从时间上看,锁的竞争、获取与释放,才是花费时间的。
3)与没有锁操作比,带锁的开销从时间上看,还是差很多的,1000倍的时间开销。
4)线程切换也是很消耗cpu的,100亿数据的情况下,单线程只需2秒,100线程由于线程切换消耗太多cpu用了30多秒。



3. 迭代器


#include <iostream>#include <ctime>#include "ace/Task.h"#include <list>using namespace std;int main(int argc, char* argv[]){std::list<int> listTmp;for (int i=0; i<100; i++){listTmp.push_back(i);}time_t begin = time(NULL);// 1亿次int count = 1000*1000*10;for (int j=0; j<count; j++){list<int>::iterator iter = listTmp.begin();for(; iter != listTmp.end(); ++iter){}}time_t end = time(NULL);printf("Using seconds %d.\n ", end - begin);system("pause");  return 0;}


对一个10个元素的list进行1亿次遍历,30秒
对一个100个元素的list进行1千万次遍历,27秒


1亿次的迭代操作大概只花费3秒。虽然在代码编写时候,从时间上运行时间差不太多,但是cpu差距很大,不要以为用list与用map的时间差不多就用list,cpu也是一种资源。


如果1秒钟有1w的数据,那么要分析出处理一条数据用到多少锁竞争、dynamic_cast、线程切换通常这些在处理一条数据都不会很多,对系统的影响基本不会很大。最需要注意的就是程序逻辑上,比如迭代器操作有多少,假如一条数据有1000次迭代器操作,那么就是每秒1千万的迭代器操作,即时处理上cpu绰绰有余,但是对cpu消耗很大。


如果1秒钟有1千万的数据,那么很多问题都会凸显出来,dynamic的系统消耗,锁竞争造成的时间浪费,任何多余的迭代器遍历操作。带宽问题。1千万的数据,假如1条数据100字节,那么需要带宽(100*1000*10000/8)bit/s 有120兆的带宽了,一般网卡上行600兆,再大可能带宽就是问题了。


一个简单对象,new和delete 1千万次,大概耗费1s。

0 0