編寫正常運(yùn)行的程序很容易冰评,但一旦數(shù)據(jù)量大起來,對代碼的性能就需要認(rèn)真考慮了木羹。對于服務(wù)器來說甲雅,如果客戶端一次訪問,就需要話費(fèi)幾百毫秒坑填,那么一旦每秒的訪問次數(shù)多起來抛人,后續(xù)的請求就會(huì)造成很明顯的延遲。嚴(yán)重影響用戶體驗(yàn)脐瑰。最近做后端服務(wù)也遇到了一些需要優(yōu)化的問題妖枚,數(shù)據(jù)量大了后,有明顯延遲苍在。
性能優(yōu)化的前提是良好的構(gòu)架設(shè)計(jì):如果架構(gòu)本身的設(shè)計(jì)就存在問題盅惜,在怎么優(yōu)化也所能提升的空間也是很小。根據(jù)二八原則忌穿,大部分性能應(yīng)該消耗在很少的地方抒寂,所以優(yōu)化的關(guān)注點(diǎn)就在于那20%最耗時(shí)的代碼塊,解決了這個(gè)問題掠剑,整體性能就會(huì)有很大的提升屈芜。
代碼層面的優(yōu)化一般涉及到一下方面:
1.并發(fā)模型的選擇
對于不同的功能需要用不同的并發(fā)模型,適合單線程還是多線程朴译,以及線程池這些都會(huì)影響到性能井佑,對于服務(wù)器,模塊是io密集型還是計(jì)算密集型眠寿。例如如果一次請求是需要做很多的計(jì)算躬翁,那么用線程池是合理的能夠簡化編程,但是如果一次請求主要時(shí)間是等待io盯拱,那么線程池是無法提升吞吐量的盒发。
2.數(shù)據(jù)的存儲(chǔ)
容器的選擇
數(shù)據(jù)對象的存儲(chǔ)方式取決于對數(shù)據(jù)的使用方式例嘱,就拿C++容器來說,如果要存儲(chǔ)物品信息宁舰,一般系統(tǒng)中更多的是根據(jù)物品id來獲取物品信息拼卵,那么久需要用key-value來存取,但map是紅黑樹蛮艰,unorder_map是哈希表腋腮,哈希表的查詢時(shí)間是O(1),而對于map,插入壤蚜、刪除都是O(logN)即寡,最壞和平均都是。
存取方式
是存指針還是存數(shù)據(jù)袜刷,這個(gè)需要根據(jù)具體問題聪富,具體分析,盡量高效水泉。
3.算法
算法的存在就是為了高效的解決一些復(fù)雜問題,以減少時(shí)間或者空間復(fù)雜度窒盐。一個(gè)排序就有很多種選擇草则,冒泡,選擇蟹漓,快排炕横,堆排序,堆排序葡粒,歸并等份殿。一般來說快排的整體效率比較高,那是針對于基本無序的序列嗽交,如果只是后來的元素?zé)o序卿嘲,前面基本有序,這時(shí)候簡單的使用快排就會(huì)造成降低性能夫壁。
4.代碼質(zhì)量
使用引用
在C++中盡量采用引用傳遞或者指針傳遞拾枣,這樣不會(huì)產(chǎn)生參數(shù)拷貝,如果傳入?yún)?shù)是一個(gè)復(fù)雜的數(shù)據(jù)結(jié)構(gòu)盒让,就能明顯的提升性能梅肤。同理獲取數(shù)據(jù)時(shí)也要用引用。
減少副作用
例如C++是不進(jìn)行邊界檢查的邑茄,對于map的獲取value姨蝴,如果該key不在map中,map會(huì)插入一個(gè)key肺缕,value為默認(rèn)值
std::map<int,int> i_map;
int value = i_map[123];
map是紅黑樹實(shí)現(xiàn)的左医,紅黑樹最終是一平衡樹授帕,這個(gè)插入操作會(huì)導(dǎo)致原來的樹不平衡,內(nèi)部就需要翻轉(zhuǎn)達(dá)到平衡炒辉,這就會(huì)造成性能消耗豪墅。
同一變量的多次獲取:
std::vector<int> v_int;
...
for (int i = 0; i < v_int.size(); i++)
怎么寫出高質(zhì)量的C++代碼黔寇,推薦一本書 《高質(zhì)量C++/C編程指南》偶器。
同步鎖
服務(wù)器編程會(huì)涉及到多線程,一旦涉及多線程缝裤,多線程中很多涉及到競爭資源的非原子操作都需要進(jìn)行加鎖屏轰,加鎖的方式有很多種有信號量,互斥器憋飞,條件變量霎苗,讀寫鎖。絕大多數(shù)情況使用互斥器就可以足夠?qū)崿F(xiàn)大多數(shù)功能榛做,而且相對于其他鎖性能影響較小唁盏。但在使用mutex時(shí)也需要注意。
- 鎖的范圍:只在需要同步的操作前后加鎖和解鎖检眯,避免造成其他線程的無效的等待厘擂,造成性能損失。
- 盡量將操作劃分锰瘸,對于多種無前后依賴的臨界資源刽严,用多個(gè)互斥器。