性能與伸縮性
使用線程的一種說法是為了提高性能。多線程可以使程序充分利用閑置的資源,提高資源的利用率,同時能夠并行處理任務,提高系統的響應性。 但是很顯然,引入線程的同時也引入了系統的復雜性。另外系統的性能并不是總是隨著線程數的增加而總是提高。
性能與伸縮性
性能的提升通常意味著可以用更少的資源做更多的事情。這里資源是包括我們常說的CPU周期、內存、網絡帶寬、磁盤IO、數據庫、WEB服務等等。 引入多線程可以充分利用多核的優勢,充分利用IO阻塞帶來的延遲,也可以降低網絡開銷帶來的影響,從而提高單位時間內的響應效率。
為了提高性能,需要有效的利用我們現有的處理資源,同時也要開拓新的可用資源。例如,對于CPU而言,理想狀況下希望CPU能夠滿負荷工作。當然這里滿負荷工作是指做有用的事情,而不是無謂的死循環或者等待。受限于CPU的計算能力,如果CPU達到了極限,那么很顯然我們充分利用了計算能力。對于IO而言(內存、磁盤、網絡等),如果達到了其對于的帶寬,這些資源的利用率也就上去了。理想狀況下所有資源的能力都被用完了,那么這個系統的性能達到了最大值。
為了衡量系統的性能,有一些指標用于定性、定量的分析。例如服務時間、等待時間、吞吐量、效率、可伸縮性、生成量等等。服務時間、等待時間等用于衡量系統的效率,即到底有多快。吞吐量、生成量等用于衡量系統的容量,即能夠處理多少數據。除此之外,有效服務時間、中斷時間等用于能力系統的可靠性和穩定性等。
可伸縮性的意思是指增加計算資源,吞吐量和生產量相應得到的改進。 從算法的角度講,通常用復雜度來衡量其對應的性能。例如時間復雜度、空間復雜度等。
Amdahl定律
并行的任務增加資源顯然能夠提高性能,但是如果是串行的任務,增加資源并不一定能夠得到合理的性能提升。 Amdahl定律描述的在一個系統中,增加處理器資源對系統行的提升比率。 假定在一個系統中,F是必須串行化執行的比重,N是處理器資源,那么隨著N的增加最多增加的加速比:
理論上,當N趨近于無窮大時,加速比最大值無限趨近于1/F。 這意味著如果一個程序的串行化比重為50%,那么并行化后最大加速比為2倍。
加速比除了可以用于加速的比率外,也可以用于衡量CPU資源的利用率。如果每一個CPU的資源利用率為100%,那么CPU的資源每次翻倍時,加速比也應該翻倍。 事實上,在擁有10個處理器的系統中,程序如果有10%是串行化的,那么最多可以加速1/(0.1+(1-0.1)/10)=5.3倍,換句話說CPU的利用率只用5.3/10=53%。而如果處理器增加到100倍,那么加速比為9.2倍,也就是說CPU的利用率只有個9.3%。
顯然增加CPU的數量并不能提高CPU的利用率。下圖描述的是隨著CPU的數量增加,不同串行化比重的系統的加速比。
很顯然,串行比重越大,增加CPU資源的效果越不明顯。
性能提升
性能的提升可以從以下幾個方面入手。
系統平臺的資源利用率
一個程序對系統平臺的資源利用率是指某一個設備繁忙且服務于此程序的時間占所有時間的比率。從物理學的角度講類似于有用功的比率。簡單的說就是:資源利用率=有效繁忙時間/總耗費時間。
也就說盡可能的讓設備做有用的功,同時榨取其最大值。無用的循環可能會導致CPU 100%的使用率,但不一定是有效的工作。有效性通常難以衡量,通常只能以主觀來評估,或者通過被優化的程序的行為來判斷是否提高了有效性。
延遲
延遲描述的是完成任務所耗費的時間。延遲有時候也成為響應時間。如果有多個并行的操作,那么延遲取決于耗費時間最大的任務。
多處理
多處理是指在單一系統上同時執行多個進程或者多個程序的能力。多處理能力的好處是可以提高吞吐量。多處理可以有效利用多核CPU的資源。
多線程
多線程描述的是同一個地址空間內同時執行多個線程的過程。這些線程都有不同的執行路徑和不同的棧結構。我們說的并發性更多的是指針對線程。
并發性
同時執行多個程序或者任務稱之為并發。單程序內的多任務處理或者多程序間的多任務處理都認為是并發。
吞吐量
吞吐量衡量系統在單位之間內可以完成的工作總量。對于硬件系統而言,吞吐量是物理介質的上限。在沒有達到物理介質之前,提高系統的吞吐量也可以大幅度改進性能。同時吞吐量也是衡量性能的一個指標。
瓶頸
程序運行過程中性能最差的地方。通常而言,串行的IO、磁盤IO、內存單元分配、網絡IO等都可能造成瓶頸。某些使用太頻繁的算法也有可能成為瓶頸。
可擴展性
這里的可擴展性主要是指程序或系統通過增加可使用的資源而增加性能的能力。
線程開銷
假設引入的多線程都用于計算,那么性能一定會有很大的提升么? 其實引入多線程以后也會引入更多的開銷。
切換上下文
如果可運行的線程數大于CPU的內核數,那么OS會根據一定的調度算法,強行切換正在運行的線程,從而使其它線程能夠使用CPU周期。
切換線程會導致上下文切換。線程的調度會導致CPU需要在操作系統和進程間花費更多的時間片段,這樣真正執行應用程序的時間就減少了。另外上下文切換也會導致緩存的頻繁進出,對于一個剛被切換的線程來說,可能由于高速緩沖中沒有數據而變得更慢,從而導致更多的IO開銷。
內存同步
不同線程間要進行數據同步,synchronized以及volatile提供的可見性都會導致緩存失效。線程棧之間的數據要和主存進行同步,這些同步有一些小小的開銷。如果線程間同時要進行數據同步,那么這些同步的線程可能都會受阻。
阻塞
當發生鎖競爭時,失敗的線程會導致阻塞。通常阻塞的線程可能在JVM內部進行自旋等待,或者被操作系統掛起。自旋等待可能會導致更多的CPU切片浪費,而操作系統掛起則會導致更多的上下文切換。
了解了性能的提升的幾個方面,也了解性能的開銷后,應用程序就要根據實際的場景進行取舍和評估。沒有一勞永逸的優化方案,不斷的進行小范圍改進和調整是提高性能的有效手段。當前一些大的架構調整也會導致較大的性能的提升。
簡單的原則是在保證邏輯正確的情況小,找到性能瓶頸,小步改進和優化。
參考資料
©2009-2014 IMXYLZ
|求賢若渴