精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

perf_counter在測量系統的性能中有何作用?

嵌入式程序員 ? 來源:裸機思維 ? 作者:裸機思維 ? 2021-06-11 12:54 ? 次閱讀

不知不覺中,perf_counter已經經歷了大大小小7個版本:

提高了delay_us()的精度

增加了對GCC、IAR的支持

改進了__cycleof__()宏,使其支持嵌套、并不再強制綁定 printf()

如果你使用的是Arm Compiler5(armcc)或是Arm Compiler 6(armclang),移植就特別簡單。你可以按照這篇文章的手把手教程在5分鐘內完成部署。

對于GCC和IAR來說,由于它們都不支持 Arm Compiler5/6 所特有的Linker語法——$Sub$$和$Super$$,因此無法直接通過 Lib 的方式實現對已有SysTick應用的 “寄居”——這里就只能忍痛割愛了。 這并不影響我們以源代碼的形式將它們加入已有的 GCC 或是 IAR 工程。大體步驟如下: 第一步:將perf_counter.c和perf_counter.h拷貝到你的工程目錄下,并將perf_counter.c 加入到編譯列表中; 第二步:將perf_counter.h所在的路徑加入到編譯器的頭文件搜索路徑中;

第三步:perf_counter.c依賴CMSIS5.4.0及其以上版本,確保你的工程中正確的包含了對CMSIS的支持。(這里就不再贅述)。

第四步:在需要用到perf_counter功能的C源文件中加入對頭文件的包含:

#include "perf_counter.h"

第五步:一般來說,用戶會在某一個地方,比如main()函數內完成對CPU工作頻率的配置,我們應該在完成這一工作之后確保全局變量SystemCoreClock被正確的更新——保存當前CPU的工作頻率,比如:

externuint32_tSystemCoreClock;voidmain(void){system_clock_update();//!更新CPU工作頻率SystemCoreClock=72000000ul//!假設更新后的系統頻率是 72MHz...}

一般來說,你的芯片工程如果本身都是基于較新的CMSIS框架而創建的,你的啟動文件中已經為你定義好了全局變量SystemCoreClock——當然,凡事都有例外,如果你在編譯的時候報告找不到變量SystemCoreClock或者說“Undefined symbol __SystemCoreClock” 之類的,你自己定義一下就好了,比如:

uint32_tSystemCoreClock;voidmain(void){system_clock_update();//!更新CPU工作頻率SystemCoreClock=72000000ul//!假設更新后的系統頻率是 72MHz...}

在這以后,我們需要對perf_counter庫進行初始化。這里分兩種情況:

1、用戶自己的應用里完全沒有使用SysTick。對于這種情況,我們要在 main.c (或者別的什么源文件里)添加一個SysTick中斷處理程序:

#include "perf_counter.h"... __attribute__((used))//!

然后我們在main()函數里初始化perf_counter服務:

#include... voidmain(void){system_clock_update(); //!更新CPU工作頻率SystemCoreClock=72000000ul//!假設更新后的系統頻率是 72MHzinit_cycle_counter(false);...}

需要特別注意的是:由于用戶并沒有自己初始化SysTick,因此我們需要將這一情況告知perf_counter庫——由它來完成對SysTick的初始化——這里傳遞false給函數init_cycle_counter()就是這個功能。如果由perf_counter 庫自己來初始化SysTick,它會為了自己功能更可靠將 SysTick的溢出值(LOAD寄存器)設置為最大值(0x00FFFFFF)。

2、用戶自己的應用里使用了SysTick,擁有自己的初始化過程。對于這種情況,我們需要確保一件事情:即,SysTick的CTRL寄存器的 BIT2(SysTick_CTRL_CLKSOURCE_Msk)是否被置位了——如果其值是1,說明SysTick使用了跟CPU一樣的工作頻率,那么SysTick的測量結果就是CPU的周期數;如果其值是0,說明SysTick使用了來自于別處的時鐘源,這個時鐘源具體頻率是多少就只能看芯片手冊了(比如STM32就喜歡將系統頻率做 1/8 分頻后提供給SysTick作為時鐘源),此時SysTick測量出來的結果就不是CPU的周期數。

在確保了CTRL寄存器的BIT2被正確置位,并且SysTick中斷被使能(置位BIT1,SysTick_CTRL_TICKINT_Msk)后,我們可以簡單的通過init_cycle_counter()函數告訴perf_counter模塊:SysTick被用戶占用了——這里傳遞 true 就實現這一功能。

#include ... void main(void){ system_clock_update(); //! 更新CPU工作頻率 SystemCoreClock = 72000000ul //! 假設更新后的系統頻率是 72MHz init_cycle_counter(true); ...}

當然,不要忘記向已經存在的SysTick_Handler()內加入perf_counter()的插入函數:

#include "perf_counter.h"... __attribute__((used))//!

至此,我們就完成了perf_counter模塊在GCC和IAR中的部署。

如何測量代碼片斷占用了多少CPU資源?

很多時候,我們會關心某一段代碼或者函數究竟用了多少CPU周期,比如,我們寫了一個算法,你很擔心“這個算法究竟使用了多少CPU資源”,為了解決這個問題,我們需要用到如下的公式: CPU資源占用(百分比) = (函數運行所需的時間)(算法運行間隔的最小值) 100% 對于【函數運行所需的時間】和【算法運行間隔的最小值】來說,雖然它們都是時間單位,但考慮到CPU的頻率是給定的(不變的),因此,這里的時間單位在乘以CPU的工作頻率后都可以被換算為CPU的周期數。舉例來說,假如【算法運行間隔的最小值】是 20ms、CPU的頻率是72MHz,那么對應的周期數就是 72000000* (20ms / 1000ms) = 1440000 個周期。看來上述公式中唯一需要我們實際測量的就是【函數運行所需的周期數】了。

perf_counter提供了一個非常簡單的運算符:__cycleof__()。假設我們要測量的代碼片斷如下:

...my_algorithm_step_a();my_algorithm_step_b();...my_algorithm_step_c();...則我們可以輕松的通過__cycleof__()運算來測量結果:

...__cycleof__("myalgorithm"){ my_algorithm_step_a(); my_algorithm_step_b(); ... my_algorithm_step_c();}...如果你的系統支持printf(),則可以看到類似如下的輸出結果:

poYBAGDC7bWAAtn6AABBJkEIg2I291.jpg

帶入上述公式:525139 /14400000 * 100% ≈36.5% 就計算出這個算法占用了大約36.5%的CPU資源,值得說明的是,從原理上看,這一方式對裸機和RTOS同樣有效哦。

有的小伙伴很快會說,我的系統并不允許我調用printf,那我還可以使用__cycleof__()么?當然了!就繼續以上述代碼為例子:

int32_tnCycleUsed=0; ...__cycleof__("my algorithm", { nCycleUsed = _; }) { my_algorithm_step_a(); my_algorithm_step_b(); ... my_algorithm_step_c();}...這里的代碼所實現的功能是:

測量了用戶函數my_algorithm_step_xxx()所使用的周期數:

測量的結果被轉存到了一個叫做nCycleUsed的變量中;

__cycleof__()將不會調用printf()進行任何內容輸出。

我相信很多小伙伴會揉了揉眼睛、仔細看了又看,然后回過頭來滿頭問號:

這是C語言? 這是什么語法? 不要懷疑,這就是C語言,只不過使用了一點GCC的語法擴展(感興趣的小伙伴可以復制這里的連接https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs),考慮到本文只介紹perf_counter如何使用,而對其如何實現的并不關心,我們不妨略過GCC擴展語法的部分,專門來看看上述代碼的使用細節:

首先,為了方便大家觀察,我們先忽略圓括號內的部分:

...__cycleof__(...){ my_algorithm_step_a(); my_algorithm_step_b(); ... my_algorithm_step_c();}...可以發現,這里跟此前并沒有什么不同:花括號包圍的部分就是我們要測量的代碼片斷;

接下來,我們專門來看__cycleof__()圓括號中的部分:

int32_tnCycleUsed=0; ...__cycleof__("my algorithm", { nCycleUsed = _;}){...}...容易發現,如果以“,” 為分隔符,那么實際傳遞給__cycleof__()的是兩個部分: 1、標注測量名稱的字符串

"my algorithm"2、一段用花括號括起來的代碼片斷:

{nCycleUsed = _;}其中,nCycleUsed是一個事先已經初始化好的變量。 這里,對于表示測量名稱的字符串"my algorithm",在這一用法下在最終的編譯結果里并不會占用任何RAM或者是ROM,但作為語法結構是必須的。 對于花括號所囊括的代碼片段來說,實際上在這個花括號里,你幾乎可以為所欲為:

你可以寫任意數量的代碼

你可以調用函數

你可以定義變量(當然這里定義變量肯定就是局部變量了)

但我們一般要做的事情其實是通過__cycleof__()所定義的一個局部變量"_"來獲取測量結果——這也是下面代碼的本意:

nCycleUsed = _;需要說明的是,這個局部變量"_"生命周期僅限于這個花括號中,因此不會影響 __cycleof__() 整個結構之外的部分——或者說,下述代碼是沒有意義的:

int32_t nCycleUsed = 0; ...__cycleof__("my algorithm", { nCycleUsed = _; }) { my_algorithm_step_a(); my_algorithm_step_b(); ... my_algorithm_step_c();} printf("Cycle Used %d", _);編譯器會毫不客氣的告訴你 "_" 是一個未定義的變量,反之如果你這么做:

int32_t nCycleUsed = 0; ...__cycleof__("my algorithm", { nCycleUsed = _; printf("Cycle Used %d", _); }) { my_algorithm_step_a(); my_algorithm_step_b(); ... my_algorithm_step_c();}

則會看到你心怡的輸出結果:

pYYBAGDC7b2AR4b3AAAY48Xf9OE887.jpg

沒有什么黑魔法

如果你對上述例子的等效形式(展開形式)感到非常好奇,其實大可不必,上述代碼在“邏輯上等效”于如下的形式:

int32_t nCycleUsed = 0; ...do {int64_t_=get_system_ticks(); { my_algorithm_step_a(); my_algorithm_step_b(); ... my_algorithm_step_c(); } _ = get_system_ticks() - _; //!我們添加的代碼 nCycleUsed = _; printf("Cycle Used %d", _);}while(0);是不是突然就沒有那么神秘了?通過“邏輯等效”的形式展開,我們很容易發現一些有趣的內容:

起核心作用的是一個叫做get_system_ticks()的函數。實際上它返回的是從復位后 SysTick被使能至今所經歷的 CPU 周期數——由于它是int64_t 的類型,因此不用擔心超過 SysTick 24位計數器的量程,也不用擔心人類歷史范圍內會發生溢出的可能。知道這一點后,聰明的小伙伴就可以自己整活兒了。

由于 "_"是一個局部變量,因此可以判斷__cycleof__() 是支持嵌套的。

需要特別說明的是,get_system_tick()函數自己也是有CPU時鐘開銷的,所以如果要獲得較為精確的結果,推薦通過下面的方法來獲取校準值:

staticint64_ts_lPerfCalib; voidcalib_perf_counter(void){ int64_t lTemp = get_system_tick(); s_lPerfCalib = get_system_tick() - lTemp;} int64_tget_perf_counter_calib(void){return s_lPerfCalib;}具體如何使用,這里就不再贅述了。 說在后面的話。

perf_counter仍然在不停的演化中,這多虧了開源社區不斷的使用和反饋。perf_counter的應用場景實際上非常廣泛,包括但不限于:

為裸機或者RTOS提供Cycle級別的性能測量;

評估代碼片段的CPU占用;

算法精細優化時用于測量和觀察優化的效果;

測量中斷的響應時間;

測量中斷的發生間隔(查找最短時間間隔);

評估GUI的幀率或者刷新率;

與SystemCoreClock計算后,獲得一個系統時間戳(Timestamp);

當做Realtime Clock的基準;

作為隨機數種子

……

實際上perf_counter在我參與的另外一個開源項目arm-2d里也被悄悄的藏在了platform_utilities.lib中用來為例子代碼提供幀率的測量服務。

責任編輯:lq6

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 測量系統
    +關注

    關注

    2

    文章

    533

    瀏覽量

    41353
  • Counter
    +關注

    關注

    0

    文章

    24

    瀏覽量

    17980

原文標題:如何“優雅”的測量系統性能

文章出處:【微信號:InterruptISR,微信公眾號:嵌入式程序員】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    【RA-Eco-RA2E1-48PIN-V1.0開發板試用】原創測量代碼運行時間

    重要了,有時候為了達到高性能的執行效率,甚至會在軟件上通過查表等方法通過空間來換取時間用以優化算法。 嵌入式開發當中,測量算法的執行時間是非常重要的,為了完成這個指標,我特地試用
    發表于 11-06 15:32

    晶振電路中有什么作用

    晶振電路是一種常見的電子元件,其作用是產生穩定的時鐘信號。許多電子設備中,晶振電路被廣泛應用,以確保設備的正常運行。
    的頭像 發表于 08-06 17:10 ?331次閱讀

    請問IC1(OPA140或OPA227)電路中有什么作用

    IC1(OPA140或OPA227)電路中有什么作用? 換檔電路使用了模擬開關U3A(74HC4052),模擬開關的內阻(一般幾歐-幾十歐)對電路有影響沒?請說明理由。 (3)R2、R5
    發表于 07-29 06:43

    導熱儀材料性能分析中的重要作用

    導熱儀是一種用于測量材料熱傳導性能的儀器,對于研究材料性能方面起到關鍵的作用,因此,被廣泛應用在材料科學、能源、化工、建筑工程等領域,用來對材料熱
    的頭像 發表于 07-01 14:03 ?184次閱讀
    導熱儀<b class='flag-5'>在</b>材料<b class='flag-5'>性能</b>分析中的重要<b class='flag-5'>作用</b>

    傳感器控制系統中有什么作用

    隨著科技的不斷進步和工業自動化的快速發展,傳感器控制系統中的作用愈發凸顯。傳感器作為一種能夠檢測和感知環境信息的裝置,控制系統中扮演著至
    的頭像 發表于 06-17 15:24 ?1284次閱讀

    示波器探頭高速信號測量中的作用

    探頭作為示波器與被測電路之間的關鍵接口,高速信號測量中發揮著至關重要的作用。本文將詳細探討示波器探頭高速信號測量中的
    的頭像 發表于 05-30 17:25 ?577次閱讀

    晶振激光雷達系統中的作用有哪些

    激光雷達系統需要用精確的時間測量來計算距離和生成高分辨率的3D圖像。晶振激光雷達系統中起著關鍵作用,主要用于提供穩定的時鐘信號和高精度的時
    的頭像 發表于 05-29 11:45 ?553次閱讀

    三坐標檢測技術制造業中有貢獻?

    三坐標檢測技術(CoordinateMeasuringMachine,CMM)制造業中的貢獻是多方面的,它對提高產品質量、優化生產流程、降低成本等方面起到了關鍵作用。以下是三坐標檢測技術制造業中
    的頭像 發表于 05-17 09:53 ?443次閱讀
    三坐標檢測技術<b class='flag-5'>在</b>制造業<b class='flag-5'>中有</b><b class='flag-5'>何</b>貢獻?

    什么是勵磁電感?與漏磁電感有區別呢?

    什么是勵磁電感?與漏磁電感有區別呢? 勵磁電感和漏磁電感是電感元件中的兩個重要概念,它們電磁學和電路應用中有著不同的作用和特性。本文將詳細介紹勵磁電感和漏磁電感的定義、特點以及區別
    的頭像 發表于 03-08 16:39 ?6175次閱讀

    壓敏電阻在家用電器中有作用

    壓敏電阻是一種特殊的電阻器件,其在家用電器中起著重要作用
    的頭像 發表于 02-23 18:15 ?2.1w次閱讀

    如何在Aurix TC399中測量DMIPS?

    (ifxcpu_perf_mpc.instruction.Counter;), 以及執行所花費的時間。 如果還有其他具體的方法可以測量 DMIPS,請幫我解決同樣的問題。
    發表于 01-29 06:29

    default單片機中有什么作用

    單片機中,default關鍵字的作用是為了switch語句中處理default情況下的代碼邏輯。switch語句是一種選擇結構,根據給定的表達式值,選擇不同的分支執行不同的代碼。當switch
    的頭像 發表于 01-08 10:11 ?1187次閱讀

    高精度電壓源的作用及其電壓測量中的應用

    電子領域中,高精度電壓源扮演著關鍵的角色,為各種應用提供穩定、準確的電壓信號。它是由精確的電壓源和精密的參考電路組成。下面Aigtek西安安泰電子將為大家介紹高精度電壓源的作用及其電壓測量
    的頭像 發表于 12-19 10:11 ?540次閱讀
    高精度電壓源的<b class='flag-5'>作用</b>及其<b class='flag-5'>在</b>電壓<b class='flag-5'>測量</b>中的應用

    POL負載點電源設計中有訣竅?有兩點很關鍵,你必須知道

    POL負載點電源設計中有訣竅?有兩點很關鍵,你必須知道
    的頭像 發表于 12-06 16:06 ?1910次閱讀
    POL負載點電源設計<b class='flag-5'>中有</b><b class='flag-5'>何</b>訣竅?有兩點很關鍵,你必須知道

    異構集成 (HI) 與系統級芯片 (SoC) 有區別?

    異構集成 (HI) 與系統級芯片 (SoC) 有區別?
    的頭像 發表于 11-29 15:39 ?2002次閱讀
    異構集成 (HI) 與<b class='flag-5'>系統</b>級芯片 (SoC) 有<b class='flag-5'>何</b>區別?