C++ 优化

本文详细介绍了C++中的内联函数和宏定义的区别,包括它们的工作原理、优缺点及适用场景。内联函数在运行时可调试,避免了函数调用的开销,但可能导致代码膨胀;宏定义则是简单的文本替换,不进行类型检查。内联函数适用于小函数以提高效率,但不应滥用,尤其是包含循环或大量代码的函数。此外,文章还探讨了内存寻址、汇编语言中的寄存器使用以及性能优化策略,如避免分支、利用CPU缓存等。
摘要由CSDN通过智能技术生成
Inline

宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。

内联函数从 源代码层看,有函数的结构,而在编译后,却不具备函数的性质。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看 编译器对该函数定义的具体处理。

优点:inline函数是一个真正的函数,它可以进行参数检测,相比较于普通函数,它的执行效率上更加快速。
缺点:浪费内存。inline函数在函数调用的地方会在预编译的时候生成一份函数的拷贝,也就是说,只要有调用inline函数的地方,就会生成一处拷贝。而普通的函数在函数调用的地方只是存储了此函数的地址。

diff #define

内联函数直接嵌入到目标代码中,宏是简单的做文本替换.

在内联函数内不容许用循环语句和开关语句。如有则编译器将该函数视为普通函数那样产生函数调用代码。
递归函数(本身调用本身)是不能做为内联函数的。
内联函数只适用于1——5行的小函数。对于含有不少语句的大函数,函数调用和返回的开销相对于来讲微不足道,因此没不要用内联函数。

inline 与 #define的区别:
(1)内联函数在运行时可调试,而宏定义不能够;递归

(2)编译器会对内联函数的参数类型作安全检查或自动类型转换(同普通函数),而宏定义则不会; 编译器

(3)内联函数能够访问类的成员变量,宏定义则不能; 编译

(4)在类中声明同时定义的成员函数,自动转化为内联函数。

实例

定义在类中的成员函数默认都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的。

class A
{
    public:void Foo(int x, int y) {  } // 自动地成为内联函数
}

将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程风格,上例应该改成:

// 头文件
class A
{
    public:
    void Foo(int x, int y);
}
 
 // 定义文件
inline void A::Foo(int x, int y){}
inline 是一种"用于实现的关键字"

关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。

如下风格的函数 Foo 不能成为内联函数:

inline void Foo(int x, int y); // inline 仅与函数声明放在一起
void Foo(int x, int y){}

而如下风格的函数 Foo 则成为内联函数:

void Foo(int x, int y);
inline void Foo(int x, int y) {} // inline 与函数定义体放在一起

所以说,inline 是一种"用于实现的关键字",而不是一种"用于声明的关键字"。

慎用 inline

内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?如果所有的函数都是内联函数,还用得着"内联"这个关键字吗?
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如"偷偷地"执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。

memcpy 优化
void BitSet(uint8_t* buffer_address, int32_t index) {
  int64_t mask = 1L << (index & 0x3f);  // mod 64 and shift
  int64_t wordOffset = (index >> 6) * 8;
  int64_t word;
  memcpy(&word, buffer_address + wordOffset, sizeof(int64_t));
  int64_t value = word | mask;
  memcpy(buffer_address + wordOffset, &value, sizeof(int64_t));
}

可以优化成

inline void BitSet(uint8_t* buffer_address, int32_t index) {
  int64_t mask = 1L << (index & 0x3f);  // mod 64 and shift
  int64_t wordOffset = (index >> 6) * 8;
  int64_t word;
  word = *(int64_t*)(buffer_address + wordOffset);
  int64_t value = word | mask;
  *(int64_t*)(buffer_address + wordOffset) = value;
}
常用的寄存器

有16个常用寄存器
rax、rbx、rcx 、rdx、rsi、rdi、rbp、rsp
r8、r9、r10、r11、r12、r13、r14、r15

寄存器的具体用途

  • rax、rdx常作为函数返回值使用
  • rdi、rsi、rdx、rcx、r8、r9等寄存器常用于存放函数参数
  • rsp、rbp用于栈操作
  • rip作为指令指针
    存储着CPU下一条要执行的指令的地址
    一旦CPU读取一条指令,rip会自动指向下一条指令(存储下一条指令的地址)
内存寻址

小括号表示这个内存地址存储的数据。

AT&T 和 Intel 汇编语法的主要区别

AT&T 语法先写源操作数,再写目标操作数;Intel 语法先写目标操作数,再写源操作数:

AT&T

movl %esp, %ebp

Intel

MOV EBP, ESP

各种取址方式的表示。AT&T 语法总体上是offset(base, index, width)的格式;Intel 语法总体上是[INDEX * WIDTH + BASE + OFFSET]的格式:
AT&T

movl           0x0100, %eax
movl           (%esi), %eax
movl         -8(%ebp), %eax
movl  0x0100(,%ebx,4), %eax
movl 0x8(%edx,%ebx,4), %eax

Intel

MOV EAX, [0100]
MOV EAX, [ESI]
MOV EAX, [EBP-8]
MOV EAX, [EBX*4+0100]
MOV EAX, [EDX+EBX*4+8]

AT&T 语法要在常数前加 $、在寄存器名前加 % 符号;Intel 语法没有相应的东西要加:
AT&T

subl $0x30, %eax

Intel

SUB EAX, 30
变址寻址

是以一个寄存器里的数值加上另一个寄存器里的数字 乘以一个比例因子(1,2,4,8)再加上一个常数得到最终地址,把地址上的值放到寄存器中

movl $0x2000, %eax   # 立即数寻址
movl $0x2, %ebx   # 立即数寻址
movl (,%eax,4), %ecx   #比例变址寻址, 把地址0x8000(0x2000 *4)上的值放到ecx中
movl  6(,%eax,4), %ecx   #比例变址寻址, 把地址0x8006(0x2000 *4+6)上的值放到ecx中
movl  (%ebx,%eax,4), %ecx   #变址寻址, 把地址0x8002(0x2000*4+2)上的值放到ecx中
movl  6(%ebx,%eax,4), %ecx   #变址寻址, 把地址0x8008(0x2000*4+2+6)上的值放到ecx中

R2C & C2R Performance Profiling and Optimization

Performance Optimization
  • Avoid if-else branch
  • Inline function
  • Utilize CPU cache
  • Use AVX-512
  • First Row Second Column Pattern

Optimize String/Binary type

  • Buffer instead Builder
  • Initialize all true once validity_buffer allocated, to avoid too many AppendToBitmap
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值