日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

性能优化的一般策略及方法

發(fā)布時(shí)間:2023/11/27 windows 35 coder
生活随笔 收集整理的這篇文章主要介紹了 性能优化的一般策略及方法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

性能優(yōu)化的一般策略及方法

在汽車嵌入式開發(fā)領(lǐng)域,性能優(yōu)化始終是一個(gè)無法回避的問題:

  • 座艙 HMI 想要實(shí)現(xiàn)更流暢的人機(jī)交互
  • 通信中間件在給定的 CPU 資源下,追求更高的吞吐量
  • 更一般的場(chǎng)景:嵌入式設(shè)備 CPU 資源告急,需要降低 CPU 使用率...

不同的工程師會(huì)從不同的角度給出不同的優(yōu)化建議:

  • 有人關(guān)注系統(tǒng)調(diào)用情況
  • 有人建議從算法和數(shù)據(jù)結(jié)構(gòu)入手
  • 有人建議避免遞歸、循環(huán)嵌套
  • 有人會(huì)從存儲(chǔ)器層次結(jié)構(gòu)出發(fā),建議修改代碼提高緩存命中率來提升性能
  • ...

這些都是具體的代碼調(diào)優(yōu)技術(shù)/技巧,或許有效,但不夠系統(tǒng)。本文不討論具體的代碼調(diào)優(yōu)技術(shù),而是想介紹下具體代碼優(yōu)化技巧之上,更高層次的優(yōu)化策略。比起代碼級(jí)別的調(diào)優(yōu),可能效果更好,成本更低。

開始之前,需要強(qiáng)調(diào)下:

Premature optimization is the root of all evil. — Donald Knuth

一、性能概述

代碼調(diào)優(yōu)只是代碼性能優(yōu)化的方法之一,還有其他性能優(yōu)化的方法,也許效果更好、成本更低、對(duì)代碼的負(fù)面影響(降低可讀性/可維護(hù)性、引入 bug 等)也更少。

1.1 軟件質(zhì)量和性能

性能只是眾多軟件質(zhì)量標(biāo)準(zhǔn)中的一個(gè)。比起單純的代碼執(zhí)行速度,用戶可能更在意其他方面,比如穩(wěn)定可靠、簡(jiǎn)潔易用等。

性能也不只是代碼的執(zhí)行速度,過分追求代碼的執(zhí)行速度而忽略其他方面可能會(huì)影響整體性能及軟件質(zhì)量。

1.2 性能和代碼調(diào)優(yōu)

假如確定了把 Efficiency 作為首要目標(biāo),在代碼調(diào)優(yōu)之前,請(qǐng)優(yōu)先考慮:

  • 性能需求
  • 程序設(shè)計(jì)
  • 類和方法設(shè)計(jì)
  • 操作系統(tǒng)交互
  • 編譯器優(yōu)化
  • 硬件升級(jí)
  • 代碼調(diào)優(yōu)

a. 性能需求

Barry Boehm 講過一個(gè)故事:某系統(tǒng)一開始要求亞秒級(jí)的響應(yīng)時(shí)間,導(dǎo)致非常復(fù)雜的設(shè)計(jì),預(yù)估成本 1 億美元。后來分析發(fā)現(xiàn),90%的情況下,用戶可以接受 4s 的響應(yīng)時(shí)間。重新修改需求之后,節(jié)省了 7000 萬美元。

再舉一個(gè)例子,自動(dòng)駕駛算法需要周期性獲取某些車輛數(shù)據(jù),當(dāng)前的需求是 10ms 的周期上報(bào)。如果將周期改為 20ms 仍然可以滿足需求,那么不需要任何額外的優(yōu)化,CPU 占用率便可減少一半。

解決性能問題之前,先確認(rèn)是否真的必要。

b. 程序設(shè)計(jì)

