一个 100 MiB 的 RPC 要在内存里复制多少份?小消息每次调用的固定开销是多少?string 和 bytes 的反序列化差多少?这篇用 testing.Benchmark 和 MemStats 把这些数字量出来。
1 KiB Marshal 0.2 µs、分配 1152 字节/次(1.12 倍 payload);Unmarshal 0.2 µs、分配 1120 字节/次(1.09 倍 payload) 16 KiB Marshal 1.9 µs、分配 18432 字节/次(1.12 倍 payload);Unmarshal 1.9 µs、分配 16480 字节/次(1.01 倍 payload) 100 KiB Marshal 10.5 µs、分配 106497 字节/次(1.04 倍 payload);Unmarshal 10.3 µs、分配 106592 字节/次(1.04 倍 payload) 512 KiB Marshal 53.3 µs、分配 532481 字节/次(1.02 倍 payload);Unmarshal 26.6 µs、分配 524384 字节/次(1.00 倍 payload) 1 MiB Marshal 74.0 µs、分配 1056770 字节/次(1.01 倍 payload);Unmarshal 52.6 µs、分配 1048674 字节/次(1.00 倍 payload) 2 MiB Marshal 124.7 µs、分配 2105346 字节/次(1.00 倍 payload);Unmarshal 86.5 µs、分配 2097254 字节/次(1.00 倍 payload) 8 MiB Marshal 279.9 µs、分配 8396815 字节/次(1.00 倍 payload);Unmarshal 260.9 µs、分配 8388709 字节/次(1.00 倍 payload)
Marshal 把字段拷进新缓冲,Unmarshal 把 bytes 字段拷出来。大消息每经过一次编码或解码就多一份拷贝。
1 KiB 每次调用分配 11632 字节(= 11.36 倍 payload;请求加响应共 2048 字节 payload 来回);139 次内存分配;耗时 0.1 ms/次 16 KiB 每次调用分配 47274 字节(= 2.89 倍 payload;请求加响应共 32768 字节 payload 来回);153 次内存分配;耗时 0.1 ms/次 100 KiB 每次调用分配 422102 字节(= 4.12 倍 payload;请求加响应共 204800 字节 payload 来回);162 次内存分配;耗时 0.2 ms/次 512 KiB 每次调用分配 1847324 字节(= 3.52 倍 payload;请求加响应共 1048576 字节 payload 来回);234 次内存分配;耗时 0.8 ms/次 1 MiB 每次调用分配 3172576 字节(= 3.03 倍 payload;请求加响应共 2097152 字节 payload 来回);293 次内存分配;耗时 1.3 ms/次 2 MiB 每次调用分配 6622418 字节(= 3.16 倍 payload;请求加响应共 4194304 字节 payload 来回);414 次内存分配;耗时 2.5 ms/次 8 MiB 每次调用分配 27044150 字节(= 3.22 倍 payload;请求加响应共 16777216 字节 payload 来回);957 次内存分配;耗时 9.4 ms/次
echo 一次大消息,客户端和服务端合计分配的内存稳定在 payload 的 3~4 倍;小消息每次还有约 11 KiB 的框架固定开销。
string(ASCII) 线上 1048580 字节;Unmarshal 62.8 µs/次,分配 1048674 字节/次 string(中文) 线上 1048579 字节;Unmarshal 568.5 µs/次,分配 1048673 字节/次 bytes 线上 1048580 字节;Unmarshal 48.2 µs/次,分配 1048676 字节/次 string 字段里放非法 UTF-8(0xff 0xfe):proto.Unmarshal 的错误:string field contains invalid UTF-8
string 要校验 UTF-8,中文文本的校验明显更慢;不需要文本语义的数据用 bytes。