All news
ASCodeworksReleases

SYNX 3.8 处处更快,所有解析器的结果终于一致了

SYNX 3.8.0 完全没有改动语言本身。变化在于每个实现读取它的速度:TypeScript 最多快 9.6 倍,Rust 快 2 到 3.6 倍;而且现在对同一个文件,所有实现都给出同一个结果。

by APERTURESyndicate · 2026年10月17日

SYNX 3.8 处处更快,所有解析器的结果终于一致了

SYNX 3.8.0 发布了。如果你只是写配置文件,文件本身不会有任何新东西,因为语法一点都没动。动的是它下面的一切:Rust 核心、TypeScript 包、Python 和 Node 绑定,以及全部七个原生移植,我们都在同一台机器、同一组文件上和 3.7.4 做了对比测量,然后在真正耗时的地方重新实现。

快了多少

简短的版本,直接用这个格式本身来写:

# 3.7.4 -> 3.8.0, same machine, same files
rust
  config_2_5kb     38.6us -> 17.6us
  static_4mb       60.1ms -> 35.3ms
  active_1_2mb     81.0ms -> 22.5ms
typescript_node
  config_2_5kb     34.0us -> 20.4us
  active_1_2mb    297.5ms -> 31.1ms
python
  config_2_5kb     50.3us -> 17.6us
  synxl_100k      173.7ms -> 83.2ms

提升最大的是 TypeScript 处理带标记的大文档,原因说起来有点尴尬:每个 :calc 键都会拿自己的表达式和文档里所有其他键重新比对一遍,所以工作量随文件大小的平方增长。在 Rust 里,大部分收益来自内存。使用 Windows 系统分配器时,一次普通解析几乎有一半时间花在申请内存上,而 3.8.0 在静态文件上的申请次数约为原来的三分之一,在活动模式下约为五分之一。二进制格式 .synxb 也做了同样的处理:文档里出现最多的字符串现在得到最短的索引,因此文件在 Rust 中缩小了 3% 到 20%,在 TypeScript 中最多缩小 40%;同一个文档现在总是编译成相同的字节,这是 3.7.4 没有保证的。

各个解析器其实并不一致

这一部分并不是我们去找的。为了确认更快的代码给出相同的结果,我们把同样的随机生成文档分别交给 Rust 核心和 TypeScript 包,再比较输出,结果有 17% 的 !active 文档输出不同。有时是 TypeScript 错了,有时是 Rust,有时 Rust 每次运行错得还不一样:它按哈希表的顺序遍历对象,所以规范里原样给出的 tax:calc / total:calc 示例,大约每两次运行就有一次以 CALC_ERR 失败。

一旦开始找,发现的问题就越来越多。Rust 里列表项内部的标记根本没有生效。Synx.stringify 和 .synxb 在写回时会改动数据:"007" 回来变成了数字 7,|+ 代码块丢了缩进,3,000 个随机文档里有 1,777 个经过一次往返就不再相同。有些文档甚至能让整个进程崩溃::clamp:nan:5 会让 Rust 恐慌退出,一个包含自身的文件会让栈溢出。这 55 个问题全部修复了,在最终代码上,Rust 核心和 TypeScript 对生成的 70,000 个文档全部给出一致结果。

七个移植,全部升级到 3.8.0

SYNX 还有面向 Go、Java、.NET、Dart、C++、Swift 和 Godot 的原生解析器,它们落后了好几个版本。现在全部升级到了 3.8.0,并用同样的方式和 Rust 核心对照,每个都用了数千个生成文档,已经没有任何差异。有些变化比其他的大得多:C++ 和 Swift 移植处理一个 1.2 MB 的活动文档原来要 88 秒和 47 秒,现在只要 33 毫秒和 95 毫秒;而 Godot 插件在这之前甚至无法在 Godot 4.7 上编译。

如何升级

你的 .synx 文件不需要任何改动。软件包是 npm 上的 @aperturesyndicate/synx-format、crates.io 上的 synx-core 和 PyPI 上的 synx-format,各个移植通过各自常用的仓库发布。如果你在 Rust 中直接使用 synx-core,对象现在是有序的 IndexMap 而不是 HashMap,所以自己构造 HashMap 再包装的代码需要加一个 .collect(),而通过 get 和遍历读取的代码照常编译。

这个格式目前还有一件事表达不了:列表项里的列表后面如果还跟着下一个列表项,写出再读回就无法还原,因为 SYNX 同一时间只保持一个打开的列表。这计划在 4.0 中解决,同时还会加入紧凑字符串,它在核心的实验副本里让解析又快了 1.2 到 1.5 倍。

所有改动和数据都在更新日志和 GitHub 上的 3.8.0 发布里。

Other stories

All stories
SYNX 3.8 处处更快,所有解析器的结果终于一致了 | APERTURESyndicate