游戲引擎學習第113天

倉庫:https://gitee.com/mrxiao_com/2d_game_2

黑板:優化的基本過程

在游戲編程中,優化是一個非常重要的學習內容,尤其是想要成為專業開發者時。優化的核心是理解代碼的執行速度,以及如何提升其性能。在這個階段,已經開始討論如何有效地進行優化。

首先,需要收集統計數據,這是優化的第一步。收集數據的目的是為了了解程序運行緩慢的地方,找出具體的瓶頸,并分析這些部分的特點,比如它們處理了多少內容、執行的頻率如何等。這些信息將幫助確定哪些代碼部分需要被優化。

接下來,基于收集到的統計數據,進行估算。估算的目的是根據現有數據,推測出代碼應該達到的理想性能。盡管很難做到準確,但可以通過估算得到一個大致的參考值,了解當前性能與理想性能之間的差距。

然后,進行效率和性能分析。效率和性能是兩個不同的概念,效率指的是代碼實際做了多少工作,而性能則是指完成這些工作的速度。提高效率意味著減少實際需要做的工作量,而提高性能則是讓這些工作通過CPU更快速地完成。

一旦分析了效率和性能的差異,就可以判斷出優化的潛力。最后,根據分析結果,開始編寫優化代碼,并測試其效果,看是否能夠提升性能。

總結來說,優化過程包含四個步驟:收集數據、進行估算、分析效率和性能、以及編寫優化代碼。通過這一過程,可以系統地改進代碼的執行效率和速度。

收集統計數據

首先要進行的是收集統計數據,這主要涉及編程的部分。我們需要開始收集一些統計信息。收集統計數據的方式是對代碼進行“圖表化”,即在代碼中添加一些內容,使其能夠報告統計數據給我們。雖然有一些商業程序可以分析代碼,如 Intel 的 vtune 等,但這里不打算使用這些工具,而是展示如何自己編寫并收集統計數據。如果進行深入優化,可能會選擇使用一些工具,像 Intel 提供的工具,或者其他地方的工具,它們能提供一些程序運行的內部信息,這些是我們通過簡單的儀表化可能無法得到的。

我們將專注于實現一個簡單的統計收集方法。首先,需要知道的是,在 CPP 中,游戲的更新和渲染是我們負責的代碼部分(即不在 Windows 系統內)。我們想要了解的是:游戲更新和渲染調用總共消耗了多少處理器周期。然后,我們還希望能看到,在其中一些代碼片段的執行過程中,分別消耗了多少處理器周期,這樣可以幫助我們大致了解哪些部分的代碼可能會影響程序的運行速度。

雖然我們已經知道渲染器目前非常慢,因為在進行相關更改時已經觀察到它的性能下降,所以此時并不一定需要重新分析渲染器部分的代碼。但為了能夠跟蹤那些性能可能不那么直觀的部分,還是決定進行一些通用的儀表化工作。這么做是為了展示如何找出性能瓶頸。實際上,如果只想盡可能快速地完成這個過程,我們可以直接獲取游戲代碼的總處理器周期數,再獲取繪制矩形慢操作的處理器周期數,然后對比兩者,直接看出慢的部分。但現在,決定采用稍微復雜一點的方法來實現這個目標。

重新審視 __rdtsc

目標是創建一個簡單的系統,能夠記錄處理器周期并計算兩個位置之間所花費的周期數。實際上,這種功能我們已經實現過一次,早在處理 Win32 平臺層時就已經有過類似的代碼,這段代碼被稱為 DTSC

DTSC 使用了一個處理器指令,稱為 read time stamp counter,這是編譯器通過內嵌指令生成的匯編指令。這個指令的作用是提供一個來自處理器的計數器,表示當前處理器在指令流中的位置,換句話說,它表示處理器執行了多少個周期。需要注意的是,“周期”這個詞現在有些模糊,因為不同的處理器可能以不同的方式報告周期時間,特別是在啟用了“SpeedStep”等技術的處理器上。

不過,現代處理器通常會給出在滿速運行下的周期數,即使在啟用節能模式(如筆記本的處理器會在不同負載下調整功耗)時,可能也能給出一個相對準確的周期計數。如果處理器頻率時常變化,可能需要更小心地處理這些周期數據,但如果處理器在高性能模式下運行,假設 DTSC 提供的周期數與實際周期數接近,可以大致認為數據是準確的。

GAME_UPDATE_AND_RENDER:添加 __rdtsc 循環計數

可以像之前那樣,通過記錄開始和結束時的 __rdtsc 來計算經過的周期數。具體做法是,在函數的開始和結束處獲取時間戳計數器的值,然后通過計算這兩個值的差異來得出執行該代碼段所花費的周期數。

