dotnet / dotnet/runtime

XmlDocument slow with many children

Open
#120,297 3 comments 0 reactions 0 assignees View on GitHub
area-System.Xml tenet-performance
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Description

I ran into an issue in an application where a XmlDocument had a node with 150000 text node children.
The element contained an base64-encoded pdf which for some reason was split up in text nodes of 256 characters each.

The problem is not the split but that it takes long time to iterate over all children.
```cs
for (var node = root.FirstChild; node != null; node = node.NextSibling)
{
count += 1;
}
```
(and also `node.ChildNodes.Count`)

In my test it also looks like the iteration time is exponential.
50000 text nodes = 1.8s
100000 text nodes = 7s
150000 text nodes = 16s

My test code
```cs
void Main()
{
var doc = new XmlDocument();
var root = doc.CreateElement("Root");
doc.AppendChild(root);
AddChildren(doc, root, 150000);
CountChildren(doc.FirstChild).Dump();
}

void AddChildren(XmlDocument doc, XmlNode node, int count)
{
string text = new string('x', 256);
for (int ii = 0; ii < count; ++ii)
{
var textNode = doc.CreateTextNode(text);
node.AppendChild(textNode);
}
}

int CountChildren(XmlNode root)
{
int count = 0;
var sw = new Stopwatch();
sw.Start();
for (var node = root.FirstChild; node != null; node = node.NextSibling) {
count += 1;
}

Console.WriteLine($"{count} - {sw.ElapsedMilliseconds}ms");
return count;
}
```

Could not figure out why, but in my real application it takes much longer time.
The source for the XmlDocument in that case is a WCF message created from
```cs
var doc = new XmlDocument();
using (var docWriter = doc.CreateNavigator().AppendChild())
{
message.WriteBody(docWriter);
}
```

### Configuration
```
Benchmark Process Environment Information:
BenchmarkDotNet v0.13.8
Runtime=.NET 9.0.9 (9.0.925.41916), X64 RyuJIT AVX2
GC=Concurrent Workstation
HardwareIntrinsics=AVX2,AES,BMI1,BMI2,FMA,LZCNT,PCLMUL,POPCNT,AvxVnni,SERIALIZE VectorSize=256
```

### Regression?

I got the same result in .Net Core 3.1.32

### Data

### Analysis

I looked at the source code which seems to be a single linked list. Have no idea how that can result in exponential iteration time.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.