說出來不確定大家信不信,實現(xiàn)起來也就70來行算上大括號的代碼,是不是很激動人心?
言歸正傳,再小的程序,也是數(shù)據(jù)結(jié)構(gòu)+代碼。咱們先來由表及里地看看核心數(shù)據(jù)結(jié)構(gòu)的樣子。
首先,既然要從Cortex-M核在響應(yīng)中斷時自動入棧的信息采集PC,就必須了解自動入棧了些啥東東:
這里可以看出Cortex-M內(nèi)核自動壓入了8個寄存器,右二那個不起眼的pc,正是一號主角。對自動入棧不太了解的小伙伴,可以查看《Cortex-M3權(quán)威指南》第9章的介紹
(https://github.com/RockySong/cm3_def_guide_cn)
理論上pc可以是任何指令位置。不幸的是一般工程生成的指令數(shù)常常在幾萬甚至幾十萬條,難道都要記錄下來?估計天價的開發(fā)工具也不會這么做。常言說“首惡必辦,協(xié)從不問”,咱們做profiling,也沒必要統(tǒng)計出PC在所有指令上的分布密度,只要抓幾個大頭就夠了。還有個麻煩的,是一個函數(shù)可以有多個指令,函數(shù)長度可以相差巨大,而且在一個大函數(shù)里不同區(qū)域的覆蓋密度也不同。過日子還需要精打細(xì)算呀,咱們權(quán)衡打擊精度與彈藥消耗量,使用2個宏來決定配置,比如:
第1個宏P(guān)ROF_CNT決定了抓多少個大頭,第2個宏P(guān)ROF_ERR決定了網(wǎng)眼的大小——抓取的地址范圍(也就是最大誤差),在這個范圍內(nèi)的地址都計作同一個地址塊。顯然,PROF_CNT越多,PROF_ERR越小,抓取的就越多越精確,也就更接近高檔的分析工具。值得一表的是,如果PROF_ERR夠小,可以在較大的函數(shù)中抓出更消耗性能的位置。
第3個宏P(guān)ROF_MASK又是什么鬼?這其實是個工具宏,用來把地址向下對齊到誤差范圍的邊界,這也意味著PROF_ERR必須是2的整數(shù)次冪,這么做是避免消耗性能的取模運算。
下面請出關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):性能分析的PC統(tǒng)計單元:
很顯然,PROF_CNT是多少,就應(yīng)該有多少個ProfUnit_t實例。結(jié)構(gòu)中,hitCnt是關(guān)鍵的參數(shù),它統(tǒng)計了這個對齊后的PC地址”baseAddr”被采集到了多少次,”hitRatio”則是一個對人類友好的輔助變量,提供千分?jǐn)?shù)(其實是1024級)精度的CPU占用率。
此外,還有個非常有用的小細(xì)節(jié)。比如,小伙伴們可能也注意到了,CPU占用率也是有時效性的。就像一個漫長的初始化可能讓一些查詢等待的函數(shù)紅極一時,但在之前越是弄得滿城風(fēng)雨,程序主體運行后往往越是無聲無息,甚至都沒機(jī)會再運行一遍。
而即使在正常運行期間,不同時段開啟的功能不同,常常出現(xiàn)“皇帝輪流做,明年到我家”。因此,咱們可以加一點衰減處理,也就是定期對于非0的hitCnt進(jìn)行扣除一格,如果沒有后續(xù)源源不斷的再次命中,就會漸漸走下神壇直至跌出排行榜。這樣可以提高統(tǒng)計結(jié)果的實時性。衰減機(jī)制的思路也很簡單,就是輪流從hitCnt非0的各個PC樣本點去扣。
綜合上面的如意算盤,定義了如下統(tǒng)領(lǐng)全局的結(jié)構(gòu)體:
這個結(jié)構(gòu)里decayNdx表示下次統(tǒng)計時從誰身上扣除hitCnt,每一次扣除后就輪轉(zhuǎn)到后面的item上,以公平公正。profCnt則表示已經(jīng)做了多少次profiling統(tǒng)計,用于計算命中率,而items則是上文介紹的PC樣本統(tǒng)計單元。這里也有個小細(xì)節(jié),就是在應(yīng)用衰減來扣除每個item的hitCnt時,profCnt也需要扣除。
好了,有了完整的數(shù)據(jù)結(jié)構(gòu),該寫代碼了。從易到難,咱們可以先處理命中時的動作。
代碼很簡單,記錄地址,增加hitCnt,計算hitRate,再實時地“冒泡”,把最多hitCnt的item頂上去,排序的目的也是為了便于突出重點,對人類查看友好。這里每次hitCnt加2,是為了讓衰減得沒有增加的快,“過氣”得緩慢點,小伙伴們可以根據(jù)需要調(diào)節(jié)增加量。
再剩下的就是最復(fù)雜的主函數(shù)了——說是復(fù)雜也就不到40行的代碼。要在主函數(shù)里先應(yīng)用衰減,然后檢查這次的PC樣本是否已有記錄。如有記錄就調(diào)用上面的_ProfOnHit(),如無記錄則在一個hitCnt為0的item上記錄這個新PC樣本,也是調(diào)用_ProfOnHit()。此外,為了避免把idle函數(shù)和一些不想關(guān)心的函數(shù)也記錄下來,程序還支持一個“忽略列表”,凡是位于忽略列表地址范圍的PC樣本都不理會。
大功告成!接下來就是要使用了。使用非常簡單,只需在定時器中斷服務(wù)程序的主體中調(diào)用Profiling()并告訴它進(jìn)入定時器中斷時pc寄存器的值。為了獲取入棧的PC,這個需要一點Cortex-M的基礎(chǔ)知識和手寫匯編。下面給出KEIL下的匯編入口:
這個小程序先查出中斷前使用的棧指針并以作為參數(shù)傳遞給C語言主體“SysTick_C_Handler”。如果小伙伴們對這段匯編看不明白,就直接用就可以。
C語言主體的使用方式如下:
在使用的時候,咱們就進(jìn)入開發(fā)工具的調(diào)試會話,讓程序跑一會,再停下來。如果是在KEIL或IAR中,可以使用memory窗口或watch窗口觀察s_prof.items。如果使用了GDB,可以輸入命令p/a s_prof.items。查看排名靠前的item,對照map文件即可估計出函數(shù)的名字和大致位置。值得一表的是,GDB下會自動解析出地址所對應(yīng)的函數(shù)名,不用再讓咱們手動查map,非常貼心!
回顧理論篇介紹的幾個小坑,當(dāng)查到一個不合理的地址時,先別激動,看看是不是小坑中的之一。如果確定不是,就有必要深入處理了。
到了這里,這期性能分析的話題的理論和實踐的故事就講完了。
等等,似乎還有什么沒交待完。試想,當(dāng)我們一一找出最耗CPU資源的函數(shù)后,倘若束手無策,那也是徒勞無功,我們必須有對付他們的辦法。其中一項省力而又見效快的辦法就是把它們放在執(zhí)行性能更高的位置中去,也就是前面說的VIP區(qū)。下次,咱們就介紹一下各種VIP區(qū)的特點,以及升V的方法!敬請繼續(xù)關(guān)注!
-
mcu
+關(guān)注
關(guān)注
146文章
16900瀏覽量
349944 -
分析器
+關(guān)注
關(guān)注
0文章
92瀏覽量
12479 -
數(shù)據(jù)結(jié)構(gòu)
+關(guān)注
關(guān)注
3文章
569瀏覽量
40076
原文標(biāo)題:70行代碼來打造MCU性能分析利器!
文章出處:【微信號:mcuworld,微信公眾號:嵌入式資訊精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論