c++ 优化方法
1.单个线程中,将加锁变量取出来,传递给局部变量,然后释放加锁变量,可有效减少加锁时间。
void qcjsynchronizer::core() { while (run_flag_) { std::deque data; { std::unique_lock lock(mutex_); if (msg_deque_.empty()) { condition_.wait(lock); if (!run_flag_) { continue; } } data = msg_deque_; // msg指针传递给局部变量data指针 msg_deque_.clear(); // msg指针清空,不再指向内存空间 } // 后续就是线程内部对局部变量的处理,不需要加速 while (!data.empty()) { // work // 1. 正常情况,所有的消息序列都在均匀地接收消息数据 if (deque.size() == (size_t) 1) { // work } else { } // 2. 不正常情况,某一个已经超过了queue_size // work } }}
2.4个变量的向量需要计算,可使用vmulq_f32并行处理加速
// ×××××××××××××× x86平台 ××××××××××××××void so3(const float *src, float *tgt) const { __m128 p0 = _mm_mul_ps(_mm_load_ps1(&src[0]), c[0]); // 并行处理 __m128 p1 = _mm_mul_ps(_mm_load_ps1(&src[1]), c[1]); __m128 p2 = _mm_mul_ps(_mm_load_ps1(&src[2]), c[2]); _mm_store_ps(tgt, _mm_add_ps(p0, _mm_add_ps(p1, p2)));}
_mm_mul_ps:
multiplies the four single-precision, floating-point values of a and b.
3.strength reduction
3.1 由于不同指令本身的速度就是不一样的,比较、整型的加减、位操作速度都是最快的,而除法/取余却很慢。
#include #include // 获取一个整数对应10进制的位数uint32_t digits10_v1(uint64_t v) { uint32_t result = 0; do { ++result; v /= 10; } while (v); return result;}uint32_t digits10_v2(uint64_t v) { uint32_t result = 1; for (;;) { if (v < 10) return result; if (v < 100) return result + 1; if (v < 1000) return result + 2; if (v < 10000) return result + 3; // skip ahead by 4 orders of magnitude v /= 10000u; result += 4; }}uint32_t digits10_v3(uint64_t v) { if (v < 10) return 1; if (v < 100) return 2; if (v < 1000) return 3; if (v < 1000000000000) { // 10^12 if (v < 100000000) { // 10^7 if (v = 10000000); // 10^7 } if (v = 1000000000); // 10^9 } return 11 + (v >= 100000000000); // 10^11 } return 12 + digits10_v3(v / 1000000000000); // 10^12}#define item_count 100#define run_times 99int main(int argc, char **argv){ srand(100); uint64_t digit10_array[item_count]; for( int i = 0; i < item_count; ++i ) { digit10_array[i] = rand(); } struct timeval start, end; // digits10_v1 uint64_t sum1 = 0; uint64_t time1 = 0; gettimeofday(&start,null); for( int i = 0; i < run_times; ++i ) { sum1 += digits10_v1(digit10_array[i]); } gettimeofday(&end,null); time1 = ( end.tv_sec - start.tv_sec ) * 1000 * 1000 + end.tv_usec - start.tv_usec;// digits10_v2 uint64_t sum2 = 0; uint64_t time2 = 0; gettimeofday(&start,null); for( int i = 0; i < run_times; ++i ) { sum2 += digits10_v2(digit10_array[i]); } gettimeofday(&end,null); time2 = ( end.tv_sec - start.tv_sec ) * 1000 * 1000 + end.tv_usec - start.tv_usec;// digits10_v3 uint64_t sum3 = 0; uint64_t time3 = 0; gettimeofday(&start,null); for( int i = 0; i < run_times; ++i ) { sum3 += digits10_v3(digit10_array[i]); } gettimeofday(&end,null); time3 = ( end.tv_sec - start.tv_sec ) * 1000 * 1000 + end.tv_usec - start.tv_usec; std::cout << sum1: << sum1 << sum2: << sum2 << sum3: << sum3 << std::endl; std::cout << cost1: << time1 << us cost2: << time2 << us cost3: << time3 << us << cost2/cost1: << (1.0*time2)/time1 << cost3/cost1: << (1.0*time3)/time1 < short/char (0~1 clock cycle)
int --> float/double (4~16个clock cycle), signed int 快于 unsigned int,唯一一个场景 signed 比 unsigned 快的
short/char 的计算通常使用 32bit 存储,只是返回的时候做了截取,故只在要考虑内存大小的时候才使用 short/char,如 array
注:隐式类型转换可能会溢出,有符号的溢出变成负数,无符号的溢出变成小的整数
2.运算:
除法、取余运算unsigned int 快于signed int
除以常量比除以变量效率高,因为可以在编译期做优化,尤其是常量可以表示成2^n时
++i和i++本身性能一样,但不同的语境效果不一样,如array[i++]比arry[++i]性能好;当依赖自增结果时,++i性能更好,如a=++b,a和b可复用同一个寄存器
代码示例
// div和mod效率timer timer;int a, b, c;timer.tic();a = b / c; // this is slowdouble time4 = timer.toc(true);std::cout << a = b / c << time4 * 1000.0 << us << std::endl;timer.tic();a = b / 10; // division by a constant is fasterdouble time5 = timer.toc(true);std::cout << a = b / 10 << time5 * 1000.0 << us << std::endl;timer.tic();a = (unsigned int)b / 10; // still faster if unsigneddouble time6 = timer.toc(true);std::cout << a = (unsigned int)b / 10 << time6 * 1000.0 << us << std::endl;timer.tic();a = b / 16; // faster if divisor is a power of 2double time7 = timer.toc(true);std::cout << a = b / 16 << time7 * 1000.0 << us << std::endl;timer.tic();a = (unsigned int)b / 16; // still faster if unsigneddouble time8 = timer.toc(true);std::cout << a = (unsigned int)b / 16 << time8 * 1000.0 << us << std::endl;
结果:
公式 耗时(us)
a = b / c 0.054
a = b / 10 0.039
a = (unsigned int)b / 10 0.038
a = b / 16 0.036
a = (unsigned int)b / 16 0.037
3.3 浮点型:
单精度、双精度的计算性能是一样的
常量的默认精度是双精度
不要混淆单精度、双精度,混合精度计算会带来额外的精度转换开销
// 混用float a, b;a = b * 1.2; // bad. 先将b转换成double,返回结果转回成floatfloat a, b;a = b * 1.2f; // ok. everything is floatdouble a, b;a = b * 1.2; // ok. everything is double
浮点除法比乘法慢很多,故可以利用乘法来加快速度
double y, a1, a2, b1, b2;y = a1/b1 + a2/b2; // slowdouble y, a1, a2, b1, b2;y = (a1*b2 + a2*b1) / (b1*b2); // faster
实际结果差不多,在除法较多的情况下,第二种方式肯定要快一点.
y = a1/b1 + a2/b2 0.026 usy = (a1*b2 + a2*b1) / (b1*b2); 0.026 us
时间比较:
comparisons (1 clock cycle)(u)int add, subtract, bitops, shift (1 clock cycle)floating point add, sub (3~6 clock cycle) indexed array access (cache effects)(u)int32 mul (3~4 clock cycle)floating point mul (4~8 clock cycle)float point division, remainder (14~45 clock cycle)(u)int division, remainder (40~80 clock cycle)
3.4 减少内存写操作 一个很自然的优化想法,应该尽量避免内存写操作;对于内存读取,尽可能顺序访问内存
struct bitfield {int a:4;int b:2;int c:2;};bitfield x;int a, b, c;x.a = a;x.b = b;x.c = c;
假定 a、b、c 都很小,且不会溢出,可以写成
union bitfield {struct {int a:4;int b:2;int c:2;};char abc;};bitfield x;int a, b, c;x.abc = a | (b << 4) | (c << 6);
如果需要考虑溢出,也可以改为
x.abc = (a & 0x0f) | ((b & 3) << 4) | ((c & 3) <<6 );
3.5 避免不必要的函数,特别在最底层的循环,应该尽量让代码在一个函数内。看起来与良好的编码习惯冲突(一个函数最好不要超过80行),其实不然,跟这个系列其他优化一样,我们应该知道何时去使用这些优化,而不是一上来就让代码不可读。
3.6 尝试使用inline函数,让函数调用的地方直接用函数体替换。inline对编译器来说是个建议,而且不是inline了性能就好,一般当函数比较小或者只有一个地方调用的时候,inline效果会比较好
3.7 在函数内部使用循环
(e.g., change for(i=0;i<100;i++) dosomething(); into dosomething() { for(i=0;i (b) ? (a) : (b))y = max(f(x), g(x));// replace macro by templatetemplate static inline t max(t const & a, t const & b) {return a > b ? a : b;}
3.11 ** 尽可能的减少跳转和分支**,对于长的if...else,使用switch case,以减少后面条件的判断,把最容易出现的条件放在最前面
3.12 在跳转之间的代码尽量减少数据依赖
3.13 多使用引用传递参数,减少值传递
4 常见的优化手段
1. 消除条件分支
代码实例
if (a > 31;r = (mask & c) | (~mask & d);
优化版本2
int mask = (a-b) >> 31;r = d + mask & (c-d);
优化版本3
// cmovg版本r = (a < b) ?c : d;
bool 类型变换
实例代码
bool a, b, c, d;c = a && b;d = a || b;
编译器的行为是
bool a, b, c, d;if (a != 0) { if (b != 0) { c = 1; } else { goto cfalse; }}else {cfalse: c = 0;}if (a == 0) { if (b == 0) { d = 0; } else { goto dtrue; }}else {dtrue: d = 1;}
优化版本
char a = 0, b = 0, c, d;c = a & b;d = a | b;
实例代码2
bool a, b;b = !a;// 优化成char a = 0, b;b = a ^ 1;
反例
a && b 何时不能转换成 a & b,当 a 不可能为 false 的情况下
a | | b 何时不能转换成 a | b,当 a 不可能为 true 的情况下
2. 循环展开
实例代码
int i;for (i = 0; i < 20; i++) { if (i % 2 == 0) { funca(i); } else { funcb(i); } funcc(i);}
优化版本
int i;for (i = 0; i < 20; i += 2) { funca(i); funcc(i); funcb(i+1); funcc(i+1);}
优化说明
优点:减少比较次数、某些cpu上重复次数越少预测越准、去掉了if判断
缺点:需要更多的code cache or micro-op cache、有些处理器(core 2)对于小循环性能很好(小于65bytes code)、循环的次数和展开的个数不匹配
一般编译器会自动展开循环,程序员不需要主动去做,除非有一些明显优点,比如减少上面的if判断
3. 边界检查
实例代码1
const int size = 16; int i;float list[size];...if (i = size) { cout <= (unsigned int)size) { cout <= min && i <= max) { ...//优化版本if ((unsigned int)(i - min) <= (unsigned int)(max - min)) { ...
4. 使用数组
实例代码1
float a; int b;a = (b == 0) ? 1.0f : 2.5f;// 使用静态数组float a; int b;static const float oneortwo5[2] = {1.0f, 2.5f};a = oneortwo5[b & 1];
实例代码2
// 数组的长度是2的幂float list[16]; int i;...list[i & 15] += 1.0f;
5. 整形的 bit array 语义,适用于 enum、const、define
enum weekdays { sunday, monday, tuesday, wednesday, thursday, friday, saturday};weekdays day;if (day == tuesday || day == wednesday || day == friday) { dothisthreetimesaweek();}// 优化版本 using &enum weekdays { sunday = 1, monday = 2, tuesday = 4, wednesday = 8, thursday = 0x10, friday = 0x20, saturday = 0x40};weekdays day;if (day & (tuesday | wednesday | friday)) { dothisthreetimesaweek();}
人工智能在深圳正式启幕 天数智获得“AI+芯片最佳未来成长奖”
沙地英国银行计划“沙特2030年愿景”将区块链用于政府服务和相关交易
常用的电平转换方案分享
导热结构胶点胶问题的解决方案
出售Agilent86106B光/电模块
C++优化方法
创“新”正当“适” :Xilinx 积极拥抱“新基建”
如何利用人工智能进行打拐
新宙邦发布2020年年报,去年实现营业收入29.61亿元
无线WLAN的安全技术体系
智能电网的发展方向和挑战
三星S8评测:进步最大的智能手机 售价超5千成本高达2117
日本家用美容仪多次获奖,斩获少女肌的美容仪哪家强?
将区块链引入医疗系统给患者带来极大便利
Android 12使通过AirDrop解放“附近共享”轻松共享无线连接的凭据
Wolf3D致力构建跨平台服务 将不同虚拟体整合到一个的虚拟世界
永磁同步电动机原理
红米RedmiBook14体验 到底怎么样
OnRobot推出2.5D视觉系统“Eyes”,实现视觉引导机器人应用的极致简易操作
2030年苹果要实现整个供应链的碳中和