PHP中json_encode比Avro编码器更快?基准测试存疑求解
问题:Avro序列化速度远慢于JSON的原因排查
所有资料都显示Avro编码比JSON更快,但实际测试中JSON序列化速度始终远超Avro,这是为什么?
我的测试代码
Avro序列化方法
public function serialize(array $documentArray) { $this->schemaID = 1; $parsedSchema = AvroSchema::parse(SCHEMA); // 检查本地缓存中是否已有io datum writer,有则复用 if (isset($this->ioDatumWriterCache[$this->schemaID])) { $datumWriter = $this->ioDatumWriterCache[$this->schemaID]; } else { $datumWriter = new \AvroIODatumWriter($parsedSchema); $this->ioDatumWriterCache[$this->schemaID] = $datumWriter; } // 检查本地缓存中是否已有binary encoder,有则复用 if (isset($this->binaryEncoderCache[$this->schemaID])) { $encoder = $this->binaryEncoderCache[$this->schemaID]; } else { $encoder = new \AvroIOBinaryEncoder($this->ioWriter); $this->binaryEncoderCache[$this->schemaID] = $encoder; } $datumWriter->write($documentArray, $encoder); return $this->ioWriter->string(); }
JSON序列化方法
public function serialize(array $documentArray): string { try { return json_encode($documentArray, JSON_THROW_ON_ERROR); } catch (\Exception $ex) { throw new ObjectIOException('JSON decode exception', 0, $ex); } }
测试结果(使用phpbench)
- JSON编码平均耗时:
1.685μs、1.642μs、1.637μs、1.642μs、1.637μs - Avro编码平均耗时:
40.574μs、40.716μs、40.664μs、40.480μs、40.583μs
问题原因与优化方案
1. Schema重复解析是核心开销
你的代码每次调用serialize都会执行AvroSchema::parse(SCHEMA),Schema解析是CPU密集型操作,哪怕缓存了Writer和Encoder,这一步的重复执行会吃掉大量性能。必须将Schema解析移到类初始化阶段,只执行一次。
2. PHP Avro与JSON的实现差异
json_encode是PHP底层的C语言实现,性能极高;而PHP的Avro库大多是纯PHP实现,本身在执行效率上就有先天差距。Avro的优势更多体现在:
- 大数据量、重复序列化相同结构数据的场景
- 跨语言兼容性
- 序列化后的数据体积更小(二进制格式)
小数据量下,纯PHP实现的Avro很难打过原生C实现的JSON。
3. IO Writer未重置导致的额外开销
如果$this->ioWriter没有在每次序列化前重置状态,会导致缓冲区累积之前的数据,增加后续写入和读取的开销。每次序列化前需要调用$this->ioWriter->rewind()清空缓冲区。
4. 缓存逻辑的有效性验证
确认测试时使用的是同一个类实例多次调用serialize,如果每次测试都新建类实例,缓存的Writer和Encoder就无法复用,会重复初始化带来额外开销。
优化后的Avro序列化代码示例
public function __construct() { // 初始化阶段只执行一次Schema解析 $this->schemaID = 1; $this->parsedSchema = AvroSchema::parse(SCHEMA); // 提前初始化并缓存Writer $this->ioDatumWriterCache[$this->schemaID] = new \AvroIODatumWriter($this->parsedSchema); // 初始化可复用的IO Writer $this->ioWriter = new \AvroStringIO(); // 缓存Encoder $this->binaryEncoderCache[$this->schemaID] = new \AvroIOBinaryEncoder($this->ioWriter); } public function serialize(array $documentArray) { // 重置IO Writer,清除上次序列化的残留数据 $this->ioWriter->rewind(); $datumWriter = $this->ioDatumWriterCache[$this->schemaID]; $encoder = $this->binaryEncoderCache[$this->schemaID]; $datumWriter->write($documentArray, $encoder); return $this->ioWriter->string(); }
额外测试建议
- 测试更大的数据集(比如包含数百字段的复杂结构,或批量序列化数千条数据),观察Avro的性能表现是否会提升
- 对比序列化后的数据体积,Avro的二进制格式通常比JSON小30%-50%,这也是它的核心优势之一
- 确保测试时复用同一个类实例,让缓存逻辑真正生效
内容的提问来源于stack exchange,提问作者HerbertRedford
相关产品推荐
相关产品推荐

