我有一个 100Gb 大小的 xml 文件,并在这段代码中使用 SAX 方法解析它
file, err := os.Open(filename)
handle(err)
defer file.Close()
buffer := bufio.NewReaderSize(file, 1024*1024*256) // 33554432
decoder := xml.NewDecoder(buffer)
for {
t, _ := decoder.Token()
if t == nil {
break
}
switch se := t.(type) {
case xml.StartElement:
if se.Name.Local == "House" {
house := House{}
err := decoder.DecodeElement(&house, &se)
handle(err)
}
}
}
但是 golang 工作起来很慢,从执行时间和磁盘使用情况来看似乎是这样。我的硬盘能够以大约 100-120 mb/s 的速度读取数据,但 golang 仅使用 10-13 mb/s。 为了实验,我用 C# 重写了这段代码:
using (XmlReader reader = XmlReader.Create(filename)
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name == "House")
{
//Code
}
break;
}
}
}
并且我加载了完整的硬盘,c# 以 100-110mb/s 的速度读取数据。执行时间缩短约 10 倍。
如何使用 golang 提高 xml 解析性能?
最佳答案
这 5 件事可以帮助提高使用 encoding/xml
库的速度:
(针对具有 75k 个条目的 XMB 进行测试,20MB,%s 应用于前一个项目符号)
- 使用明确定义的结构
- 在所有结构上实现
xml.Unmarshaller
- 大量代码
- 节省 20% 的时间和 15% 的分配
- 将
d.DecodeElement(&foo, &token)
替换为foo.UnmarshallXML(d, &token)
- 几乎 100% 安全
- 节省 10% 的时间和分配
- 使用
d.RawToken()
而不是d.Token()
- 需要手动处理嵌套对象和命名空间
- 节省 10% 的时间和 20% 的分配
- 如果使用
d.Skip()
,请使用d.RawToken()
重新实现它
我在我的特定用例上减少了 40% 的时间和分配,代价是更多的代码、样板和可能更糟糕的极端情况处理,但我的输入相当一致,但这还不够.
benchstat first.bench.txt parseraw.bench.txt
name old time/op new time/op delta
Unmarshal-16 1.06s ± 6% 0.66s ± 4% -37.55% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
Unmarshal-16 461MB ± 0% 280MB ± 0% -39.20% (p=0.029 n=4+4)
name old allocs/op new allocs/op delta
Unmarshal-16 8.42M ± 0% 5.03M ± 0% -40.26% (p=0.016 n=4+5)
在我的实验中,lack of memoizing issue是 XML 解析器上的大量时间/分配显着减慢的原因,主要是因为 Go 按值复制。
关于c# - 标准 xml 解析器在 Golang 中的性能非常低,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46135167/