簡單來說,首先在代碼段開始處記錄一個起始周期計數,使用 __rdtsc 獲取初始計數值;然后在代碼段結束時記錄一個結束周期計數,同樣使用 __rdtsc。計算這兩個值的差異,即可得到該代碼段執行所消耗的處理器周期數。

基本的實現方式是,在代碼開始時獲取開始周期數,在結束時獲取結束周期數,然后計算它們的差值。這樣就能夠得到該段代碼執行所用的總周期數。

然而,在這個過程中存在一個問題。
在這里插入圖片描述

在 game_platform.h 中引入 debug_cycle_counter

目前,我們沒有辦法將任何信息輸出到屏幕上。雖然 Win32 代碼能夠輸出內容,但游戲本身并沒有輸出的能力,因為游戲并沒有顯示功能,甚至沒有相關的輸入輸出支持。因此,想要將統計數據輸出到 Win32 層,需要簡單地通過一些基本的方式實現。

為了實現這一點,可以做一些簡單的操作。考慮到許多性能計數器的實現有時過于復雜,這里決定采取一個非常簡單的辦法:創建一個靜態表來存儲這些計數器。實現過程非常直接,基本上只是創建一個簡單的結構來存儲周期計數。

在游戲的內存管理部分,新增一個名為 debug cycle counter 的計數器。這個計數器會是一個 uint64_t 類型,用來記錄周期數。在調試模式下,會有多個這樣的計數器。舉個例子,可以在游戲調試內存區域分配 256 個這樣的計數器。

這些計數器只是用來調試時使用的,在發布版本時可以去掉。為了避免在發布版本中仍然保留這些調試信息,可以將它們放到一個名為 game_internal 的區域,只有在調試模式下定義 game_internal 時,才會啟用這些計數器。這樣,就能在開發和發布版本之間靈活切換,確保發布版本沒有多余的調試信息。
在這里插入圖片描述

支持 Linux 和 Mac 等平臺的用戶

為了避免對其他平臺的支持產生問題,特別是處理器不支持 __rdtsc 指令的情況,可以通過宏來實現這一功能。首先,確定編譯器是否支持 __rdtsc,然后在符合條件的情況下,使用宏來處理代碼的開始和結束時間記錄。

具體做法是,創建一個宏,名為 BEGIN_TIMED_BLOCK,它接受一個 ID,用于標識該時間塊。宏的作用是記錄代碼塊執行的開始時間,然后通過 __rdtsc 獲取當前的周期數。同樣的,創建另一個宏 END_TIMED_BLOCK,它在代碼塊結束時再次調用 __rdtsc 獲取結束時間,然后計算兩次周期數的差值,這個差值即為代碼執行的周期數。

為了避免在同一作用域中多個時間塊可能造成的命名沖突,使用了 C++ 的拼接操作符,將 ID 添加到計數器名稱中。這樣可以確保每個計時器都有唯一的名稱,即使多個計時器位于同一函數內。

計時器的計數值會存儲在一個本地變量中,而實際的周期數則會存儲在相應的調試內存中。為了方便擴展,可以通過枚舉類型定義不同的計數器,如 DebugCycleCounter_Count 等。這樣可以使代碼更簡潔,同時也能避免硬編碼。

最終,計時器的使用方式變得非常簡單,只需要在代碼塊開始和結束時調用 BEGIN_TIMED_BLOCK(ID)END_TIMED_BLOCK(ID),并指定對應的 ID,即可自動記錄并計算執行該塊代碼所消耗的處理器周期數。
在這里插入圖片描述

編譯、清理并運行

在進行編譯之前,需要先解決一些錯誤。首先,調整類型定義,然后確保 time block 的值能夠正確累加到 CycleCount 中。還需要解引用該值以確保其正確更新。

修改完成后,理論上如果運行游戲,每次游戲代碼迭代時,周期計數器的值會逐步增加。接下來,考慮將代碼切換到發布模式進行編譯,了解為什么要這樣做,以便在最終版本中進行優化和性能驗證。
在這里插入圖片描述

FillGroundChunk:關閉地面塊

為了減少構建地面塊時的運行時間壓力,暫時關閉了填充地面塊的功能。在這一過程中,地面塊仍然在生成,但它們被填充為黃色,而不是實際內容。這么做可以確保在調試計時器功能時,地面塊的構建不會對測試結果產生影響。等到計時器部分正常工作后,再重新啟用地面塊的填充功能。
在這里插入圖片描述

查看計時器值

為了查看計時器的值,需要先確保在調用 game update and render 時,調試計數器中的所有計時器都被清零。可以使用一個類似 Windows Zero Memory 的方法來清除這些計時器。接著,創建一個新的函數來轉儲計時器信息,以便在每次更新和渲染后輸出這些計時器的值。

