c# - 标准 xml 解析器在 Golang 中的性能非常低

标签 c# xml go

我有一个 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 应用于前一个项目符号)

  1. 使用明确定义的结构
  2. 在所有结构上实现 xml.Unmarshaller
    • 大量代码
    • 节省 20% 的时间和 15% 的分配
  3. d.DecodeElement(&foo, &token) 替换为 foo.UnmarshallXML(d, &token)
    • 几乎 100% 安全
    • 节省 10% 的时间和分配
  4. 使用 d.RawToken() 而不是 d.Token()
    • 需要手动处理嵌套对象和命名空间
    • 节省 10% 的时间和 20% 的分配
  5. 如果使用 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/

相关文章:

java - 如何在 Eclipse 中为 XML 文件设置 "tabs only"-policy?

go - 这些 Go 构建标志是什么意思? netgo -extldflags "-lm -lstdc++ -static"'

c# - 取消线程内的BackgroundWorker

xml - MSXML 4.0 变成了什么?

c# - 为什么分配一个 int.MaxValue 的数组会失败,而分配两个大小为 int.MaxValue/2 的数组却不会?

java - 如何用java代码编写这个xml

go - 在 net/http golang 中链接中间件

go - Hyperledger-fabric 无法实例化链代码

c# - 自从在 nopcommerce 中创建数据库以来,支持 'ObjectContext' 上下文的模型已更改

c# - 将 json 反序列化为动态/匿名类 asp.net