軟件架構(gòu)設(shè)計(jì)主要如何將程序分解到模塊/類。有的設(shè)計(jì)決定了很難實(shí)現(xiàn)高性能,有的設(shè)計(jì)則容易實(shí)現(xiàn)高性能。

在軟件的架構(gòu)設(shè)計(jì)中,設(shè)定資源占用的目標(biāo)很重要:如果每個(gè)組件都能達(dá)成目標(biāo),則整個(gè)系統(tǒng)自然也可以。如果某個(gè)組件無法達(dá)成目標(biāo),也可以及早發(fā)現(xiàn),進(jìn)行設(shè)計(jì)修改或代碼優(yōu)化。不僅如此,清晰的目標(biāo)也更利于執(zhí)行和實(shí)施。

c. 類和方法設(shè)計(jì)

在程序設(shè)計(jì)基礎(chǔ)上更近一步,深入到類的內(nèi)部。在這一層級(jí),我們可以選擇數(shù)據(jù)結(jié)構(gòu)和算法,從而影響程序的執(zhí)行速度和內(nèi)存占用。

d. 操作系統(tǒng)交互

如果程序中涉及外部文件、動(dòng)態(tài)內(nèi)存、輸出設(shè)備,通常會(huì)和操作系統(tǒng)交互。如果程序性能不好,有可能就是系統(tǒng)調(diào)用過多導(dǎo)致的。有時(shí)系統(tǒng)庫或編譯器會(huì)在你意想不到的地方產(chǎn)生系統(tǒng)調(diào)用。

e. 編譯器

編譯器優(yōu)化比手工優(yōu)化代碼效果更好,也更安全!某種程度上來說,選擇了正確的編譯器,基本就不需要考慮代碼級(jí)優(yōu)化了。

f. 硬件

有時(shí)候升級(jí)硬件是解決性能問題成本最低的方案。不僅節(jié)省了性能優(yōu)化的人力成本,同時(shí)還避免了由于性能優(yōu)化引入的一系列隱性成本。同時(shí),所有其他程序也因?yàn)橛布?jí)而得到性能提升。

g. 代碼調(diào)優(yōu)(Code Tuning)

“代碼調(diào)優(yōu)”指的是修改正確的代碼,使之運(yùn)行得更快。代碼調(diào)優(yōu)的前提是代碼正確:設(shè)計(jì)良好,易于理解和修改。“調(diào)優(yōu)”指的是小規(guī)模修改,一個(gè)類,一個(gè)函數(shù)或者幾行代碼。“調(diào)優(yōu)”不包括大規(guī)模設(shè)計(jì)修改,以及更高層次的性能優(yōu)化手段。

上面從程序設(shè)計(jì)到代碼調(diào)優(yōu)六個(gè)層級(jí)中,每一個(gè)層級(jí)都可能產(chǎn)生 10 倍的性能提升,不同層級(jí)的組合起來理論上可以有百萬倍的提升。雖然實(shí)際不可能在每個(gè)層級(jí)都取得 10 倍的提升,但是這里想表達(dá)的是,性能優(yōu)化的空間潛力是巨大的。

二、代碼調(diào)優(yōu)

2.1 二八法則

a. 優(yōu)化哪里

有研究和報(bào)告表明:

  • 20% 的函數(shù)占用了 80% 的程序執(zhí)行時(shí)間
  • <4% 的代碼甚至能占用 50% 的執(zhí)行時(shí)間

不是每一行代碼都要做到最快,真正值得花時(shí)間把性能調(diào)到極致的代碼只有很小的一部分!

b. 誰來優(yōu)化

項(xiàng)目中系統(tǒng)整體的 CPU 接近滿負(fù)荷,其中 A 負(fù)責(zé)的模塊 CPU 占用 5%,而 B 負(fù)責(zé)的模塊 CPU 占用超過 60%。即便 A 再厲害,把自己優(yōu)化沒了,帶來的整體收益也不過 5%,而 B 卻因?yàn)橛懈蟮膬?yōu)化空間,能輕松地地降低 10%的 CPU 占用。