handleDebugCycleCounters 函數中,首先要確保只有在啟用了內部調試時,才執行相關代碼,避免在沒有調試計數器時出現編譯錯誤。然后,通過一個循環遍歷每個計數器,并使用 OutputDebugString 函數將每個計數器的值輸出。此輸出將包括計數器的索引及其相應的周期計數值。為了正確輸出 64 位整數,需要使用合適的格式符號。最終,這些輸出將幫助追蹤每個計時器的狀態并調試游戲性能。

這一過程為調試提供了基礎輸出,但將來可能會通過自定義調試服務進行更精細的處理。目前,這種方法雖然簡單,但足以滿足當前的需求。
在這里插入圖片描述

在這里插入圖片描述

運行游戲并查看調試循環計數

調試輸出現在可以顯示每幀游戲運行所消耗的周期數。從輸出中可以看到,當前每幀的周期數相對一致,但卻非常高。具體來說,當前游戲代碼本身就已經消耗了debug大約 31 億個周期,release 1.2億個周期 而不包括 Windows 操作系統的開銷。根據之前的計算,每個 CPU 核心每幀應當消耗的周期數應該低于 1.07 億,而現在的數字遠遠超出了這個預期。這表明,當前代碼的性能開銷較大,需要進一步優化。
debug模式 = 0: 3,289,537,500 三十多億個周期嗎
在這里插入圖片描述

release模式 = 0: 119925458
在這里插入圖片描述

確認我們知道的情況

從周期計數來看,可以確認目前的幀率遠低于期望值,游戲的性能無法達到理想的水平。周期計數顯示的數字遠遠超過了實際發布時所能接受的范圍,這證明了當前的代碼效率不高,無法支持足夠的幀率。因此,接下來的步驟是分析并改進代碼,優化性能以提高幀率。

RenderGroupToOutput:添加一個定時塊

為了更清楚地了解每個部分消耗的周期,可以通過在渲染和模擬等關鍵操作中添加計時器來跟蹤時間。例如,渲染的處理是通過 RenderGroupToOutput 函數完成的,可以在該函數內部插入一個時間塊,標記為 DebugCycleCounter_RenderGroupToOutput。通過這種方式,能夠記錄并顯示渲染過程中花費的周期,從而進一步分析各個部分的性能瓶頸。
在這里插入圖片描述

引入 DebugGlobalMemory 以便在不應訪問的情況下仍然能夠訪問這些計時信息

接下來,為了避免頻繁傳遞調試周期計數器,可以使用一個全局變量來存儲調試內存,從而讓所有地方都能訪問到它。這樣,雖然通常會避免使用全局變量,但在這種性能分析的情況下,它有助于簡化調試流程。這個全局變量 DebugGlobalMemory 會在內部模式下定義,并且只有在內部模式下編譯時才會有效,從而避免在發布版本中誤用。

此外,確保這些調試宏在其他平臺上完全被編譯掉,不會影響正常運行。通過在游戲更新和渲染過程中設置這個全局變量,可以保證每次訪問時都能獲得調試數據,而無需改變程序的架構。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

注意兩個循環計數之間的差異

通過查看調試周期計數器的輸出,可以清楚地看到游戲的整體性能情況。通過對比不同部分的周期數,可以得出以下結論:整個游戲的運行只消耗了大約 267286次周期,而渲染部分則消耗了剩余的大部分周期,約111686539多次。這一數據確認了之前的觀察結果,即游戲本身的計算工作量較少,而渲染過程則是性能瓶頸的主要來源。

盡管這還不是非常精確的分析,但已經能大致勾畫出性能瓶頸的位置,驗證了游戲中明顯的性能下降正是由于渲染部分的高周期消耗所導致的。接下來,計劃繼續深入分析并優化這一部分。

0: 111953825 - 1: 111686539 = 267286
在這里插入圖片描述

確認 DrawRectangleSlowly 是罪魁禍首

為了進一步確認性能瓶頸所在,決定對 DrawRectangleSlowly 這一函數進行檢查。雖然最初的假設是渲染部分可能導致性能問題,但在沒有確認之前,不能僅憑直覺做出優化,因為這可能會浪費時間在非關鍵部分。實際運行時,結果證明了這一假設,幾乎所有的時間都花費在了 DrawRectangleSlowly 函數上。

通過對比不同函數的周期消耗,發現大部分時間的消耗確實集中在渲染部分,尤其是在繪制小三角形的過程中。這也證明了渲染環節仍然是性能瓶頸的核心。雖然渲染過程中可能還有一些其他小的耗時操作,但問題的主要根源已經明確,為后續的優化提供了方向。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

引入 HitCount 以了解 DrawRectangleSlowly 是因為本身慢,還是因為調用次數太多

為了進一步確認 rawRectangleSlowly 是否真的因為本身效率低導致性能問題,還是因為其調用頻率過高,決定在現有的周期計數系統中加入執行次數的統計。通過為每個函數路徑增加一個“命中計數”,可以追蹤每個代碼塊被執行的次數。

