一、网络IO
1.对待高并发程序使用epoll模型替换select模型
2.根据网络数据量适当调整缓冲区大小
二、锁和原子操作
1.可以不使用锁就尽量少用锁,如果需要锁保证线程间数据同步就大胆使用锁
2.对象引用计数时可使用原子操作,gcc提供了__sync_*系列的函数,提供加减和逻辑运算的原子操作
返回值为更新前的值:
type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)
返回值为更新后的值:
type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)
比较 oldval 和 *ptr,如果它们相等,就把 newval 复制到 *ptr
如果 oldval 和*ptr 匹配,返回值是 true,否则是 false:
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
返回操作之前的旧值:
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
三、语言层面
1.构造函数中初始化类成员方式有两种,构造函数中初始化列表和赋值,如果类成员为类类型时,采用初始化列表方式性能有优势,如果类成员为int float内置类型却无差异。
class Base
{
int m_basemember;
public:
Base()
: m_basemember(1) {
printf("Base Construct Function\n");
}
~Base(){
printf("Base Destruct Function\n");
}
Base(const Base& b) {
printf("Base Copy constructor Function\n");
this->m_basemember = b.m_basemember ;
}
Base& operator = (Base& b){
printf("Base operator = Function\n");
this->m_basemember = b.m_basemember;
return *this;
}
};
class Derive
{
Base m_base;
public:
Derive (Base &base)
/*:m_base(base)*/
{
this->m_base = base;
}
};
int main(int argc, char* argv[])
{
Base base;
Derive D(base);
return 0;
}
在上例中:
如果Derive构造函数初始化方式为赋值方式,即this->m_base = base;,代码运行输出结果为:
Base Construct Function
Base Construct Function
Base operator = Function
Base Destruct Function
Base Destruct Function
如果Derive构造函数初始化方式为初始化列表,即:m_base(base),代码运行输出结果为:
Base Construct Function
Base Copy constructor Function
Base Destruct Function
Base Destruct Function
类类型使用初始化列表方式会少调用一次构造函数,在大量的类成员为类类型中初始化列表性能更好。
以下几种情况时必须使用初始化列表:
·常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
·引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
·没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
Note:
初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
2.strlen和list容器中的size()函数
strlen的实现是遍历字符串直至’\0’,从而计算出字符串的len,如果是经常性计算或者字符串超长(HLS协议中当时移m3u8文件索引字符串长度会随着时移时间而增长),这种情况就不适合每次都strlen(str),可以自行用变量记录长度。
while(m_list.size() > 0) 这样的语句用while(!m_list.empty())替换,有些size()的实现是遍历链表才获得长度值,而不是用一个变量记录长度值。
3.临时对象
减少临时对象的产生。
四、内存使用
1.字节对齐
在不使用#pragma pack设定内存以多少字节对齐是,默认采用4字节对齐,方式1比方式2不管是在内存占用大小和访问变量时寻址方面都具有一定优势。
方式1
#pragma pack(4)
struct test
{
int a;
short b;
char c;
double d;
};
#pragma pack(pop)
方式2
#pragma pack(4)
struct test
{
char c;
double d;
short b;
int a;
};
#pragma pack(pop)
2.内存池
内存池的基本都是基于预分配大块内存,建立内存索引给外部使用,从而避免频繁创建释放内存导致效率低下。
3.内存拷贝
在流媒体服务器中音视频数据占用非常多的内存,程序模块间拷贝内存进行处理的现象也很多,个人经验尽量传递内存指针通过修改同一块内存避免大量的memcpy影响性能。
五、小结
从二八原则来看,影响程序性能的很有可能是20%的代码,而且没有必要过早的进入性能优化的阶段,也没有必要花大量时间在那80%代码身上。
可以在框架搭建阶段考虑性能瓶颈,在整体功能完成后通过性能工具来测试性能瓶颈并有针对性的优化。
以上都是泛泛的谈,但还是得具体问题具体分析具体解决。