你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是微控制器CPU性能測試基準CoreMark。php
在嵌入式系統行業用於評價CPU性能指標的標準主要有三種:Dhrystone、MIPS、CoreMark,其中CoreMark是一種新興流行的嵌入式系統處理器測試基準,被認爲是比Dhrystone和MIPS更具備實際價值的測試基準。今天痞子衡就和你們詳細聊一聊CoreMark。html
在講CoreMark以前,必需要先提EEMBC(Embedded Microprocessor Benchmark Consortium)即嵌入式微處理器基準評測協會,它是一個非盈利性組織,該組織目前爲止(2018.03)共發佈了46個性能測試基準,有了這些性能基準參考,嵌入式設計人員能夠快速有效地選擇處理器。
EEMBC測試是基於每秒鐘算法執行的次數和編譯代碼大小的綜合統計結果。衆所周知,編譯器選項會對代碼大小和執行效率會產生巨大的影響,因此每種測試必須包括足夠多的編譯器信息並設置不一樣的優化項。
EEMBC發展勢頭很好,其頗有可能發展成爲嵌入式系統開發人員進行處理器和編譯器性能比較的工業標準。關於EEMBC的更多介紹可移步它的官方網站 http://www.eembc.org/linux
CoreMark是由EEMBC的Shay Gla-On於2009年提出的一項基準測試程序,其主要目標是測試處理器核心性能。
CoreMark標準的測試方法很簡單,就是在某配置參數組合下單位時間內跑了多少次CoreMark程序,其指標單位爲CoreMark/MHz。CoreMark數字越高,意味着性能更高。算法
CoreMark程序的目前(2018.03)最新版本是1.01。數組
核心程序下載 http://www.eembc.org/coremark/download.php
平臺移植示例 http://www.eembc.org/coremark/ports.php數據結構
核心程序包下載後,在\coremark_v1.0\readme.txt裏可見基本介紹,在\coremark_v1.0\docs\Coremark-requirements.doc裏可見設計需求。詳細文件目錄以下:less
\coremark_v1.0 \barebones --移植到裸機下須要修改的文件 \core_portme.h -- 移植平臺工程具體配置信息 \core_portme.c -- 計時以及板級初始化實現 \cvt.c \ee_printf.c -- 打印函數串口發送實現 \cygwin --移植到cygwin下須要修改的文件 \linux --移植到linux下須要修改的文件 \linux64 --移植到linux64下須要修改的文件 \simple --基本移植須要修改的文件 core_main.c --主程序入口 core_state.c --狀態機控制子程序 core_list_join.c --列表操做子程序 core_matrix.c --矩陣運算子程序 core_util.c --CRC計算子程序 coremark.h --工程配置與數據結構定義 \docs --設計文檔 coremark.md5 LICENSE.txt Makefile readme.txt --基本介紹 release_notes.txt --版本說明
若是是移植到ARM Cortex-M平臺下裸系統運行,通常只須要修改\barebones目錄下的文件便可(僅需改動三個函數portable_init()、barebones_clock()、uart_send_char()以及core_portme.h中若干宏定義),其他代碼文件不須要修改。關於\barebones下的文件修改,EEMBC上有以下4個示例平臺可參考:ide
前面講到作平臺移植時除了必需要改動3個函數外,還須要設置core_portme.h中若干宏定義,這些宏定義即爲配置參數,須要根據要移植到的具體平臺的屬性而定。一共以下14個宏:函數
宏 | 解釋 | 示例 |
---|---|---|
HAS_FLOAT | Define to 1 if the platform supports floating point | 1 |
HAS_TIME_H | Define to 1 if platform has the time.h header file and implementation of functions thereof | 0 |
USE_CLOCK | Define to 1 if platform has the time.h header file and implementation of functions thereof | 0 |
HAS_STDIO | Define to 1 if the platform has stdio.h. | 0 |
HAS_PRINTF | Define to 1 if the platform has stdio.h and implements the printf function. | 0 |
COMPILER_VERSION | Please put compiler version here (e.g. gcc 4.1) | "IAR EWARM v8.20.2" |
COMPILER_FLAGS | Please put compiler flags here (e.g. -o3) | "High - Speed - No size constraints" |
MEM_LOCATION | Please put the mem location of code execution here (e.g STACK) | "STACK" |
CORETIMETYPE | Define type of return from the timing functions. | ee_u32 |
SEED_METHOD | Defines method to get seed values that cannot be computed at compile time. | SEED_VOLATILE |
MEM_METHOD | Defines method to get a block of memry. | MEM_STATIC |
MULTITHREAD | Define for parallel execution | 1 |
MAIN_HAS_NOARGC | Needed if platform does not support getting arguments to main. | 1 |
MAIN_HAS_NORETURN | Needed if platform does not support returning a value from main. | 0 |
細心的朋友應該能注意到core_portme.h文件的最後有以下條件編譯,實際上CoreMark主程序的運行有3種模式可選,即PROFILE_RUN(原型模式)/PERFORMANCE_RUN(性能模式)/VALIDATION_RUN(驗證模式)性能
#if !defined(PROFILE_RUN) && !defined(PERFORMANCE_RUN) && !defined(VALIDATION_RUN) #if (TOTAL_DATA_SIZE==1200) #define PROFILE_RUN 1 #elif (TOTAL_DATA_SIZE==2000) #define PERFORMANCE_RUN 1 #else #define VALIDATION_RUN 1 #endif #endif
而在coremark.h文件的最開始就定義了缺省的TOTAL_DATA_SIZE的值爲2000,即CoreMark程序默認跑在PERFORMANCE_RUN(性能模式)下。若是你想修改運行模式,須要在編譯器預編譯選項裏自定義TOTAL_DATA_SIZE。
/* Configuration: TOTAL_DATA_SIZE Define total size for data algorithms will operate on */ #ifndef TOTAL_DATA_SIZE #define TOTAL_DATA_SIZE 2*1000 #endif
CoreMark程序使用C語言寫成,包含以下四類運算法則:數學矩陣操做(普通矩陣運算)、列舉(尋找並排序)、狀態機(用來肯定輸入流中是否包含有效數字)、CRC(循環冗餘校驗),都是在真實的嵌入式應用中很常見的操做,這也是CoreMark比其餘測試標準更有實際價值的緣由所在。
a. Matrix multiply (allow for use of MAC operations, common math use)
b. Linked list search/sort/read (common pointer use)
c. State machine (common use of data dependent branches)
d. CRC (common in embedded)
讓咱們嘗試分析CoreMark主函數入口main(以2.2節中配置示例值爲例):
/* Function: main Main entry routine for the benchmark. This function is responsible for the following steps: 1 - Initialize input seeds from a source that cannot be determined at compile time. 2 - Initialize memory block for use. 3 - Run and time the benchmark. 4 - Report results, testing the validity of the output if the seeds are known. Arguments: 1 - first seed : Any value 2 - second seed : Must be identical to first for iterations to be identical 3 - third seed : Any value, should be at least an order of magnitude less then the input size, but bigger then 32. 4 - Iterations : Special, if set to 0, iterations will be automatically determined such that the benchmark will run between 10 to 100 secs */ MAIN_RETURN_TYPE main(void) { int argc=0; char *argv[1]; ee_u16 i,j=0,num_algorithms=0; ee_s16 known_id=-1,total_errors=0; ee_u16 seedcrc=0; CORE_TICKS total_time; core_results results[MULTITHREAD]; // 系統板級初始化 portable_init(&(results[0].port), &argc, argv); // ... // 設置PERFORMANCE_RUN的初始參數 results[0].seed1=get_seed(1); //0x0 results[0].seed2=get_seed(2); //0x0 results[0].seed3=get_seed(3); //0x66 results[0].iterations=get_seed_32(4); //ITERATIONS // execs參數爲須要跑的算法使能位 results[0].execs=get_seed_32(5); //0x0 if (results[0].execs==0) { /* if not supplied, execute all algorithms */ results[0].execs=ALL_ALGORITHMS_MASK; } // ... results[0].memblock[0]=(void *)static_memblk; results[0].size=TOTAL_DATA_SIZE; results[0].err=0; /* Data init */ /* Find out how space much we have based on number of algorithms */ // ... // 各算法子程序初始化(共LIST, MATRIX, STATE三種) for (i=0 ; i<MULTITHREAD; i++) { if (results[i].execs & ID_LIST) { results[i].list=core_list_init(results[0].size,results[i].memblock[1],results[i].seed1); } if (results[i].execs & ID_MATRIX) { core_init_matrix(results[0].size, results[i].memblock[2], (ee_s32)results[i].seed1 | (((ee_s32)results[i].seed2) << 16), &(results[i].mat) ); } if (results[i].execs & ID_STATE) { core_init_state(results[0].size,results[i].seed1,results[i].memblock[3]); } } /* automatically determine number of iterations if not set */ // ... // 開始跑CoreMark程序且記錄累計消耗時間 start_time(); iterate(&results[0]); stop_time(); total_time=get_time(); // ... // 最終信息的打印 // ... if (total_errors==0) { ee_printf("Correct operation validated. See readme.txt for run and reporting rules.\n"); if (known_id==3) { ee_printf("CoreMark 1.0 : %f / %s %s",default_num_contexts*results[0].iterations/time_in_secs(total_time),COMPILER_VERSION,COMPILER_FLAGS); ee_printf("\n"); } } // ... /* And last call any target specific code for finalizing */ portable_fini(&(results[0].port)); return MAIN_RETURN_VAL; }
當移植好CoreMark程序後,即可以開始跑起來了,在跑程序的時候,EEMBC同時制定了必需要遵照規則(不遵照的話,跑分結果不被EEMBC所承認),詳見 https://www.eembc.org/coremark/CoreMarkRunRules.pdf。
當獲得跑分結果後可將結果提交到EEMBC網站上,跑分結果需按以下標準格式進行提交:
CoreMark 1.0 : N / C [/ P] [/ M] N - Number of iterations per second with seeds 0,0,0x66,size=2000) C - Compiler version and flags P - Parameters such as data and code allocation specifics - This parameter *may* be omitted if all data was allocated on the heap in RAM. - This parameter *may not* be omitted when reporting CoreMark/MHz M - Type of parallel execution (if used) and number of contexts This parameter may be omitted if parallel execution was not used. e.g. > CoreMark 1.0 : 128 / GCC 4.1.2 -O2 -fprofile-use / Heap in TCRAM / FORK:2 or > CoreMark 1.0 : 1400 / GCC 3.4 -O4 If reporting scaling results, the results must be reported as follows: CoreMark/MHz 1.0 : N / C / P [/ M] P - When reporting scaling results, memory parameter must also indicate memory frequency:core frequency ratio. - If the core has cache and cache frequency to core frequency ratio is configurable, that must also be included. e.g. > CoreMark/MHz 1.0 : 1.47 / GCC 4.1.2 -O2 / DDR3(Heap) 30:1 Memory 1:1 Cache
若是移植的CoreMark可以正確運行,你應該能夠看到串口會打印出相似以下格式的信息,上述要求的CoreMark標準結果就在打印信息的最後。
2K performance run parameters for coremark. (Run type) CoreMark Size : 666 (Buffer size) Total ticks : 25875 (platform dependent value) Total time (secs) : 25.875000 (actual time in seconds) Iterations/Sec : 3864.734300 (Performance value to report) Iterations : 100000 (number of iterations used) Compiler version : GCC3.4.4 (Compiler and version) Compiler flags : -O2 (Compiler and linker flags) Memory location : Code in flash, data in on chip RAM seedcrc : 0xe9f5 (identifier for the input seeds) [0]crclist : 0xe714 (validation for list part) [0]crcmatrix : 0x1fd7 (validation for matrix part) [0]crcstate : 0x8e3a (validation for state part) [0]crcfinal : 0x33ff (iteration dependent output) Correct operation validated. See readme.txt for run and reporting rules. (*Only when run is successful*) CoreMark 1.0 : 6508.490622 / GCC3.4.4 -O2 / Heap (*Only on a successful performance run*)
截止到目前(2018.03),EEMBC網站共記錄535款微控制器的CoreMark跑分結果(注意並非全部跑分結果都通過EEMBC覈實),全部跑分結果可在這裏查詢 https://www.eembc.org/coremark/scores.php,下圖是跑分榜部分結果(按提交日期排序)。若是是設計人員根據性能選型的話,能夠選按得分高低排序。
在上節介紹的跑分榜裏可點擊微控制器型號查看具體結果,也可選擇多個微控制器進行結果對比。最近兩家ARM Cortex-M微控制器知名廠商恩智浦半導體和意法半導體在高性能微控制器上正一決雌雄,恩智浦推出的i.MX RT1050和意法半導體推出的STM32H743均是基於Cortex-M7內核,且都在2017.10實現第一版量產,咱們且來比比看這兩款微控制器:
從對比結果來看,i.MX RT1050在性能上完爆STM32H743,其3036的總得分在Cortex-M微控制器裏獨孤求敗,這個跑分結果雖未通過EEMBC認證,但與恩智浦官方給的數據3020基本吻合。
關於i.MX RT系列微控制器簡介可詳見個人另外一篇文章 飛思卡爾i.MX RT系列微控制器介紹篇(1)- 概覽,對於i.MX RT1050跑分結果的驗證與復現可詳見個人文章 飛思卡爾i.MX RT系列微控制器介紹篇(2)- 性能CoreMark。
至此,微控制器CPU性能測試基準CoreMark痞子衡便介紹完畢了,掌聲在哪裏~~~