2.2 常見誤區(qū)

很多過時(shí)的、傳說中的代碼優(yōu)化技巧都是無效的,甚至能夠產(chǎn)生負(fù)面影響。

誤區(qū) 1: 代碼行數(shù)越少,程序越快

很容易找到一個(gè)反例:初始化大小為 N 的數(shù)組,直接寫出 N 條賦值語句,其性能是循環(huán)賦值的 2.5~4 倍!

誤區(qū) 2: xxxx 寫法很很可能更快

對(duì)于性能而言,沒有所謂的“很可能”,必須實(shí)際測(cè)量才知道到底是“優(yōu)化”了還是“劣化”了。影響性能的因素很多:處理器架構(gòu)、編程語言、編譯器、編譯器版本、庫、庫的版本、內(nèi)存大小...“很可能”是非常不負(fù)責(zé)任的說法,對(duì)于特定的環(huán)境是優(yōu)化,在另外環(huán)境下很能就是劣化。再次強(qiáng)調(diào),必須要實(shí)際測(cè)量!

此外,為了“性能優(yōu)化”而引入的特殊寫法,反而會(huì)影響編譯器的優(yōu)化。

誤區(qū) 3: 從一開始就寫要出“快”的代碼

在程序沒最終完成之前,幾乎不可能識(shí)別出真正的性能瓶頸,你所“優(yōu)化”的代碼中,96%其實(shí)不需要優(yōu)化。過分關(guān)注執(zhí)行速度反而會(huì)影響軟件質(zhì)量的其他方面。

Premature optimization is the root of all evil. — Donald Knuth

誤區(qū) 4: “快”和“正確”同等重要

如果程序不能正確運(yùn)行,或者運(yùn)行結(jié)果不正確,即使再快也沒有任何價(jià)值。

2.3 什么時(shí)候去調(diào)優(yōu)

Jackson's Rule of Optimization:

Rule 1. Don't do it.

Rule 2 (for expert only). Don't do it yet -- that is, not until you have a perfectly clear and unoptimized solution.

簡(jiǎn)言之,非必要,不優(yōu)化。先保證良好的設(shè)計(jì),編寫易于理解和修改的整潔代碼。如果現(xiàn)有的代碼很糟糕,先清理重構(gòu),然后再考慮優(yōu)化。

2.4 編譯器優(yōu)化

現(xiàn)代編譯器優(yōu)化遠(yuǎn)比你想象中的更強(qiáng)大。例如編譯器能夠識(shí)別并優(yōu)化循環(huán)嵌套,比手動(dòng)優(yōu)化更安全,效果也更好。不要自作聰明地用一些幾十年前所謂的特殊“優(yōu)化技巧”,大概率會(huì)給編譯器造成困擾,適得其反。

  • 各家的編譯器各有優(yōu)缺點(diǎn),選擇最適合項(xiàng)目的編譯器

  • 開啟編譯器的不同優(yōu)化選項(xiàng),性能可提升為原來的 2 倍甚至更多

程序員應(yīng)該專注于寫整潔代碼(設(shè)計(jì)良好,意圖明確清晰,可讀性好,易于維護(hù)),優(yōu)化的事情交給編譯器就好啦!

三、導(dǎo)致性能問題的常見原因

3.1 常見性能問題元兇

a. 輸入/輸出操作

不必要的 I/O 操作是最常見的導(dǎo)致性能問題的罪魁禍?zhǔn)住1热珙l繁讀寫磁盤上的文件、通過網(wǎng)絡(luò)訪問數(shù)據(jù)庫等。一般來說,內(nèi)存的讀寫性能是磁盤的幾千幾萬倍,如果有內(nèi)存不是很 critical,可以將數(shù)據(jù)保存在內(nèi)存中以減少不必要的 IO 操作從而改善性能。

