真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

性能優(yōu)化指南:性能優(yōu)化的一般性原則與方法

【本文轉(zhuǎn)自博客園 作者:xybaby 原文鏈接:https://www.cnblogs.com/xybaby/p/9055734.html】
作為一個(gè)程序員,性能優(yōu)化是常有的事情,不管是桌面應(yīng)用還是web應(yīng)用,不管是前端還是后端,不管是單點(diǎn)應(yīng)用還是分布式系統(tǒng)。本文從以下幾個(gè)方面來思考這個(gè)問題:性能優(yōu)化的一般性原則,性能優(yōu)化的層次,性能優(yōu)化的通用方法。本文不限于任何語(yǔ)言、框架,不過可能會(huì)用Python語(yǔ)言來舉例。

成都創(chuàng)新互聯(lián)公司成立與2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元南江做網(wǎng)站,已為上家服務(wù),為南江各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108

不過囿于個(gè)人經(jīng)驗(yàn),可能更多的是從Linux服務(wù)端的角度來思考這些問題。

 一般性原則

依據(jù)數(shù)據(jù)而不是憑空猜測(cè)

這是性能優(yōu)化的第一原則,當(dāng)我們懷疑性能有問題的時(shí)候,應(yīng)該通過測(cè)試、日志、profillig來分析出哪里有問題,有的放矢,而不是憑感覺、撞運(yùn)氣。一個(gè)系統(tǒng)有了性能問題,瓶頸有可能是CPU,有可能是內(nèi)存,有可能是IO(磁盤IO,網(wǎng)絡(luò)IO),大方向的定位可以使用top以及stat系列來定位(vmstat,iostat,netstat...),針對(duì)單個(gè)進(jìn)程,可以使用pidstat來分析。

在本文中,主要討論的是CPU相關(guān)的性能問題。按照80/20定律,絕大多數(shù)的時(shí)間都耗費(fèi)在少量的代碼片段里面,找出這些代碼唯一可靠的辦法就是profile,我所知的編程語(yǔ)言,都有相關(guān)的profile工具,熟練使用這些profile工具是性能優(yōu)化的第一步。

 忌過早優(yōu)化

The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.

我并不十分清楚Donald Knuth說出這句名言的上下文環(huán)境,但我自己是十分認(rèn)同這個(gè)觀念的。在我的工作環(huán)境(以及典型的互聯(lián)網(wǎng)應(yīng)用開發(fā))與編程模式下,追求的是快速的迭代與試錯(cuò),過早的優(yōu)化往往是無(wú)用功。而且,過早的優(yōu)化很容易拍腦袋,優(yōu)化的點(diǎn)往往不是真正的性能瓶頸。

忌過度優(yōu)化

As performance is part of the specification of a program – a program that is unusably slow is not fit for purpose

性能優(yōu)化的目標(biāo)是追求合適的性價(jià)比。

在不同的階段,我們對(duì)系統(tǒng)的性能會(huì)有一定的要求,比如吞吐量要達(dá)到多少多少。如果達(dá)不到這個(gè)指標(biāo),就需要去優(yōu)化。如果能滿足預(yù)期,那么就無(wú)需花費(fèi)時(shí)間精力去優(yōu)化,比如只有幾十個(gè)人使用的內(nèi)部系統(tǒng),就不用按照十萬(wàn)在線的目標(biāo)去優(yōu)化。

而且,后面也會(huì)提到,一些優(yōu)化方法是“有損”的,可能會(huì)對(duì)代碼的可讀性、可維護(hù)性有副作用。這個(gè)時(shí)候,就更不能過度優(yōu)化。

深入理解業(yè)務(wù)

代碼是服務(wù)于業(yè)務(wù)的,也許是服務(wù)于最終用戶,也許是服務(wù)于其他程序員。不了解業(yè)務(wù),很難理解系統(tǒng)的流程,很難找出系統(tǒng)設(shè)計(jì)的不足之處。后面還會(huì)提及對(duì)業(yè)務(wù)理解的重要性。