這樣,除了更新周期計數(DTSC),還可以增加對命中計數的更新,記錄函數被調用的次數。為了展示這些數據,擴展了輸出,除了顯示周期計數外,還增加了每個函數調用的命中次數、周期數和每次調用的平均周期數(周期數除以命中次數)。這有助于確認是否是函數調用過于頻繁導致了性能瓶頸。對于命中計數為零的情況,避免除以零的錯誤,只在有命中次數時進行輸出,確保輸出的數據有效。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

發現我們調用渲染器 13 次,DrawRectangleSlowly 被調用了 202 次

通過加入命中計數,發現了一個之前未曾注意到的現象:渲染器被調用了13次,原因可能是因為正在刷新地面塊。原本認為渲染器只會調用一次,但實際情況并非如此。另外,rawRectangleSlowly 被調用了202次。通過這些數據可以看出,問題并不是矩形繪制過多,而是這些矩形本身足夠大且繪制速度較慢,導致每個矩形的繪制消耗了大量時間。

這些結果確認了之前的假設,但更重要的是,這個過程展示了如何通過層級化的調試方法來確認性能瓶頸所在。在不知道時間消耗具體位置的情況下,這種方法非常有效,能夠幫助快速定位性能問題。

計算我們正在填充的像素數

為了進一步分析性能,決定臨時添加一個計數器來統計在渲染過程中實際填充了多少像素。通過在rawRectangleSlowly函數中添加FillPixelTestPixel計數器,可以跟蹤每次循環中測試和填充的像素數量。從結果來看,大部分測試的像素確實被填充了,表明并沒有浪費太多時間在無效的像素上,這是一個積極的信號。

然而,值得注意的是,雖然FillPixelTestPixel的數量相似,但這也暴露出一個潛在的問題:如果沒有進行旋轉,理論上應該能精確計算出需要填充的像素。因此,若兩者沒有完全一致,可能表明邊界計算或邊緣函數的實現存在問題,這是一個明顯的警示信號,需要重新檢查邊界計算或修正邊緣函數。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

解讀數據

正在討論填充的像素數量。首先,需要確認實際填充了多少像素,因此檢查了這個數字。然后,查看了一個名為“scratch buffer”的內容,這顯示了已經測試過的像素數量。接著,考慮到當前分辨率,想知道屏幕上實際上有多少像素。分辨率為1204 x 800,計算結果為963200。這一過程被重復進行了確認。
在這里插入圖片描述

TestFill -> 1531114h
total -> 963200

注意我們操作的像素數并沒有比屏幕上的總像素數多多少

通過比較當前填充的像素數量與屏幕上總像素數,可以看出,實際上填充的像素并沒有超過總像素的太多,這意味著在過度繪制(overdraw)方面的表現還算不錯。過度繪制是指在渲染過程中,同一個像素被多次填充的情況,當前的情況表明并沒有出現過多的無效繪制。

黑板:過度繪制

過度繪制(overdraw)是指在渲染過程中,像素被多次觸及的情況。理想的渲染器應該只觸及每個像素一次,并且準確地給每個像素上色。理想情況下,渲染器的操作數應該等于屏幕上總像素的數量,這意味著每個像素僅被繪制一次。過度繪制衡量的是渲染器的效率,表示渲染器在渲染過程中需要重新繪制的像素次數。過度繪制的增加意味著渲染效率低下,因為每次覆蓋之前的像素都浪費了工作。因此,過度繪制與渲染效率密切相關。

目前的測試結果顯示,測試像素的數量與屏幕上的像素數量差距不大,這表明過度繪制的情況并不嚴重,渲染效率還算不錯。然而,當前屏幕上沒有太多內容,因此隨著內容的增加,過度繪制的數量可能會上升,需要通過創建一些測試場景來故意增加過度繪制,以便進一步優化這一指標。

黑板:進度報告

經過這些步驟,現在已經了解了一些關鍵的性能特點。首先,已經確定了性能瓶頸的具體位置,并且了解了一些關于該瓶頸的特征。目前,并沒有出現顯著的過度繪制問題,渲染效率還可以。然而,存在較大的速度問題。從周期數來看,每個像素的處理時間約為160個周期。通過將像素數量與所消耗的周期數相除,可以得出每個像素大約需要160個周期的處理時間。
在這里插入圖片描述

關閉 NormalMap

目前,每個像素的處理時間大約是160個周期,這為進一步評估性能提供了一些信息。接下來,通過不進行法線貼圖合成的實驗,可以進一步了解渲染速度的變化。在沒有法線貼圖的情況下,渲染速度的差距相對較小,處理周期也有所減少。這表明法線貼圖合成對當前性能的影響不小。
在這里插入圖片描述

理解大致的時間估算

