You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart JIT的startsWith为何性能远低于JavaScript同名方法?

Dart JIT环境下startsWith性能远低于JavaScript环境的核心原因

基准测试说明

测试对比两种字符串匹配逻辑的执行耗时:

  • 逻辑1:调用字符串内置startsWith方法从偏移1位置匹配固定字符串"123"
  • 逻辑2:手动通过codeUnitAt取固定偏移位置的字符编码,和1、2、3的ASCII编码直接比对

测试代码如下:

void _test1(int count) {
  final source = ' 123 ';
  for (var i = 0; i < count; i++) {
    var x = source.startsWith('123', 1);
  }
}

void _test2(int count) {
  final source = ' 123 ';
  for (var i = 0; i < count; i++) {
    var ok = source.codeUnitAt(1) == 0x31 &&
        source.codeUnitAt(2) == 0x32 &&
        source.codeUnitAt(3) == 0x33;
    var x = ok;
  }
}

JavaScript环境测试结果

Time passed: 0.000, Test 'startsWith': 105.4 ms
Time passed: 0.106, Test 'codeUnitAt': 87.8 ms
Time passed: 0.194, Test 'startsWith': 86.7 ms
Time passed: 0.280, Test 'codeUnitAt': 87.101 ms
Time passed: 0.368, Test 'startsWith': 87 ms
Time passed: 0.455, Test 'codeUnitAt': 87.2 ms
Time passed: 0.542, Test 'startsWith': 86.699 ms
Time passed: 0.629, Test 'codeUnitAt': 85.6 ms
Time passed: 0.715, Test 'startsWith': 85.7 ms
Time passed: 0.801, Test 'codeUnitAt': 86.6 ms

JS环境下两种逻辑性能基本持平,startsWith平均耗时约85.7ms。

Dart JIT环境测试结果

Time passed: 0.000, Test 'startsWith': 837.2 ms
Time passed: 0.842, Test 'codeUnitAt': 125.094 ms
Time passed: 0.968, Test 'startsWith': 924.482 ms
Time passed: 1.892, Test 'codeUnitAt': 131.861 ms
Time passed: 2.024, Test 'startsWith': 736.603 ms
Time passed: 2.761, Test 'codeUnitAt': 76.636 ms
Time passed: 2.838, Test 'startsWith': 792.341 ms
Time passed: 3.631, Test 'codeUnitAt': 61.732 ms
Time passed: 3.693, Test 'startsWith': 721.816 ms
Time passed: 4.415, Test 'codeUnitAt': 73.93 ms

Dart JIT环境下startsWith平均耗时约721.8ms,是JS环境的8倍以上,也远高于同环境下手写codeUnitAt比对的性能。

性能差异核心成因

  • 两端引擎对内置方法的优化策略完全不同
    V8引擎(JS运行时)把String.prototype.startsWith作为核心内置函数做了IR层的深度特化:当编译器识别到调用点传入的匹配串是常量、起始偏移是固定值时,会直接把整个方法调用降级为连续的固定偏移内存取值+常量对比操作,生成的机器码和手写codeUnitAt比对的逻辑完全一致,不存在额外开销,因此两者性能持平。
    而Dart VM JIT下的startsWith走通用实现逻辑:首先要做参数合法性校验(偏移是否越界、入参是否为空),同时因为startsWith的入参支持所有实现Pattern接口的类型(不止String,还支持正则等),即使实际传入的是固定字符串常量,JIT编译器也不会做激进的调用点特化,不会把方法内联折叠成原生的字符比对逻辑,每次调用都要走类型判断、分支跳转的固定开销。
  • Dart JIT的编译优化保守性限制
    手写codeUnitAt比对的逻辑,Dart JIT可以做极致优化:识别到source是固定常量字符串、偏移是固定值,直接把循环内的操作折叠成常量返回,甚至消除整个循环的执行开销,因此性能极高。但startsWith作为core库的通用方法,JIT编译时采用保守策略,要兼容所有可能的入参场景,不会针对固定常量入参的场景做特殊优化,因此始终保留了函数调用、类型检查、通用循环比对的开销。
  • 补充验证结论
    若将测试中startsWith的入参改为动态变量而非固定常量,JS端的startsWith性能同样会出现明显下降;若使用Dart AOT模式编译运行,编译器可做全局调用点分析,对常量入参的短字符串startsWith调用做特化优化,与手写codeUnitAt的性能差距会缩小至10%以内。

内容的提问来源于stack exchange,提问作者mezoni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 13:12:27