性能優(yōu)化是持久戰(zhàn)

當(dāng)核心業(yè)務(wù)方向明確之后,就應(yīng)該開始關(guān)注性能問題,當(dāng)項(xiàng)目上線之后,更應(yīng)該持續(xù)的進(jìn)行性能檢測(cè)與優(yōu)化。

現(xiàn)在的互聯(lián)網(wǎng)產(chǎn)品,不再是一錘子買賣,在上線之后還需要持續(xù)的開發(fā),用戶的涌入也會(huì)帶來性能問題。因此需要自動(dòng)化的檢測(cè)性能問題,保持穩(wěn)定的測(cè)試環(huán)境,持續(xù)的發(fā)現(xiàn)并解決性能問題,而不是被動(dòng)地等到用戶的投訴。

選擇合適的衡量指標(biāo)、測(cè)試用例、測(cè)試環(huán)境

正因?yàn)樾阅軆?yōu)化是一個(gè)長(zhǎng)期的行為,所以需要固定衡量指標(biāo)、測(cè)試用例、測(cè)試環(huán)境,這樣才能客觀反映性能的實(shí)際情況,也能展現(xiàn)出優(yōu)化的效果。

衡量性能有很多指標(biāo),比如系統(tǒng)響應(yīng)時(shí)間、系統(tǒng)吞吐量、系統(tǒng)并發(fā)量。不同的系統(tǒng)核心指標(biāo)是不一樣的,首先要明確本系統(tǒng)的核心性能訴求,固定測(cè)試用例;其次也要兼顧其他指標(biāo),不能顧此失彼。

測(cè)試環(huán)境也很重要,有一次突然發(fā)現(xiàn)我們的QPS高了許多,但是程序壓根兒沒優(yōu)化,查了半天,才發(fā)現(xiàn)是換了一個(gè)更牛逼的物理機(jī)做測(cè)試服務(wù)器。

  性能優(yōu)化的層次

按照我的理解可以分為需求階段,設(shè)計(jì)階段,實(shí)現(xiàn)階段;越上層的階段優(yōu)化效果越明顯,同時(shí)也更需要對(duì)業(yè)務(wù)、需求的深入理解。

需求階段

不戰(zhàn)而屈人之兵,善之善者也

程序員的需求可能來自PM、UI的業(yè)務(wù)需求(或者說是功能性需求),也可能來自Team Leader的需求。當(dāng)我們拿到一個(gè)需求的時(shí)候,首先需要的是思考、討論需求的合理性,而不是立刻去設(shè)計(jì)、去編碼。

需求是為了解決某個(gè)問題,問題是本質(zhì),需求是解決問題的手段。那么需求是否能否真正的解決問題,程序員也得自己去思考,在之前的文章也提到過,產(chǎn)品經(jīng)理(特別是知道一點(diǎn)技術(shù)的產(chǎn)品經(jīng)理)的某個(gè)需求可能只是某個(gè)問題的解決方案,他認(rèn)為這個(gè)方法可以解決他的問題,于是把解決方案當(dāng)成了需求,而不是真正的問題。

需求討論的前提對(duì)業(yè)務(wù)的深入了解,如果不了解業(yè)務(wù),根本沒法討論。即使需求已經(jīng)實(shí)現(xiàn)了,當(dāng)我們發(fā)現(xiàn)有性能問題的時(shí)候,首先也可以從需求出發(fā)。

需求分析對(duì)性能優(yōu)化有什么幫助呢,第一,為了達(dá)到同樣的目的,解決同樣問題,也許可以有性能更優(yōu)(消耗更小)的辦法。這種優(yōu)化是無(wú)損的,即不改變需求本質(zhì)的同時(shí),又能達(dá)到性能優(yōu)化的效果;第二種情況,有損的優(yōu)化,即在不明顯影響用戶的體驗(yàn),稍微修改需求、放寬條件,就能大大解決性能問題。PM退步一小步,程序前進(jìn)一大步。