目前的周期計數只是一個大概的估算,不能準確反映執行時間。即使游戲狀態沒有變化,渲染的內容也相同,周期數依然會波動。這是因為現代處理器復雜,存在內存訪問延遲、緩存命中、任務切換等因素,這些都會引入不確定性,導致每次周期計數不同。為了獲得更準確的周期計數,可以通過多次運行同一個操作,取最低周期數來減少這些變動,這樣可以排除外部干擾并盡量將操作保持在緩存中,從而更精確地測量最優周期數。然而,目前所看到的周期計數只能作為粗略的參考,不能視為絕對準確的數值。在查看分析結果時,必須理解這些數字的含義以及它們的準確性。

進行估算

為了估算每個像素所需的操作,首先需要分析渲染過程中的各種操作步驟。需要進行的操作包括變量重命名、減法、點積運算、取反、比較、屏幕空間坐標計算、乘法和采樣等。一些步驟(如旋轉、顏色空間轉換和分解)可能需要額外的乘法、加法、減法和位移操作,特別是在進行采樣時。對于內存查找部分,涉及的操作會更復雜,因此需要考慮到的指令總數大致為96條。

通過粗略估算,如果每個指令需要2個周期,那么每個像素可能需要200個周期。若能夠優化處理,使得每個周期可并行處理四個像素,那么每個像素的周期數將降到50個周期。假設通過優化將操作降到這種程度,填充當前屏幕的像素可能需要4200萬個周期。基于當前的測試像素數,如果能夠減少到100個周期以下,則可能達到預期的性能目標。

最終,優化的目標是盡量將每個像素的周期數降低到100周期以內,同時考慮到法線映射等額外開銷。這一過程需要在未來進一步細化和驗證,以確保渲染效率符合需求。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

AVX-512 熱議

如果使用像AVX2或即將推出的AVX-512處理器,就可以顯著提高效率。相比每次處理4個像素,新的處理器能夠每次處理16個像素,這將大大提高渲染速度,帶來顯著的性能提升。因此,考慮到這種硬件進展,可能會在未來優化渲染性能時發揮巨大作用。

為什么計數器上沒有文本標簽?

在計數器上沒有文本標簽的原因是,目前并不需要它們。雖然可以考慮以后加上,但目前并不覺得有必要。

對不起,如果這是你之前講過的內容,但能否解釋一下 C++ 中 new 和 malloc() 之間的區別,以及何時使用它們?

newmalloc 之間的區別在于是否需要使用 C++ 的特性。malloc 僅分配內存,而 new 不僅分配內存,還會調用對象的構造函數。因此,使用帶有構造函數的對象時,必須使用 new,除非打算手動調用構造函數。對于沒有構造函數的對象,new 并不會做任何額外的事情,malloc 就足夠了。類似地,對于帶有析構函數的對象,需要使用 delete 來釋放內存,而對于沒有析構函數的對象,可以直接使用 free 來釋放內存。

你是否可以通過做更多的工作來省略一些指令,例如 d - XAxis,接著 d - XAxis - YAxis。那應該只需要 2 條指令嗎?

通過對之前的計算結果進行復用來減少指令的數量是完全可行的。例如,可以通過先計算 D - XAxis,然后再計算 d - XAxis - YAxis 來節省一些計算。雖然編譯器已經開啟優化,可能會自動去除一些冗余的操作,但依然會盡量手動進行優化,避免多余的計算。

你認為我們會在軟件渲染器中使用多線程嗎?

在軟件渲染中,確實會考慮使用多線程。計劃將屏幕分成四個部分,并在不同的線程中分別渲染每一部分。這種方式能夠提高渲染效率,并利用多核處理器的優勢。

是否可以每次操作都做四倍優化?

處理器如何發布指令以及如何管理不同指令的執行。處理器的工作方式是,它只能在有足夠空閑單元的情況下發布指令。可以將處理器看作由多個較小的單元組成,每個單元負責不同的任務,例如加法或位移操作。如果兩個指令是獨立的,可以在同一周期內分別發給不同的單元執行。如果兩個指令相同且依賴于相同的資源(如同為位移指令),則無法在同一周期執行。

優化指令發布的過程涉及深入了解處理器的結構,例如每個單元的數量和處理器是否能夠在一個周期內同時執行多個指令。這個過程是復雜的,需要了解處理器的每個細節,并考慮它的亂序執行窗口。對于x64處理器來說,由于其復雜性和多樣性,這類優化特別困難。盡管如此,在某些情況下,確實有必要深入了解處理器,以實現最大化的性能優化。

我的意思是,把它放入寬指令(SIMD)中

關于是否可以在指令中進行四倍(quad pump)操作的問題,回答是否定的。雖然浮點和整數運算通常可以進行四倍泵,但內存訪問操作(例如紋理獲取和計算需要加載的紋素位置)是無法進行的。這是因為SSE2、AVX及其之前的指令集在內存訪問方面有局限,無法處理寬操作(wide operations)。雖然AVX-512可能解決了這個問題,因為Larrabee指令集就包含了這些功能,并且AVX-512已經將這些功能整合到主流指令集中了,但在目前的指令集下,內存訪問仍然是一個瓶頸。

