C++优化热点语句
優(yōu)化熱點(diǎn)語句
提前計(jì)算固定值
先觀察下面的性能測(cè)試代碼:
static void find_blank(benchmark::State& state) {for (auto _: state) {char s[] = "This string has many space (0x20) chars. ";for (size_t i = 0; i < strlen(s); ++i)if (s[i] == ' ')s[i] = '*';} } BENCHMARK(find_blank);這段代碼對(duì)字符串中的每個(gè)字符都會(huì)判斷循環(huán)條件 i < strlen(s) 是否成立 1。調(diào)用strlen() 的開銷是昂貴的,遍歷參數(shù)字符串對(duì)它的字符計(jì)數(shù)使得這個(gè)算法的開銷從 O(n)變?yōu)榱?O(n2)。這是一個(gè)在庫函數(shù)中隱藏了循環(huán)的典型例子
既然,每次strlen()調(diào)用都會(huì)導(dǎo)致一次遍歷,并且計(jì)算結(jié)果不會(huì)隨著函數(shù)的運(yùn)行而改變,可以嘗試先求出strlen保存,然后后期就直接使用計(jì)算結(jié)果,而不是每次循環(huán)都進(jìn)行計(jì)算,如下:
static void find_blank_init_length(benchmark::State& state) {for (auto _: state) {char s[] = "This string has many space (0x20) chars. ";for (size_t i = 0, len = strlen(s); i < len; ++i)if (s[i] == ' ')s[i] = '*';} }BENCHMARK(find_blank_init_length);測(cè)試結(jié)果如下:
----------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------- find_blank 191 ns 191 ns 3431752 find_blank_init_length 72.4 ns 72.4 ns 9635766在禁用變異優(yōu)化的選項(xiàng)下編譯,從計(jì)算結(jié)果可以看出,更改之后整個(gè)函數(shù)性能提升了將近3倍左右。
使用更加高效的循環(huán)
通常for循環(huán)將會(huì)被編譯成如下的代碼:
初始化表達(dá)式; L1: if (!循環(huán)條件) goto L2;語句;繼續(xù)表達(dá)式;goto L1; L2:而do-while編譯之后一般為:
L1: 控制語句if (循環(huán)條件) goto L1;當(dāng)然不同的編譯器可能實(shí)現(xiàn)不一樣,按照上述分析使用do-while肯定要比for循環(huán)要好很多,但是,在ubuntu20.04上實(shí)際測(cè)試for循環(huán)的速度基本上和do-while保持一致,也可能是for循環(huán)用的多,所以編譯器哪些大佬特意特意進(jìn)行了優(yōu)化。
do-while的實(shí)現(xiàn):
static void find_blank_do_while(benchmark::State& state) {for (auto _: state) {char s[] = "This string has many space (0x20) chars. ";size_t i = 0, len = strlen(s);do {if (s[i] == ' ')s[i] = '*';++ i;}while (i < len);} }BENCHMARK(find_blank_do_while);實(shí)際測(cè)試結(jié)果:
----------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------- find_blank 191 ns 191 ns 3431752 find_blank_init_length 72.4 ns 72.4 ns 9635766 find_blank_do_while 71.6 ns 71.6 ns 9629498使用編譯器進(jìn)行優(yōu)化
在不更改代碼的情況下,可以更改優(yōu)化選項(xiàng),告訴編譯器可以對(duì)代碼進(jìn)行優(yōu)化,當(dāng)編譯器選項(xiàng)由O0更改為O3之后的測(cè)試結(jié)果如下:
----------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------- find_blank 60.4 ns 60.4 ns 10536782 find_blank_init_length 34.6 ns 34.6 ns 20298248 find_blank_do_while 34.4 ns 34.4 ns 20249507 ----------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------- find_blank 60.2 ns 60.2 ns 9566706 find_blank_init_length 34.4 ns 34.4 ns 20362644 find_blank_do_while 34.6 ns 34.6 ns 20355712多次運(yùn)行之后,使用for循環(huán)和使用do-while結(jié)構(gòu)基本上沒有任何差別,而代價(jià)就是編譯過程慢了一點(diǎn)。
總結(jié)
- 上一篇: 作者:郑纬民
- 下一篇: EffectiveC++编程的50个建议