Dhrystone基准测试详解:从DMIPS到嵌入式性能评估 📅 发布时间:2026/9/2 3:51:09 👁 浏览次数: 简介这是一套基于MCU场景的经典Dhrystone 2.1基准测试程序包面向嵌入式开发、板卡选型与硬件性能评估人员用于测量处理器整数运算能力并可在不同MCU或编译器优化选项间进行横向对比典型适用对象包括嵌入式软件工程师、硬件选型人员和性能评测爱好者。包内共51个文件以C源码、README说明、汇编文件及编译/运行脚本为主并针对Dhrystone、Whetstone、Linpack、Livermore loops等经典测试提供64位、NoOpt等不同编译变体便于快速执行与结果参照。压缩包为7z格式仅166KB目录组织清晰包含源码目录、预编译目录以及批量运行脚本可直接在目标平台运行也可自行交叉编译。各测试均附带源码与可执行版本方便比对不同优化策略下的性能差异满足从裸机评测到系统级对比的常见需求。目前已有573人学习下载适合希望建立标准化性能测试流程、进行MCU选型对比或验证编译器优化效果的开发者和评测人员。1. 从跑分说起Dhrystone Benchmark到底在测什么如果你在嵌入式开发、CPU选型或者RTOS评估的圈子里泡过一阵子大概率绕不开一个名字Dhrystone。这不是一个花哨的测试工具它不测GPU、不测浮点、不测内存带宽它只干一件事——用一套精心设计的混合整数运算把你的处理器烤一遍然后给出一个叫DMIPS的分数。我第一次认真接触Dhrystone是在做一颗Cortex-M内核的MCU选型时。当时芯片手册上写120 MHz主频1.25 DMIPS/MHz我直觉上觉得这个数挺高但又说不清它到底代表什么样的真实性能。后来花了一下午把Dhrystone 2.1源码拉下来编译、运行、读报告才算是把这块短板补上了。这篇东西不打算写成教材我就以实际跑分的过程为线索把Dhrystone Benchmark Version 2.1是什么、怎么跑、结果怎么解读、哪些坑我必须替你踩过一遍全部摊开讲清楚。如果你正在做嵌入式开发、在对比不同架构的IPC每时钟周期指令数、或者只是想搞明白芯片手册上那个DMIPS是怎么来的这篇应该能帮你省不少时间。2. 核心机制拆解Dhrystone 2.1的运作逻辑和分数换算2.1 它测的整数运算到底是什么很多人一听整数运算基准测试第一反应是不就是算算术吗。其实Dhrystone测的不是单纯的加减乘除而是一整套混合负载。它模拟的是一个系统级程序中比较典型的操作分布字符串赋值、指针数组访问、递归调用、跳转判断、函数调用开销等。这些操作共同构成了一个程序运行的日常压力。V2.1版本的源码里包含几个核心程序段指针操作pointer operations本质上是在内存里来回搬运地址考验访存延迟和地址计算能力。字符串复制这玩意儿看着简单但实打实靠内存读写带宽和循环控制效率。整数运算和逻辑判断包括加减乘除、位操作、条件分支。递归调用fn_B、fn_A这些函数会互相调用甚至带参数和全局变量泄漏考验栈处理能力。数组操作连续内存访问看缓存流畅度。有意思的是Dhrystone 2.1源码刻意把所有变量类型、函数签名、全局变量定义都写得非常干净目的就是保证在不同编译器下行为一致。它不像一些老测试那样依赖未定义行为这也是它能跨平台流行几十年的原因之一。2.2 DMIPS到底怎么换算Dhrystone运行后会直接输出一个原始数值单位是Dhrystones per second每秒完成的Dhrystone循环次数。但大家讨论的时候一般不说这个原始值而是把它换算成DMIPS。核心换算关系是1 DMIPS 1757 Dhrystones per second这个基准参考机是VAX 11/7801980年代经典服务器它每秒能跑1757个Dhrystone循环官方标称1 MIPS所以这一条换算链路就固定下来了。换算出DMIPS后还可以进一步除以主频GHz得到DMIPS/MHz或者DMIPS/GHz用来做架构级别IPC的粗略估算。举个例子某MCU在120 MHz下跑出来每秒约263,000个Dhrystones。那么DMIPS就是263000 / 1757 ≈ 149.6 DMIPS换算成DMIPS/MHz就是149.6 / 120 ≈ 1.25。这就是芯片手册上那个数字的由来。这里必须提醒一件事DMIPS/MHz这个指标的参考价值依赖CPU主频和编译器参数不同编译选项下结果能差出30%以上。后面我会专门说编译器这块。2.3 为什么有人说它测的是编译器而不是CPU我听过不少工程师吐槽Dhrystone的分数很大程度上是编译器的功劳不是CPU的功劳。这话有一定道理但也不全对。道理在于Dhrystone里的很多操作比如函数调用、指针递进、循环计数都是对编译器优化极其敏感的。一个寄存器分配好的编译器能把循环里的指针操作塞进寄存器内存访问次数大幅减少而另一个优化水平一般的编译器可能每次都老老实实读写内存。差距就这么出来了。不对在哪里呢Dhrystone从设计初衷上考察的就是编译器加硬件组合之后的综合性能。从实际系统角度看你最终跑软件用的是编译后的二进制不是纸上谈兵的CPU理论性能。所以DMIPS某种意义上比纯理论IPC更接近真实体验。但如果有人拿Dhrystone分数跨架构硬比那就危险了。不同编译器的脾气不同不同指令集的处理方式差异巨大可比性很容易失真。3. 实操指南从源码到跑分结果的全流程细节3.1 获取源码、确认版本Dhrystone Version 2.1的源码在网上非常容易找到经典的就是来自Netlib的打包文件——通常包含dhry.h、dhry_1.c、dhry_2.c、cp.c这几个文件外加一个README。如果你做嵌入式开发有些MCU厂商的SDK里也会自带一个适配好的副本。我在动手之前会快速确认一下版本号。V2.1源码在dhry.h里通常有版本注释也定义了所有函数原型。如果看到某些移植版改了很多局部变量甚至把全局变量改成了static那结果的可比性就要画个问号。3.2 编译参数的选择编译器的选择是跑Dhrystone最核心的一步。我自己的经验是跑分之前先想清楚你是在评估硬件还是在评估方案。如果面向同架构MCU评估硬件性能我倾向于在默认优化级别比如-O2上对比因为大多数实际工程编译时也是这个级别。如果跟不同厂商的数据手册对比就不得不承认对手用的是什么优化级别尽量保证一致对比才公平。以GCC为例常用的编译命令gcc -O2 -DHAVE_CONFIG_H -I. dhry_1.c dhry_2.c cp.c -o dhrystone这里-DHAVE_CONFIG_H是让代码走已配置分支避免某些移植代码默认走可移植模式导致结果大幅下降。目录下如果没有config.h可能需要自己搞一个或者直接不定义这个宏我用过的适配版本里通常自带。还有一个小细节-fno-inline这个选项。如果加了它所有函数的调用开销都会被真实压测到分数会偏低但如果你的目标场景本身就是关内联编译那也没错。我建议默认不要加因为开-O2时GCC会内联部分函数这本身就符合生产环境的状态。3.3 运行参数和结果生成编译出可执行文件后直接跑起来。经典命令行长这样./dhrystone如果没传参数程序默认会以1000000次循环跑一遍输出大概长这样Dhrystone Benchmark, Version 2.1 (Language: C) Program compiled without register attribute Please give the number of runs through the benchmark:等等有些版本它在开始前会等待你输入一个运行次数。如果不想手动交互可以直接用管道输入或者跑之前先看源码里的main函数怎么写的。我习惯用echo 1000000 | ./dhrystone跑完之后会有几行输出Execution times, seconds time_passed: 1.20 seconds integer: 0.98 seconds string: 0.22 seconds This machine benchmarks at 833333 dhrystones/second这里输出的dhrystones/second就是原始成绩。换算方式就是前面说的除以1757得到DMIPS。如果机器性能太强跑1000000次循环可能不到0.01秒就结束计时精度不足。此时可以增大循环数。我比较推荐把运行时间控制在2秒以上否则计时误差会影响结果。经过实测我一般选择5000000甚至10000000次循环。3.4 计时器精度问题老版本的Dhrystone源码在时基选择上依赖操作系统。在Linux上直接用系统时钟通常没问题但在Windows上用古老的秒表计时结果可能忽高忽低。如果你在嵌入式裸机环境下跑Dhrystone要在板子上移植计时函数。通常做法是拿一个硬件定时器做时间基准精度需要微秒级否则跑分误差大到没法看。我自己在STM32F4上跑过直接把TIM用起来作为时钟源然后修改dhry_1.c里的计时逻辑输出秒数。那是另一个折腾但理解了原理后就很简单——本质上就是把程序跑完用了多少秒测准。4. 结果解读DMIPS的实际参考价值与局限4.1 从DMIPS里能读出什么DMIPS数字背后隐藏着至少两层信息。第一层是取舍若两个芯片主频一样DMIPS高的那一个通常意味着单周期能干的活更多可能是指令集效率高也可能是流水线设计更好。如果你在做芯片选型主频和DMIPS要一起看单看其中一个容易翻车。第二层是识别优化水平如果你把同一个芯片的Dhrystone分数比手册上低20%以上不要急着怀疑芯片。先检查编译器优化级别、使用的库是否有差异。我见过不少新板子性能不达标的案例最后都是编译器配置问题。4.2 常见架构的参考数据拿我这些年跑过的几类平台做参考数据大概是这样的平台主频优化级别DMIPS约ARM Cortex-M372 MHz-O287ARM Cortex-M4F168 MHz-O2210ARM Cortex-A71.2 GHz-O22700x86 Pentium G44003.3 GHz-O29200这些数字只能当作数量级参考因为实际取决于编译器版本、内存系统和具体硬件配置。但看了这份表至少能建立起一个直觉不同档次的平台之间DMIPS的跨度是非常大的。4.3 为什么不能迷信DhrystoneDhrystone最大的问题在于它的代码规模很小完全可以塞进现代CPU的L1缓存里。一旦缓存命中CPU跑的是缓存友好型负载跟真实应用的访存压力差得很远。另外它几乎不涉及浮点、不涉及多线程、不涉及内存带宽饱和。而在现代真实应用中这些往往才是瓶颈。比如视频编解码、深度学习推理靠Dhrystone是测不出差异的。所以我的建议是Dhrystone适合做快速筛选和数量级对比但不适合作为唯一选型依据。如果两款芯片DMIPS差距在10%以内实际上可能打成平手甚至反转。5. 常见问题与排查技巧实录5.1 缓存对跑分结果的影响我很早就发现一个现象同样一颗CPU在Linux上跑Dhrystone和在DOS上跑分数能差出好几倍。这个现象的根源就是缓存。Dhrystone的全部代码和数据体积大概就几十KB现代CPU的L1缓存普遍32KB甚至64KB稍微优化一下就能全部缓存住。这导致Dhrystone几乎是在测缓存命中情况下的极限计算能力而真实程序往往在缓存和外存之间饱受折磨。如果你是要跟某些嵌入式场景匹配我建议把Dhrystone结果乘以一个折扣系数比如0.6~0.8。这样估算实际应用性能更靠谱。5.2 编译器优化等级怎么选这是被问得最多的问题。我自己的固定做法是在报告里永远写清楚编译器和优化等级。用-O0跑出来的Dhrystone分数会很惨不代表CPU差。用-O3 -funroll-loops跑出来的会非常高也不代表真实程序有那么快。有一个经典比喻你用跑车测速仪量了一辆家用车测出来180 km/h但你在市区开根本到不了这个速度因为路况不允许。Dhrystone就是那条专用的高速公路。如果你的目标是跟官方数据手册对比最好参考手册里注明的编译器和优化等级尽量保持一致。如果查不到把差别说清楚不要直接拿着数字硬比。5.3 交叉编译时容易踩的坑嵌入式场景下用arm-none-eabi-gcc交叉编译太常见了。我踩过几个坑第一浮点打印不要开。Dhrystone输出分数时需要打印浮点数如果你的MCU工程没有开启浮点printf支持可能在输出时卡死或者打印乱码。解决方案是用整数逻辑去替代打印比如把浮点值乘1000转成整数输出。第二堆栈大小要够。Dhrystone的递归调用深度不大但如果你用了很大的局部数组某些深度优化下栈帧会增大。嵌入式平台默认1KB栈可能不够我一般给任务栈留4KB。第三定时器中断优先级不要干扰跑分。如果中断频率太高会持续打断基准循环导致分数偏低。跑分时建议关掉不必要的中断至少把运行期间保持稳定环境。5.4 我个人的实操心得跑Dhrystone快十年了总结下来三句话第一它是一件趁手的工具但不是神尺第二跑分之前先想明白我要拿这个数干什么第三永远记录环境参数否则这个分日后就是一堆无意义的数字。如果你在对比两颗芯片我的建议是不要只跑Dhrystone至少同时跑CoreMark和一套与应用场景相关的微测试。CoreMark目前更贴近现代处理器负载而Dhrystone胜在简单、方便、历久弥新。把几个维度摆在一起才能更接近真实世界的性能。最后再分享一个实用技巧如果跑分机器上有超频或者动态调频机制建议先锁定频率。拿Linux的话可以临时把CPU governor设为performance模式再去跑不然分数会剧烈波动。用命令cpupower frequency-set -g performance或者echo performance /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor都能快速搞定。实测下来这个步骤能避免很多不必要的返工。本文还有配套的精品资源点击获取