需求討論也有助于設(shè)計(jì)時(shí)更具擴(kuò)展性,應(yīng)對(duì)未來的需求變化,這里按下不表。

 設(shè)計(jì)階段

高手都是花80%時(shí)間思考,20%時(shí)間實(shí)現(xiàn);新手寫起代碼來很快,但后面是無(wú)窮無(wú)盡的修bug

設(shè)計(jì)的概念很寬泛,包括架構(gòu)設(shè)計(jì)、技術(shù)選型、接口設(shè)計(jì)等等。架構(gòu)設(shè)計(jì)約束了系統(tǒng)的擴(kuò)展、技術(shù)選型決定了代碼實(shí)現(xiàn)。編程語(yǔ)言、框架都是工具,不同的系統(tǒng)、業(yè)務(wù)需要選擇適當(dāng)?shù)墓ぞ呒H绻O(shè)計(jì)的時(shí)候做的不夠好,那么后面就很難優(yōu)化,甚至需要推到重來。

實(shí)現(xiàn)階段

實(shí)現(xiàn)是把功能翻譯成代碼的過程,這個(gè)層面的優(yōu)化,主要是針對(duì)一個(gè)調(diào)用流程,一個(gè)函數(shù),一段代碼的優(yōu)化。各種profile工具也主要是在這個(gè)階段生效。除了靜態(tài)的代碼的優(yōu)化,還有編譯時(shí)優(yōu)化,運(yùn)行時(shí)優(yōu)化。后二者要求就很高了,程序員可控性較弱。

代碼層面,造成性能瓶頸的原因通常是高頻調(diào)用的函數(shù)、或者單次消耗非常高的函數(shù)、或者二者的結(jié)合。

下面介紹針對(duì)設(shè)計(jì)階段與實(shí)現(xiàn)階段的優(yōu)化手段。

 一般性方法

緩存

沒有什么性能問題是緩存解決不了的,如果有,那就再加一級(jí)緩存

a cache /k??/ KASH,[1] is a hardware or software component that stores data so future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation, or the duplicate of data stored elsewhere.

緩存的本質(zhì)是加速訪問,訪問的數(shù)據(jù)要么是其他數(shù)據(jù)的副本 -- 讓數(shù)據(jù)離用戶更近;要么是之前的計(jì)算結(jié)果 -- 避免重復(fù)計(jì)算.

緩存需要用空間換時(shí)間,在緩存空間有限的情況下,需要優(yōu)秀的置換換算來保證緩存有較高的命中率。

數(shù)據(jù)的緩存

這是我們最常見的緩存形式,將數(shù)據(jù)緩存在離使用者更近的地方。比如操作系統(tǒng)中的CPU cache、disk cache。對(duì)于一個(gè)web應(yīng)用,前端會(huì)有瀏覽器緩存,有cdn,有反向代理提供的靜態(tài)內(nèi)容緩存;后端則有本地緩存、分布式緩存。

數(shù)據(jù)的緩存,很多時(shí)候是設(shè)計(jì)層面的考慮。

對(duì)于數(shù)據(jù)緩存,需要考慮的是緩存一致性問題。對(duì)于分布式系統(tǒng)中有強(qiáng)一致性要求的場(chǎng)景,可行的解決辦法有l(wèi)ease,版本號(hào)。

計(jì)算結(jié)果的緩存

對(duì)于消耗較大的計(jì)算,可以將計(jì)算結(jié)果緩存起來,下次直接使用。

我們知道,對(duì)遞歸代碼的一個(gè)有效優(yōu)化手段就是緩存中間結(jié)果,lookup table,避免了重復(fù)計(jì)算。python中的method cache就是這種思想.

對(duì)于可能重復(fù)創(chuàng)建、銷毀,且創(chuàng)建銷毀代價(jià)很大的對(duì)象,比如進(jìn)程、線程,也可以緩存,對(duì)應(yīng)的緩存形式如單例、資源池(連接池、線程池)。