“Quad pump” 這個術語通常指的是在一個時鐘周期內并行執行四個操作或指令的能力。它是在處理器架構中,特別是與SIMD(單指令多數據)指令集相關的一個術語,比如AVX(高級向量擴展)系列。

具體來說,“quad pump” 的意思是在一次時鐘周期內,通過使用寬度較大的指令集(例如 AVX-512),能夠并行處理更多的數據,通常是每個時鐘周期處理四倍于常規操作的數據量。

例如,在一個支持 AVX 的處理器上,如果能夠處理四個浮點數或者四個整數數據項,就可以稱其為“quad pumping”。這使得處理器在執行某些任務時,能夠更高效地同時處理多個數據項,顯著提高性能,尤其在圖形處理、科學計算等需要大量并行運算的領域。

linux 下面perf

在Linux中執行CPU profiling通常可以使用多種工具,以下是幾種常見的方法:

performance_test.cpp

#include <iostream>
#include <vector>
#include <cmath>
#include <chrono>// 模擬一個計算密集型函數
void compute_heavy_task(int size) {std::vector<double> vec(size, 0.0);for (int i = 0; i < size; ++i) {for (int j = 0; j < size; ++j) {vec[i] += sqrt(i * j);}}
}int main() {int size = 1000;  // 調整為較大的數值,增加計算密集性// 記錄開始時間auto start = std::chrono::high_resolution_clock::now();// 執行計算密集型任務compute_heavy_task(size);// 記錄結束時間并計算花費的時間auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> duration = end - start;std::cout << "Task completed in " << duration.count() << " seconds." << std::endl;return 0;
}

1. 使用 perf 工具

perf 是一個強大的性能分析工具,可以用于收集CPU性能數據,檢查程序瓶頸。

安裝 perf(如果尚未安裝):

sudo apt-get install linux-tools-common linux-tools-generic

在這里插入圖片描述

基本使用:

  1. 首先,編譯程序并啟用調試信息:
g++ -g -o performance_test performance_test.cpp

在這里插入圖片描述

  1. 然后,使用 perf 來記錄程序性能:
perf record ./performance_test

在這里插入圖片描述

  1. 程序運行完后,會生成一個 perf.data 文件。可以使用以下命令查看性能報告:
perf report

在這里插入圖片描述

采樣特定的事件:
你可以通過 perf 來捕獲特定的事件,比如 CPU cycles 或緩存命中等:

perf record -e cycles -p <pid>

這會捕獲指定進程(PID)的 CPU 周期事件。

rm 刪掉除.cpp 的其他文件

要刪除當前目錄下除了 .cpp 文件之外的其他文件,可以使用 find 命令結合 rm 來實現。以下是一個示例:

find . -type f ! -name "*.cpp" -exec rm -f {} \;

在這里插入圖片描述

解釋:

  • find .:從當前目錄開始查找。
  • -type f:只查找文件。
  • ! -name "*.cpp":排除 .cpp 文件,只刪除不以 .cpp 結尾的文件。
  • -exec rm -f {} \;:對找到的每個文件執行 rm -f 命令,刪除文件。

注意:

  • 確保你在正確的目錄中執行命令,避免刪除了不該刪除的文件。
  • 如果不確定,可以先使用 find 命令列出將被刪除的文件,例如:
    find . -type f ! -name "*.cpp"
    
    這將列出所有不以 .cpp 結尾的文件,你可以確認后再執行刪除操作。
    在這里插入圖片描述

2. 使用 gprof 工具

gprof 是一個較為傳統的性能分析工具,可以幫助分析程序的執行性能。

使用步驟:

  1. 編譯程序 時,確保加上 -pg 選項來啟用性能分析:

    g++ -pg -o performance_test performance_test.cpp
    
  2. 執行程序,生成性能數據:

    ./performance_test
    

在這里插入圖片描述

  1. 生成 gmon.out 文件后,使用 gprof 查看性能報告:

    gprof ./performance_test gmon.out > analysis.txt
    

    在這里插入圖片描述

    在這里插入圖片描述

3. 使用 valgrind(callgrind 模式)

valgrind 是一個用于內存調試的工具,但其 callgrind 模式可以用于進行性能分析,尤其適用于 CPU 性能分析。

安裝 valgrind

sudo apt-get install valgrind

在這里插入圖片描述

使用 callgrind

valgrind --tool=callgrind ./performance_test

在這里插入圖片描述

在這里插入圖片描述

這會生成一個包含函數調用次數、調用圖等信息的文件,可以使用 kcachegrindqcachegrind 來可視化分析。
在這里插入圖片描述

