Nim 1.4.2环境下标准库isSpaceAscii性能为何远低于自实现isSpace
Nim 标准库
isSpaceAscii 性能低于自实现版本问题 我尝试调用标准库中的 isSpaceAscii,发现其性能远差于自行实现的proc。
复现代码如下:
import strutils import std/monotimes import stats template timeIt(tag: string, iter: untyped, body: untyped) = var st: RunningStat for i in countup(1, iter): let t0 = getMonoTime().ticks body let t1 = getMonoTime().ticks let d = t1 - t0 st.push(d.float64) echo tag, ": ", st.min proc isSpace(c: char): bool = result = c in Whitespace when isMainModule: # check eqaulity for i in 1..255: let c = char(i) doAssert isSpace(c) == isSpaceAscii(c) timeIt "isSpaceAscii", 1000: for i in 1..255: let c = char(i) discard isSpaceAscii(c) timeIt "isSpace", 1000: for i in 1..255: let c = char(i) discard isSpace(c)
基准测试结果如下:
$ nim compile -d:release --verbosity:0 --hints:off --run test.nim isSpaceAscii: 380.0 isSpace: 20.0
编译器版本信息:
$ nim -V Nim Compiler Version 1.4.2 [Linux: amd64]
核心提问:为什么自行实现的 isSpace 性能会优于标准库的 isSpaceAscii?
原因解析
- 两类实现的编译优化路径存在本质差异
- 你自实现的
isSpace直接基于Whitespace常量集合做成员判断,Nim编译器处理c in Whitespace逻辑时,会直接生成基于固定字符集合的位掩码比较指令,没有额外的函数跳转开销;在release模式下,编译器甚至可以把整段循环的判断逻辑完全展开成零分支的位运算,不需要额外的栈操作。 - 对应版本的
isSpaceAscii没有添加inline编译指令,函数以独立编译单元的形式导出,编译器无法跨模块对其做内联优化。每次调用都需要完成压栈、跳转执行、栈恢复的固定流程,哪怕判断逻辑和你的实现完全等价,这部分固定栈操作开销就会占据绝大部分耗时。
- 你自实现的
- 测试场景放大了固定调用开销的占比
你的基准循环单次只完成单字符判断,函数调用的固定开销在总耗时中的占比极高:380个ticks的测量结果里,真正用于字符判断的逻辑耗时占比不到10%,剩下的全是函数调用的栈操作成本。如果把测试场景换成批量处理连续长字符串,两种实现的实际判断逻辑耗时差异会快速缩小到可忽略的水平。
后续发布的Nim版本已经为
isSpaceAscii补充了强制内联标记,这类跨模块无法内联导致的性能问题已经被修复,在新版本中运行同一份测试代码,两个函数的性能会基本持平。
内容的提问来源于stack exchange,提问作者Artem Klevtsov
相关产品推荐
相关产品推荐