對(duì)于計(jì)算結(jié)果的緩存,也需要考慮緩存失效的情況,對(duì)于pure function,固定的輸入有固定的輸出,緩存是不會(huì)失效的。但如果計(jì)算受到中間狀態(tài)、環(huán)境變量的影響,那么緩存的結(jié)果就可能失效,比如我在前面提到的python method cache

 并發(fā)

一個(gè)人干不完的活,那就找兩個(gè)人干。并發(fā)既增加了系統(tǒng)的吞吐,又減少了用戶的平均等待時(shí)間。

這里的并發(fā)是指廣義的并發(fā),粒度包括多機(jī)器(集群)、多進(jìn)程、多線程。

對(duì)于無(wú)狀態(tài)(狀態(tài)是指需要維護(hù)的上下文環(huán)境,用戶請(qǐng)求依賴于這些上下文環(huán)境)的服務(wù),采用集群就能很好的伸縮,增加系統(tǒng)的吞吐,比如掛載nginx之后的web server

對(duì)于有狀態(tài)的服務(wù),也有兩種形式,每個(gè)節(jié)點(diǎn)提供同樣的數(shù)據(jù),如MySQL的讀寫分離;每個(gè)節(jié)點(diǎn)只提供部分?jǐn)?shù)據(jù),如MongoDB中的sharding

分布式存儲(chǔ)系統(tǒng)中,partition(sharding)和replication(backup)都有助于并發(fā)。

絕大多數(shù)web server,要么使用多進(jìn)程,要么使用多線程來處理用戶的請(qǐng)求,以充分利用多核CPU,再有IO阻塞的地方,也是適合使用多線程的。比較新的協(xié)程(Python greenle、goroutine)也是一種并發(fā)。

  惰性

將計(jì)算推遲到必需的時(shí)刻,這樣很可能避免了多余的計(jì)算,甚至根本不用計(jì)算,這個(gè)在之前的《lazy ideas in programming》一文中舉了許多例子。

CopyOnWrite這個(gè)思想真牛逼

批量,合并

在有IO(網(wǎng)絡(luò)IO,磁盤IO)的時(shí)候,合并操作、批量操作往往能提升吞吐,提高性能。

我們最常見的是批量讀:每次讀取數(shù)據(jù)的時(shí)候多讀取一些,以備不時(shí)之需。如GFS client會(huì)從GFS master多讀取一些chunk信息;如分布式系統(tǒng)中,如果集中式節(jié)點(diǎn)復(fù)雜全局ID生成,俺么應(yīng)用就可以一次請(qǐng)求一批id。

特別是系統(tǒng)中有單點(diǎn)存在的時(shí)候,緩存和批量本質(zhì)上來說減少了與單點(diǎn)的交互,是減輕單點(diǎn)壓力的經(jīng)濟(jì)有效的方法

在前端開發(fā)中,經(jīng)常會(huì)有資源的壓縮和合并,也是這種思想。

當(dāng)涉及到網(wǎng)絡(luò)請(qǐng)求的時(shí)候,網(wǎng)絡(luò)傳輸?shù)臅r(shí)間可能遠(yuǎn)大于請(qǐng)求的處理時(shí)間,因此合并網(wǎng)絡(luò)請(qǐng)求就很有必要,比如mognodb的bulk operation,redis 的pipeline。寫文件的時(shí)候也可以批量寫,以減少IO開銷,GFS中就是這么干的

 更高效的實(shí)現(xiàn)

同一個(gè)算法,肯定會(huì)有不同的實(shí)現(xiàn),那么就會(huì)有不同的性能;有的實(shí)現(xiàn)可能是時(shí)間換空間,有的實(shí)現(xiàn)可能是空間換時(shí)間,那么就需要根據(jù)自己的實(shí)際情況權(quán)衡。