修改WSL 界面的字體大小

1. 安裝字體

sudo apt update
sudo apt install fontconfig

在這里插入圖片描述

2. 安裝 x11-xserver-utils

xrdb 工具通常包含在 x11-xserver-utils 包中。在 WSL 中執行以下命令來安裝它:

sudo apt update
sudo apt install x11-xserver-utils

在這里插入圖片描述

安裝完成后,您應該能夠使用 xrdb 命令。

3. 接下來,創建一個名為.Xresources的文件并在其中添加以下內容:

Xft.dpi: 220

這將把Xft的dpi設置為220,從而放大字體大小。
在這里插入圖片描述

3. 再次嘗試執行 xrdb

安裝完 x11-xserver-utils 后,您可以重新嘗試運行 xrdb

xrdb -merge ~/.Xresources

在這里插入圖片描述

如果沒有錯誤提示,并且沒有顯示任何輸出,說明 X 資源文件已經成功合并。
在這里插入圖片描述

在這里插入圖片描述

總結

  • 如果 xrdb 找不到,安裝 x11-xserver-utils 包。
  • 確保配置的 ~/.Xresources 文件格式正確。
  • 使用 xrdb -merge ~/.Xresources 來合并配置文件。

valgrind doc: https://valgrind.org/docs/manual/index.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70464.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70464.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70464.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

通義靈碼AI程序員

通義靈碼是阿里云與通義實驗室聯合打造的智能編碼輔助工具&#xff0c;基于通義大模型技術&#xff0c;為開發者提供多種編程輔助功能。它支持多種編程語言&#xff0c;包括 Java、Python、Go、TypeScript、JavaScript、C/C、PHP、C#、Ruby 等 200 多種編碼語言。 通義靈碼 AI…

SeaTunnel社區「Demo方舟計劃」首期活動上線—— MySQL CDC實時同步至PostgreSQL實戰

引言 凌晨2點&#xff0c;某電商公司的數據工程師小李正對著屏幕抓狂——業務部門臨時要求將MySQL的訂單表實時同步到PostgreSQL進行分析&#xff0c;眾所周知&#xff0c;在數據驅動的業務場景中&#xff0c;異構數據源同步是高頻剛需。 以MySQL到PostgreSQL的CDC同步為例&a…

iNeuOS工業互聯網操作系統,民爆遠程運維平臺案例

iNeuOS工業互聯網操作系統,民爆遠程運維平臺案例 目 錄 1. 概述... 2 2. iNeuOS在民爆生產廠區和北京運維中心配置... 3 1.1 生產廠區配置... 3 1.2 運維中心配置... 7 1. 概述 針對本項目進行初步調研,項目的總體需求為滿足新建…

利用websocket檢測網絡連接穩定性

瀏覽器中打開F12&#xff0c;控制臺中輸入以下內容 > 回車 > 等待結果 連接關閉 表示斷網 let reconnectDelay 1000; // 初始重連間隔 let pingInterval null; let socketManuallyClosed false; // 標志是否手動關閉function createWebSocket() {if (socketManuallyCl…

Unity shader glsl著色器特效之 模擬海面海浪效果

一個簡單的海浪效果&#xff0c;通過波的疊加實現水面起伏的動效&#xff0c;根據波峰斜率來為浪花著色&#xff0c;再根據法線貼圖和水花貼圖來和調整uv的平滑移動來增強海浪移動的細節。如果需要更逼真的效果可以考慮在滿足浪花觸發的地方添加粒子系統 前置效果圖 因為是很久…

智能經濟與個人智能助理有什么發展

智能經濟與個人智能助理有什么發展 技術融合創新 研究個人助理與新興技術&#xff08;如量子計算、邊緣計算&#xff09;融合&#xff0c;分析對智能經濟的推動作用。探索量子計算提升數據處理速度&#xff0c;邊緣計算降低延遲&#xff0c;提升個人助理性能的機制&#xff0…

spring日志

前言 入門 這些就是日志 現在開始使用一下 spring是集合了日志的 注意選這個 這樣我們就創建好了一個日志對象了 我們就可以這樣打印日志了 日志和普通的打印消息相比&#xff0c;區別就是多個一些時間之類的消息 從左到右分別是時間&#xff0c;級別&#xff0c;PID&#x…

整合Salesmart/WhatsApp、開源Odoo模塊和Deepseek AI能力,實現針對國外客戶的智能客服和個性化推薦服務

一、項目背景 本文提出了一套針對軟管制造公司的智能客服與個性化推薦系統實施方案&#xff0c;旨在通過整合開源Odoo模塊、Salesmart/WhatsApp以及Deepseek AI能力&#xff0c;打造一個724小時不間斷服務的智能化平臺&#xff0c;專注于服務國外客戶。方案圍繞實現不間斷服務…

