WebAssembly 跨语言数据格式:JSON 方便,但不一定便宜
WebAssembly 跨语言数据格式:JSON 方便,但不一定便宜
一、跨边界传数据有成本
WebAssembly 插件和宿主之间经常需要传递数据。最简单的方法是 JSON:宿主序列化,插件解析,结果再序列化回来。JSON 很方便,但不一定便宜。
跨语言边界的成本包括序列化、内存复制、编码转换和 schema 校验。数据小的时候无所谓,数据大或调用频繁时,成本会变得明显。格式选择要看场景。
二、数据路径要拆开看
flowchart TD A[宿主数据] --> B[序列化] B --> C[复制到 WASM 内存] C --> D[插件解析] D --> E[执行逻辑] E --> F[结果序列化]每一步都有成本。JSON 字符串需要编码,WASM 内存需要分配和复制,插件内部还要解析成结构。若只测插件执行逻辑,会低估端到端耗时。
如果插件只处理少量配置,JSON 很合适。如果插件处理大批向量、图片或 token 流,就要考虑二进制格式、共享内存或分块传输。
三、格式要有 schema
#[derive(serde::Serialize, serde::Deserialize)] struct PluginInput { task_id: String, text: String, max_tokens: u32, }JSON 也要有 schema。不要把任意对象传给插件,再让插件自己猜字段。结构化输入能让宿主提前校验,错误也更清楚。
{ "task_id": "t_1024", "text": "需要处理的文本", "max_tokens": 512 }字段要版本化。新增字段应有默认值,删除字段要有迁移期。跨语言协议比普通内部函数更需要稳定。
四、性能测试要端到端
比较 JSON、MessagePack、Bincode 或自定义二进制格式时,要测端到端耗时和内存峰值。只测序列化函数不够,因为 WASM 边界复制也占成本。
还要考虑可调试性。二进制格式更快,但排查问题不如 JSON 直观。对低频控制消息,JSON 的可读性可能更重要;对高频大数据,二进制更合理。
内存所有权也要写清楚。宿主分配的 buffer 由谁释放,插件返回的指针何时失效,错误时是否需要释放,必须在协议中定义。WASM 边界上最怕“能跑一次”,长期却泄漏内存。
大数据传输可以分块。一次性把大数组复制进 WASM 内存,可能造成峰值内存过高。分块协议虽然复杂一点,但更容易控制内存和进度。
还要做格式兼容测试。旧插件读新字段、新插件读旧字段、缺字段和未知字段,都要有明确行为。跨语言数据协议如果没有兼容策略,升级会很痛。
最后,格式选择要写进文档。为什么使用 JSON,何时切换二进制,最大消息大小是多少,都应明确。默认无限制,是未来事故的邀请函。
跨语言协议还要处理字节序和数字精度。JSON 对大整数、浮点和二进制数据并不友好,某些语言会把大整数转成浮点。若任务涉及向量、时间戳或 ID,必须明确编码规则。
字符串编码也要统一。宿主和插件都应默认 UTF-8,并定义非法编码如何处理。不要让插件自行猜测,否则不同语言实现会产生不一致。
错误数据也要有格式。插件解析失败时,应返回结构化错误码和位置,而不是直接 panic。宿主才能把错误展示给用户或记录到审计日志。
最后,协议测试要跨语言运行。Rust 宿主、AssemblyScript 插件、TinyGo 插件如果都能通过同一组样例,兼容性才更可信。
实际测试中,MessagePack 在处理 10KB 左右的向量数据时,序列化+复制总耗时大约 80-150 微秒,而 JSON 同样的数据要 300-500 微秒。这个差距在高频调用场景下会很明显。但低频控制消息里,JSON 的可读性优势远大于这点性能差异。选格式之前,先算一下调用频率和数据量。
五、总结
WebAssembly 跨语言数据格式要根据数据大小、调用频率、可调试性和 schema 稳定性选择。JSON 方便,但不一定适合高频大数据。
跨边界之前,先把序列化、复制和解析成本算清楚。否则插件逻辑很快,端到端仍然慢。