幾年前在一個(gè)基于 Qt 的座艙項(xiàng)目中,從 CarPlay 界面返回車機(jī)首頁會(huì)有短暫的卡頓,導(dǎo)致無法通過 CarPlay 的認(rèn)證。用 QmlProfiler 分析發(fā)現(xiàn),切換卡頓是由于從磁盤加載背景圖片導(dǎo)致的,將背景圖片緩存在內(nèi)存中,可以直接消除圖片加載時(shí)間,大幅提升界面切換的流暢度。代價(jià)是犧牲了一定的內(nèi)存,這是一個(gè)空間換時(shí)間的典型例子。

b. 缺頁

有一個(gè)經(jīng)典的例子:

// BAD
for (int col = 0; col < MAX_COLUMNS; ++col) {
  for(int row = 0; row < MAX_ROWS; ++row) {
      table[row][col] = GetDefaultValue();
  }
}

// GOOD
for (int row = 0; row < MAX_ROWS; ++row) {
  for(int col = 0; col < MAX_COLUMNS; ++col) {
      table[row][col] = GetDefaultValue();
  }
}

以上兩種寫法在特定場(chǎng)景下,性能差距可達(dá) 1000 倍。背后涉及到二維數(shù)組在內(nèi)存中的存儲(chǔ)方式以及緩存命中等知識(shí),CSAPP 的第 5、6 章對(duì)此有詳細(xì)闡述。

c. 系統(tǒng)調(diào)用

系統(tǒng)調(diào)用需要進(jìn)行上下文切換,保存程序狀態(tài)、恢復(fù)內(nèi)核狀態(tài)等一些步驟,開銷相對(duì)較大。對(duì)磁盤的讀寫操作、對(duì)鍵盤、屏幕等外設(shè)的操作、內(nèi)存管理函數(shù)的調(diào)用等都屬于系統(tǒng)調(diào)用。

Linux 系統(tǒng)調(diào)用可以通過 strace 查看,qnx 也有 tracelogger 等工具

d. 解釋型語言

一般來說,C/C++/VB/C# 這種編譯型語言的性能好于 Java 的字節(jié)碼,好于 PHP/Pyhon 等解釋型語言。這也是為什么汽車嵌入式領(lǐng)域還是 C/C++ 天下等主要原因。

e. 錯(cuò)誤

還有很大很一部分導(dǎo)致性能問題的原因可以歸為錯(cuò)誤:忘了把調(diào)試代碼(如保存 trace 到文件)關(guān)閉,忘記釋放資源/內(nèi)存泄漏、數(shù)據(jù)庫表設(shè)計(jì)缺陷(常用表沒有索引)等。

3.2 常見操作的相對(duì)開銷

操作 示例 相對(duì)耗時(shí)(C++)
整數(shù)賦值(基準(zhǔn)) i = j 1
函數(shù)調(diào)用
普通函數(shù)調(diào)用(無參) foo() 1
普通函數(shù)調(diào)用(單參) foo(i) 1.5
普通函數(shù)調(diào)用(雙參) foo(i,j) 2
類的成員函數(shù)調(diào)用 bar.foo() 2
子類的成員函數(shù)調(diào)用 derivedBar.foo() 2
多態(tài)方法調(diào)用 abstractBar.foo() 2.5
對(duì)象解引用
訪問對(duì)象成員(一級(jí)/二級(jí)) i = obj1.obj2.num 1
整數(shù)運(yùn)算
整數(shù)賦值/加/減/乘 i = j * k 1
整數(shù)除法 i = j / k 5
浮點(diǎn)運(yùn)算
浮點(diǎn)賦值/加/減/乘 x = y * z 1
浮點(diǎn)除法 x = y / z 4
超越函數(shù)
浮點(diǎn)根號(hào) y = sqrt(x) 15
浮點(diǎn) sin y = sin(x) 25
浮點(diǎn)對(duì)數(shù) y = log(x) 25
浮點(diǎn)指數(shù) y = exp(x) 50
數(shù)組操作
一維/二維整數(shù)/浮點(diǎn)數(shù)組下標(biāo)訪問 x = a[3][j] 1

