系列文章导航:《新的职业目标,以及C++性能优化》
基于对象的消息队列的性能优化checklist
一般的,消息队列的项要么采用添加type字段表示项类型,消息处理逻辑根据不同的type进入不同的分支,要么会以OO的思维使用多态的方法进行消息处理。
前者典型的如MFC的消息循环,后者就比如是reSIProcate。
対消息的处理逻辑不在本博客的讨论范围内。但就消息队列而言,影响性能的因素主要有两点,第一点是队列的同步,第二点是消息对象的分配销毁。
关于队列同步的优化,在以并发编程为主题的书籍或文章中有深入的讲解。
关于消息对象的分配销毁的优化,可以使用如下的方法进行优化:
(1)使用内存池块存储消息对象。
(2)定义类专属的new/delete操作符。
(3)使用指定分配地址的new操作符。
笔者在自己的台式机上进行对比测试,机器配置:win10-64位/16G*1内存/三星SSD硬盘/i5-6500处理器。
对比的结果非常明显,在指定位置调用new分配内存,比从OS获取内存的性能要高出2~3个数量级。以下是测试代码:
#include <Windows.h>
#include <iostream>
using namespace std;
/*
测试不同new的耗时。
(1)从指定地址new
(2)每次new都从OS获取地址
*/
void test_new_consume_on_heap()
{
/*
在我的台式机上测试结果如下:
1000*100 new[1B]-on-heap consume 9709 ticks,avg 310.688 ns per new+delete
1000*100 new[1k]-on-heap consume 10802 ticks,avg 345.664 ns per new+delete
1000*100 new[4k]-on-heap consume 20785 ticks,avg 665.12 ns per new+delete
1000*100 new[32k]-on-heap consume 60765 ticks,avg 1944.48 ns per new+delete
1000*1 new[1M]-on-heap consume 1847939 ticks,avg 591340 ns per new+delete
1000*1 new[4M]-on-heap consume 8043361 ticks,avg 2573875.520000 ns per new+delete
看得出,当数组较小时,new+delete的耗时很小,数组越大越耗时。
*/
LARGE_INTEGER start,stop,freqency;
QueryPerformanceFrequency(&freqency);
double ns_per_tick=1000*1000*1000/freqency.QuadPart,avg=0;
int counters = 1000*10;
char *pc=NULL;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new char[1];
//do-something
delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[1B]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new char[1024];
//do-something
delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[1k]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new char[4096];
//do-something
delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[4k]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new char[32768];
//do-something
delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[32k]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
counters = 1000*1;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new char[1048576];
//do-something
delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*1 new[1M]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new char[4194304];
//do-something
delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*1 new[4M]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<std::fixed <<avg<<" ns per new+delete"<<endl;
}
char c1B[1];
char c1k[1024];
char c4k[1024*4];
char c32k[1024*32];
char c1M[1024*1024];
char c4M[1024*1024*4];
void test_new_consume_on_stack()
{
/*
在我的台式机上测试结果如下:
1000*100 new[1B]-on-heap consume 466 ticks,avg 14.912 ns per new+delete
1000*100 new[1k]-on-heap consume 382 ticks,avg 12.224 ns per new+delete
1000*100 new[4k]-on-heap consume 692 ticks,avg 22.144 ns per new+delete
1000*100 new[32k]-on-heap consume 388 ticks,avg 12.416 ns per new+delete
1000*100 new[1M]-on-heap consume 4178 ticks,avg 13.3696 ns per new+delete
1000*100 new[4M]-on-heap consume 6245 ticks,avg 19.984000 ns per new+delete
看得出,数组大小对分配耗时的操作并不大。
但是需要注意的是,在指定位置调用new的内存不能使用delete销毁!!!
*/
LARGE_INTEGER start,stop,freqency;
QueryPerformanceFrequency(&freqency);
double ns_per_tick=1000*1000*1000/freqency.QuadPart,avg=0;
int counters = 1000*10;
char *pc=NULL;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new(c1B) char[1];
//do-something
//delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[1B]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new(c1k) char[1024];
//do-something
//delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[1k]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new(c4k) char[4096];
//do-something
//delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[4k]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new(c32k) char[32768];
//do-something
//delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[32k]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
counters = 1000*100;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new(c1M) char[1048576];
//do-something
//delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[1M]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<avg<<" ns per new+delete"<<endl;
QueryPerformanceCounter(&start);
for(int i=0;i<counters;++i)
{
pc = new(c4M) char[4194304];
//do-something
//delete []pc;
}
QueryPerformanceCounter(&stop);
avg = (stop.QuadPart-start.QuadPart)*ns_per_tick/counters;
cout<<"1000*100 new[4M]-on-heap consume "<<(stop.QuadPart-start.QuadPart)<<" ticks,avg "<<std::fixed <<avg<<" ns per new+delete"<<endl;
}