Go JSON 解码很慢。什么是更好的方法呢?
Go JSON decoding is very slow. What would be a better way to do it?
我正在使用 Go、Revel WAF 和 Redis。
我必须在 Redis 中存储大量 json 数据(可能 20MB)。
json.Unmarshal()
大约需要 5 秒。什么是更好的方法?
我尝试了 JsonLib、encode/json、ffjson、megajson,但其中 none 速度足够快。
想过用groupcache,但是Json是实时更新的
这是示例代码:
package main
import (
"github.com/garyburd/redigo/redis"
json "github.com/pquerna/ffjson/ffjson"
)
func main() {
c, err := redis.Dial("tcp", ":6379")
defer c.Close()
pointTable, err := redis.String(c.Do("GET", "data"))
var hashPoint map[string][]float64
json.Unmarshal([]byte(pointTable), &hashPoint) //Problem!!!
}
解析大型 JSON 数据似乎确实比应有的速度慢。查明原因并向 Go 作者提交补丁是值得的。
同时,如果你能避免JSON并使用二进制格式,你不仅会避免这个问题;您还将获得您的代码现在花费的时间将数字的 ASCII 十进制表示解析为它们的二进制 IEEE 754 等价物(并可能在这样做时引入舍入误差。)
如果你的发送方和接收方都是用 Go 编写的,我建议使用 Go 的二进制格式:gob。
做一个快速测试,生成一个有 2000 个条目的映射,每个条目有 1050 个简单的浮点数,给我 20 MB JSON,在我的机器上解析需要 1.16 秒。
对于这些快速基准测试,我采用了三个运行中最好的一个,但我确保只测量实际解析时间,在 Unmarshal 调用之前使用 t0 := time.Now()
并在它之后打印 time.Now().Sub(t0)
。
使用 GOB,同一张地图产生 18 MB 的数据,需要 115 毫秒来解析:
十分之一的时间。
您的结果会有所不同,具体取决于您那里有多少实际花车。如果您的浮点数有很多有效数字,值得使用 float64 表示,那么 20 MB 的 JSON 将包含比我的 200 万个浮点数少得多的数字。在那种情况下,JSON 和 GOB 之间的区别将更加明显。
顺便说一句,这证明问题确实出在 JSON 解析器上,而不是要解析的数据量,也不是要创建的内存结构(因为两个测试都在解析 ~ 20 MB 的数据并重新创建相同的浮点数片段。)用 JSON 中的字符串替换所有浮点数给我 1.02 秒的解析时间,确认从字符串表示到二进制浮点数的转换确实需要一定的时间(与仅移动字节)但不是罪魁祸首。
如果发送者和解析器不都是 Go,或者如果你想比 GOB 更进一步地压缩性能,你应该使用你自己定制的二进制格式,使用 Protocol Buffers 或手动 "encoding/binary"和朋友。
尝试fastjson。它针对速度进行了优化,通常解析 JSON 比标准 encoding/json
快得多。此外,fastjson
不需要遵循 JSON 架构的结构 - 单个解析器可以解析具有不同架构的多个 JSON。
尝试https://github.com/json-iterator/go
与官方相比,我的解码速度提升了 2 倍,更多的好处是 jsoniter 的 API 与 encoding/json 兼容。
我正在使用 Go、Revel WAF 和 Redis。
我必须在 Redis 中存储大量 json 数据(可能 20MB)。
json.Unmarshal()
大约需要 5 秒。什么是更好的方法?
我尝试了 JsonLib、encode/json、ffjson、megajson,但其中 none 速度足够快。
想过用groupcache,但是Json是实时更新的
这是示例代码:
package main
import (
"github.com/garyburd/redigo/redis"
json "github.com/pquerna/ffjson/ffjson"
)
func main() {
c, err := redis.Dial("tcp", ":6379")
defer c.Close()
pointTable, err := redis.String(c.Do("GET", "data"))
var hashPoint map[string][]float64
json.Unmarshal([]byte(pointTable), &hashPoint) //Problem!!!
}
解析大型 JSON 数据似乎确实比应有的速度慢。查明原因并向 Go 作者提交补丁是值得的。
同时,如果你能避免JSON并使用二进制格式,你不仅会避免这个问题;您还将获得您的代码现在花费的时间将数字的 ASCII 十进制表示解析为它们的二进制 IEEE 754 等价物(并可能在这样做时引入舍入误差。)
如果你的发送方和接收方都是用 Go 编写的,我建议使用 Go 的二进制格式:gob。
做一个快速测试,生成一个有 2000 个条目的映射,每个条目有 1050 个简单的浮点数,给我 20 MB JSON,在我的机器上解析需要 1.16 秒。
对于这些快速基准测试,我采用了三个运行中最好的一个,但我确保只测量实际解析时间,在 Unmarshal 调用之前使用 t0 := time.Now()
并在它之后打印 time.Now().Sub(t0)
。
使用 GOB,同一张地图产生 18 MB 的数据,需要 115 毫秒来解析:
十分之一的时间。
您的结果会有所不同,具体取决于您那里有多少实际花车。如果您的浮点数有很多有效数字,值得使用 float64 表示,那么 20 MB 的 JSON 将包含比我的 200 万个浮点数少得多的数字。在那种情况下,JSON 和 GOB 之间的区别将更加明显。
顺便说一句,这证明问题确实出在 JSON 解析器上,而不是要解析的数据量,也不是要创建的内存结构(因为两个测试都在解析 ~ 20 MB 的数据并重新创建相同的浮点数片段。)用 JSON 中的字符串替换所有浮点数给我 1.02 秒的解析时间,确认从字符串表示到二进制浮点数的转换确实需要一定的时间(与仅移动字节)但不是罪魁祸首。
如果发送者和解析器不都是 Go,或者如果你想比 GOB 更进一步地压缩性能,你应该使用你自己定制的二进制格式,使用 Protocol Buffers 或手动 "encoding/binary"和朋友。
尝试fastjson。它针对速度进行了优化,通常解析 JSON 比标准 encoding/json
快得多。此外,fastjson
不需要遵循 JSON 架构的结构 - 单个解析器可以解析具有不同架构的多个 JSON。
尝试https://github.com/json-iterator/go
与官方相比,我的解码速度提升了 2 倍,更多的好处是 jsoniter 的 API 与 encoding/json 兼容。