我正在研究概念证明,以研究解析具有一定数量实体的XML文档所需的时间。
首先,我的结构确实包含XML文档中的条目:
type Node struct {
ID int `xml:"id,attr"`
Position int `xml:"position,attr"`
Depth int `xml:"depth,attr"`
Parent string `xml:"parent,attr"`
Name string `xml:"Name"`
Description string `xml:"Description"`
OwnInformation struct {
Title string `xml:"Title"`
Description string `xml:"Description"`
} `xml:"OwnInformation"`
Assets []struct {
ID string `xml:"id,attr"`
Position int `xml:"position,attr"`
Type string `xml:"type,attr"`
Category int `xml:"category,attr"`
OriginalFile string `xml:"OriginalFile"`
Description string `xml:"Description"`
URI string `xml:"Uri"`
} `xml:"Assets>Asset"`
Synonyms []string `xml:"Synonyms>Synonym"`
}
接下来,我有一个工厂可以生成任何给定数量的元素:
func CreateNodeXMLDocumentBytes(
nodeElementCount int) []byte {
xmlContents := new(bytes.Buffer)
xmlContents.WriteString("<ROOT>\n")
for iterationCounter := 0; iterationCounter < nodeElementCount; iterationCounter++ {
appendNodeXMLElement(iterationCounter, xmlContents)
}
xmlContents.WriteString("</ROOT>")
return xmlContents.Bytes()
}
// PRIVATE: appendNodeXMLElement appends a '<Node />' elements to an existing bytes.Buffer instance.
func appendNodeXMLElement(
counter int,
xmlDocument *bytes.Buffer) {
xmlDocument.WriteString("<Node id=\"" + strconv.Itoa(counter) + "\" position=\"0\" depth=\"0\" parent=\"0\">\n")
xmlDocument.WriteString(" <Name>Name</Name>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" <OwnInformation>\n")
xmlDocument.WriteString(" <Title>Title</Title>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" </OwnInformation>\n")
xmlDocument.WriteString(" <Assets>\n")
xmlDocument.WriteString(" <Asset id=\"0\" position=\"0\" type=\"0\" category=\"0\">\n")
xmlDocument.WriteString(" <OriginalFile>OriginalFile</OriginalFile>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" <Uri>Uri</Uri>\n")
xmlDocument.WriteString(" </Asset>\n")
xmlDocument.WriteString(" <Asset id=\"1\" position=\"1\" type=\"1\" category=\"1\">\n")
xmlDocument.WriteString(" <OriginalFile>OriginalFile</OriginalFile>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" <Uri>Uri</Uri>\n")
xmlDocument.WriteString(" </Asset>\n")
xmlDocument.WriteString(" <Asset id=\"2\" position=\"2\" type=\"2\" category=\"2\">\n")
xmlDocument.WriteString(" <OriginalFile>OriginalFile</OriginalFile>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" <Uri>Uri</Uri>\n")
xmlDocument.WriteString(" </Asset>\n")
xmlDocument.WriteString(" <Asset id=\"3\" position=\"3\" type=\"3\" category=\"3\">\n")
xmlDocument.WriteString(" <OriginalFile>OriginalFile</OriginalFile>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" <Uri>Uri</Uri>\n")
xmlDocument.WriteString(" </Asset>\n")
xmlDocument.WriteString(" <Asset id=\"4\" position=\"4\" type=\"4\" category=\"4\">\n")
xmlDocument.WriteString(" <OriginalFile>OriginalFile</OriginalFile>\n")
xmlDocument.WriteString(" <Description>Description</Description>\n")
xmlDocument.WriteString(" <Uri>Uri</Uri>\n")
xmlDocument.WriteString(" </Asset>\n")
xmlDocument.WriteString(" </Assets>\n")
xmlDocument.WriteString(" <Synonyms>\n")
xmlDocument.WriteString(" <Synonym>Synonym 0</Synonym>\n")
xmlDocument.WriteString(" <Synonym>Synonym 1</Synonym>\n")
xmlDocument.WriteString(" <Synonym>Synonym 2</Synonym>\n")
xmlDocument.WriteString(" <Synonym>Synonym 3</Synonym>\n")
xmlDocument.WriteString(" <Synonym>Synonym 4</Synonym>\n")
xmlDocument.WriteString(" </Synonyms>\n")
xmlDocument.WriteString("</Node>\n")
}
接下来,我有一个创建示例文档并解码每个''元素的应用程序:
func main() {
nodeXMLDocumentBytes := factories.CreateNodeXMLDocumentBytes(100)
xmlDocReader := bytes.NewReader(nodeXMLDocumentBytes)
xmlDocDecoder := xml.NewDecoder(xmlDocReader)
xmlDocNodeElementCounter := 0
start := time.Now()
for {
token, _ := xmlDocDecoder.Token()
if token == nil {
break
}
switch element := token.(type) {
case xml.StartElement:
if element.Name.Local == "Node" {
xmlDocNodeElementCounter++
xmlDocDecoder.DecodeElement(new(entities.Node), &element)
}
}
}
fmt.Println("Total '<Node />' elements in the XML document: ", xmlDocNodeElementCounter)
fmt.Printf("Total elapsed time: %v\n", time.Since(start))
}
这在我的计算机上大约需要11毫秒。
接下来,我使用goroutines解码XML元素:
func main() {
nodeXMLDocumentBytes := factories.CreateNodeXMLDocumentBytes(100)
xmlDocReader := bytes.NewReader(nodeXMLDocumentBytes)
xmlDocDecoder := xml.NewDecoder(xmlDocReader)
xmlDocNodeElementCounter := 0
start := time.Now()
for {
token, _ := xmlDocDecoder.Token()
if token == nil {
break
}
switch element := token.(type) {
case xml.StartElement:
if element.Name.Local == "Node" {
xmlDocNodeElementCounter++
go xmlDocDecoder.DecodeElement(new(entities.Node), &element)
}
}
}
time.Sleep(time.Second * 5)
fmt.Println("Total '<Node />' elements in the XML document: ", xmlDocNodeElementCounter)
fmt.Printf("Total elapsed time: %v\n", time.Since(start))
}
我使用一个简单的“ Sleep”命令来确保goroutine完成。 我知道应该通过通道和工作队列来实现。
根据控制台上的输出,只有3个元素被解码。 那么其他元素发生了什么?也许与我正在使用流有关的事实?
有什么办法可以使其并发,从而减少解码所有元素所需的时间?
答案 0 :(得分:0)
您只有一个xml.Decoder
对象。每当有人调用xmlDocDecoder.Token()
时,它将从(单个)输入流中读取下一个标记。在您的示例中,主循环和您启动的每个goroutine都试图同时读取同一输入流,因此令牌流将随机地分配到所有goroutine中。如果再次执行此操作,可能会得到不同的结果。令我感到惊讶的是,这种方法不会以某种奇怪的方式惊慌。
关于XML的几件事使得这很难并行化。您实际上需要在此处实现的顺序是:
<Node>
开始元素事件。</Node>
元素结束事件在同一深度进行,并记住您在此期间通过的每个事件。在实践中,“记住每个事件”步骤很可能与取消编组一样昂贵,并且整个过程比首先读取文件的磁盘或网络I / O要快得多。这似乎并不能很好地并行化。
这在我的计算机上大约需要11毫秒。
对于“快速”还是“慢速”,您并没有真正做得很好。请查看testing package中的基准测试支持,以寻求更好的方法以及built-in profiling tools。这样可以告诉您实际的时间在哪里,并建议您可以改善的地方。