Java中JDK、JRE,JVM之間的關系

Java中的JDK、JRE和JVM是三個核心概念&#xff0c;其關系可概括為JDK > JRE > JVM&#xff0c;具體如下&#xff1a; 一、定義與作用 JDK&#xff08;Java Development Kit&#xff09; 定義&#xff1a;Java開發工具包&#xff0c;用于開發和編譯Java程序。包含內容&…

用C++ Qt實現安卓電池充電動效 | 打造工業級電量控件

一、為什么需要自定義電池控件&#xff1f; 在工業控制、車機系統、智能硬件等領域的UI開發中&#xff0c;電池狀態顯示是高頻出現的UI組件。通過實現一個支持顏色漸變、動態充電動畫、警戒閾值提示的電池控件&#xff0c;開發者可以系統掌握以下核心能力&#xff1a; Qt繪圖…

Django+Vue3全棧開發實戰:從零搭建博客系統

文章目錄 1. 開發環境準備2. 創建Django項目與配置3. 設計數據模型與API4. 使用DRF創建RESTful API5. 創建Vue3項目與配置6. 前端頁面開發與組件設計7. 前后端交互與Axios集成8. 項目優化與調試9. 部署上線10. 總結與擴展10.1 項目總結10.1.1 技術棧回顧10.1.2 項目亮點 10.2 擴…

Django 5實用指南(五)模板系統

Django5的模板系統是其核心功能之一&#xff0c;允許開發者將動態數據嵌入到HTML模板中&#xff0c;并根據不同的業務需求渲染頁面。Django模板系統基于 Django模板語言&#xff08;DTL&#xff09;&#xff0c;它提供了一些強大的功能&#xff0c;如模板標簽、過濾器、條件語句…

uni-app開發app時 使用uni.chooseLocation遇到的問題

問題一&#xff1a;不顯示 問題二&#xff1a;選擇地址列表一直在加載中 因為 uni-app 接口文檔 中已經說明&#xff0c;使用騰訊的話需要開啟云服務&#xff0c;具體可看官網&#xff0c;這就是為什么使用時直接不顯示的原因&#xff0c;所以我使用的高德&#xff0c;但又出現…

推薦系統-排序模型

本次學習的重點是FM系列和WideNDeep系列。其實這兩個模型是存在因果關系的。從最初的LR模型開始&#xff0c;因為缺失高效的特征交互方式&#xff0c;產生了FM模型&#xff0c;即通過向量內積代替特征之間的兩兩交互的參數。最后DNN的引入可以建模更高階的特征。但是DNN如何與F…

體驗用ai做了個python小游戲

體驗用ai做了個python小游戲 寫在前面使用的工具2.增加功能1.要求增加視頻作為背景。2.我讓增加了一個歡迎頁面。3.我發現中文顯示有問題。4.我提出了背景修改意見&#xff0c;歡迎頁面和結束頁面背景是視頻&#xff0c;游戲頁面背景是靜態圖片。5.提出增加更多游戲元素。 總結…

c#爬取數據并解析json

安裝 Newtonsoft.Json Install-Package Newtonsoft.Json代碼 HttpClient client new HttpClient();// 獲取網頁內容HttpResponseMessage response client.GetAsync("https://opentdb.com/api.php?amount10&category18&difficultyeasy&typemultiple"…

計算機畢業設計Python農產品推薦系統 農產品爬蟲 農產品可視化 農產品大數據(源碼+LW文檔+PPT+講解)

溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 作者簡介&#xff1a;Java領…

【分布式理論13】分布式存儲:數據存儲難題與解決之道

文章目錄 一、數據存儲面臨的問題二、RAID磁盤陣列的解決方案1. RAID概述2. RAID使用的技術3. RAID的代表性等級 三、分布式存儲的新思路1. 分布式存儲背景與特點2. 分布式存儲的組成要素 一、數據存儲面臨的問題 在單機系統時代&#xff0c;當數據量不斷增加、硬盤空間不夠時…

高德地圖android sdk(備忘)

依賴 // 權限請求框架&#xff1a;https://github.com/getActivity/XXPermissions implementation com.github.getActivity:XXPermissions:20.0 // https://mvnrepository.com/artifact/com.amap.api/navi-3dmap-location-search implementation com.amap.api:navi-3dmap-loca…

DeepSeek、微信、硅基流動、納米搜索、秘塔搜索……十種不同方法實現DeepSeek使用自由

為了讓大家實現 DeepSeek 使用自由&#xff0c;今天分享 10 個暢用 DeepSeek 的平臺。 一、官方滿血版&#xff1a;DeepSeek官網與APP 首推&#xff0c;肯定是 DeepSeek 的官網和 APP&#xff0c;可以使用滿血版 R1 和 V3 模型&#xff0c;以及聯網功能。 網址&#xff1a; htt…