注:上表僅供參考,不同處理器、不同語言、不同編譯器、不同測(cè)試環(huán)境所得結(jié)果可能相差很大!

代碼調(diào)優(yōu)的方式之一就是用低開銷的操作替代高開銷操作。一般操作(賦值、函數(shù)調(diào)用、算數(shù)運(yùn)算)的開銷基本相同,除法運(yùn)算開銷較大,超越函數(shù)開銷尤其巨大,多態(tài)函數(shù)的調(diào)用較普通函數(shù)調(diào)用有一定額外開銷。

四、測(cè)量

代碼執(zhí)行耗時(shí)和代碼量不成比例,必須經(jīng)過測(cè)量才知道時(shí)間花在哪里。找到問題,優(yōu)化,重新測(cè)量。

性能優(yōu)化很多時(shí)候是反直覺的(比如代碼量越少不一定越快),只有測(cè)量了才知道是否有效果。

過往的經(jīng)驗(yàn)可能不會(huì)有太多幫助,針對(duì)舊的機(jī)器、語言、編譯器的優(yōu)化經(jīng)驗(yàn)在現(xiàn)在可能完全不適用,必須要實(shí)際測(cè)量了才知道!

比如在舊版本的編譯器中,把二維數(shù)組的操作轉(zhuǎn)為對(duì)單個(gè)指針操作可以提升性能,而在新的編譯器卻完全沒有效果,因?yàn)樾掳婢幾g器會(huì)自動(dòng)進(jìn)行這樣的轉(zhuǎn)化。而手動(dòng)修改代碼只會(huì)降低代碼的可讀性。

測(cè)量要準(zhǔn)確

  • 用專門的 Profiling 工具或者系統(tǒng)時(shí)間
  • 只測(cè)量你自己的代碼部分
  • 必要時(shí)需要用 CPU 時(shí)鐘 tick 數(shù)來替代時(shí)間戳以獲得更準(zhǔn)確的測(cè)量結(jié)果

要想準(zhǔn)確的測(cè)量是一件非常困難的事情。不同的硬件、進(jìn)程的優(yōu)先級(jí)、線程調(diào)度策略、測(cè)量時(shí)其他的進(jìn)程的運(yùn)行、甚至外界環(huán)境都可能對(duì)測(cè)量結(jié)果產(chǎn)生影響。我們能做的就是盡可能地控制變量,剔除無關(guān)因素影響。

五、迭代

很難只用一個(gè)技巧就把性能提升 10 倍,但是可以不斷嘗試,組合不同技巧,最終實(shí)現(xiàn)巨大的性能提升。下面是一個(gè)通過不斷迭代優(yōu)化,將執(zhí)行時(shí)間從 21 分 40 秒優(yōu)化到 22 秒的例子:

優(yōu)化項(xiàng) 執(zhí)行時(shí)間
初版,直接實(shí)現(xiàn) 21:40
bit 轉(zhuǎn)數(shù)組 7:30
展開最內(nèi)層 for 循環(huán) 6:00
去除最終排列 5:24
合并 2 個(gè)變量 5:06
合并算法的前兩步 4:30
在內(nèi)層循環(huán)中,使兩個(gè)變量共享同一內(nèi)存 3:36
在外層循環(huán)中,使兩個(gè)變量共享同一內(nèi)存 3:09
展開所有循環(huán),使用字面量下標(biāo) 1:36
去除所有函數(shù)調(diào)用,把代碼寫在一行 0:45
用匯編重寫整個(gè)函數(shù) 0:22