程序員都喜歡早輪子,用于練手無(wú)可厚非,但在項(xiàng)目中,使用成熟的、經(jīng)過驗(yàn)證的輪子往往比自己造的輪子性能更好。當(dāng)然不管使用別人的輪子,還是自己的工具,當(dāng)出現(xiàn)性能的問題的時(shí)候,要么優(yōu)化它,要么替換掉他。

比如,我們有一個(gè)場(chǎng)景,有大量復(fù)雜的嵌套對(duì)象的序列化、反序列化,開始的時(shí)候是使用python(Cpython)自帶的json模塊,即使發(fā)現(xiàn)有性能問題也沒法優(yōu)化,網(wǎng)上一查,替換成了ujson,性能好了不少。

上面這個(gè)例子是無(wú)損的,但一些更高效的實(shí)現(xiàn)也可能是有損的,比如對(duì)于python,如果發(fā)現(xiàn)性能有問題,那么很可能會(huì)考慮C擴(kuò)展,但也會(huì)帶來維護(hù)性與靈活性的喪失,面臨crash的風(fēng)險(xiǎn)。

 縮小解空間

縮小解空間的意思是說,在一個(gè)更小的數(shù)據(jù)范圍內(nèi)進(jìn)行計(jì)算,而不是遍歷全部數(shù)據(jù)。最常見的就是索引,通過索引,能夠很快定位數(shù)據(jù),對(duì)數(shù)據(jù)庫(kù)的優(yōu)化絕大多數(shù)時(shí)候都是對(duì)索引的優(yōu)化。

如果有本地緩存,那么使用索引也會(huì)大大加快訪問速度。不過,索引比較適合讀多寫少的情況,畢竟索引的構(gòu)建也是需有消耗的。

另外在游戲服務(wù)端,使用的分線和AOI(格子算法)也都是縮小解空間的方法。

  性能優(yōu)化與代碼質(zhì)量

很多時(shí)候,好的代碼也是高效的代碼,各種語(yǔ)言都會(huì)有一本類似的書《effective xx》。比如對(duì)于python,pythonic的代碼通常效率都不錯(cuò),如使用迭代器而不是列表(python2.7 dict的iteritems(), 而不是items())。

衡量代碼質(zhì)量的標(biāo)準(zhǔn)是可讀性、可維護(hù)性、可擴(kuò)展性,但性能優(yōu)化有可能會(huì)違背這些特性,比如為了屏蔽實(shí)現(xiàn)細(xì)節(jié)與使用方式,我們會(huì)可能會(huì)加入接口層(虛擬層),這樣可讀性、可維護(hù)性、可擴(kuò)展性會(huì)好很多,但是額外增加了一層函數(shù)調(diào)用,如果這個(gè)地方調(diào)用頻繁,那么也是一筆開銷;又如前面提到的C擴(kuò)展,也是會(huì)降低可維護(hù)性、

這種有損代碼質(zhì)量的優(yōu)化,應(yīng)該放到最后,不得已而為之,同時(shí)寫清楚注釋與文檔。

為了追求可擴(kuò)展性,我們經(jīng)常會(huì)引入一些設(shè)計(jì)模式,如狀態(tài)模式、策略模式、模板方法、裝飾器模式等,但這些模式不一定是性能友好的。所以,為了性能,我們可能寫出一些反模式的、定制化的、不那么優(yōu)雅的代碼,這些代碼其實(shí)是脆弱的,需求的一點(diǎn)點(diǎn)變動(dòng),對(duì)代碼邏輯可能有至關(guān)重要的影響,所以還是回到前面所說,不要過早優(yōu)化,不要過度優(yōu)化。

  總結(jié)

來張腦圖總結(jié)一下

性能優(yōu)化指南:性能優(yōu)化的一般性原則與方法

本文版權(quán)歸作者xybaby(博文地址:http://www.cnblogs.com/xybaby/)所有。


新聞名稱:性能優(yōu)化指南:性能優(yōu)化的一般性原則與方法
標(biāo)題鏈接:http://weahome.cn/article/jssgsj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部