六、調(diào)優(yōu)一般方法

  1. 程序設(shè)計(jì)良好,易于理解和修改(前提)
  2. 如果性能不佳:
    a. 保存當(dāng)前狀態(tài)
    b. 測(cè)量,找出時(shí)間主要消耗在哪里
    c. 分析問題:是否因?yàn)楦邔釉O(shè)計(jì)、數(shù)據(jù)結(jié)構(gòu)、算法導(dǎo)致的,如果是,返回步驟 1
    d. 如果設(shè)計(jì)、數(shù)據(jù)結(jié)構(gòu)、算法沒問題,針對(duì)上述步驟中的瓶頸進(jìn)行代碼調(diào)優(yōu)
    e. 每進(jìn)行一項(xiàng)優(yōu)化,立即進(jìn)行測(cè)量
    f. 如果沒有效果,恢復(fù)到 a 的狀態(tài)。(大多數(shù)的調(diào)優(yōu)嘗試幾乎不會(huì)對(duì)性能產(chǎn)生影響,甚至產(chǎn)生負(fù)面影響。代碼調(diào)優(yōu)的前提是代碼設(shè)計(jì)良好,易于理解和修改。Code tuning 通常會(huì)對(duì)設(shè)計(jì)、可讀性、可維護(hù)性產(chǎn)生負(fù)面影響,如果 tuning 改良了設(shè)計(jì)或者可讀性,那么不應(yīng)該叫 tuning,而是屬于步驟 1)
  3. 重復(fù)步驟 2

七、總結(jié)

  • 性能只是眾多軟件質(zhì)量指標(biāo)中的一個(gè),而且一般不是最重要的那個(gè)。精心調(diào)優(yōu)之后的代碼也只能對(duì)整體性能產(chǎn)生部分影響,程序架構(gòu)、詳細(xì)設(shè)計(jì)、數(shù)據(jù)結(jié)構(gòu)/算法的選擇、編譯器通常比代碼本身對(duì)性能的影響更大。
  • 準(zhǔn)確地測(cè)量至關(guān)重要
    • 絕大多數(shù)程序的大部分時(shí)間都耗在少數(shù)代碼上,只有測(cè)量了才知道時(shí)間花在了哪里,優(yōu)化重點(diǎn)在哪里
    • 很多“優(yōu)化技巧”實(shí)際上不僅不會(huì)提高性能,甚至?xí)档托阅埽挥袦y(cè)量了才能知道
    • 測(cè)量越接近真實(shí)環(huán)境越好,模擬的測(cè)試環(huán)境和程序?qū)嶋H運(yùn)行環(huán)境可能得到完全不同的結(jié)果!
  • 通常需要多輪優(yōu)化迭代才能達(dá)到預(yù)期性能目標(biāo)
  • 如果想為今后(可能)的性能優(yōu)化提前作準(zhǔn)備,最好的準(zhǔn)備就是編寫易于理解和修改的整潔代碼

7.1 檢查清單

  1. 明確需求,是否真的有這么高的性能要求?
  2. 嘗試提高編譯器優(yōu)化選項(xiàng)?
  3. 考慮升級(jí)/更換編譯器?
  4. 考慮過升級(jí)/更換硬件?
  5. 程序的 high-level design、類設(shè)計(jì)是否合理?
  6. 檢查是否有不必要的系統(tǒng)調(diào)用、I/O 操作?
  7. 考慮用編譯型語言替代解釋型語言?
  8. 代碼調(diào)優(yōu)是否作為最后手段?

7.2 代碼調(diào)優(yōu)方法

  1. 調(diào)優(yōu)的前提:代碼正確,設(shè)計(jì)良好,易于理解和修改
  2. 測(cè)量,找出瓶頸
  3. 每次優(yōu)化后,立即重新測(cè)量
  4. 如果沒有效果,撤銷改動(dòng)
  5. 嘗試多種方法,不斷迭代

八、擴(kuò)展閱讀

  • 《CSAPP》第 5、6 章
  • 《Code Complete》第 25、26 章
  • 《C++ Core Guidelines》Per 章節(jié)

總結(jié)

以上是生活随笔為你收集整理的性能优化的一般策略及方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。