Skip to content

Commit 83b1102

Browse files
committed
WIP: Benchmarks
1 parent 2b4cc98 commit 83b1102

File tree

6 files changed

+276
-0
lines changed

6 files changed

+276
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<EmbeddedResource Include="Sample.xml" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
using System.Reflection;
2+
using BenchmarkDotNet.Running;
3+
4+
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<PurchaseOrders>
3+
<!-- A comment -->
4+
<PurchaseOrder PurchaseOrderNumber="99503" OrderDate="1999-10-20">
5+
<Address Type="Shipping">
6+
<Name>Ellen Adams</Name>
7+
<Street>123 Maple Street</Street>
8+
<City>Mill Valley</City>
9+
<State>CA</State>
10+
<Zip>10999</Zip>
11+
<Country>USA</Country>
12+
</Address>
13+
<Address Type="Billing">
14+
<Name>Tai Yee</Name>
15+
<Street>8 Oak Avenue</Street>
16+
<City>Old Town</City>
17+
<State>PA</State>
18+
<Zip>95819</Zip>
19+
<Country>USA</Country>
20+
</Address>
21+
<DeliveryNotes>Please leave packages in shed by driveway.</DeliveryNotes>
22+
<Items>
23+
<Item PartNumber="872-AA">
24+
<ProductName>Lawnmower</ProductName>
25+
<Quantity>1</Quantity>
26+
<USPrice>148.95</USPrice>
27+
<Comment>Confirm this is electric</Comment>
28+
</Item>
29+
<Item PartNumber="926-AA">
30+
This is some text!
31+
<ProductName>Baby Monitor</ProductName>
32+
A second text node!
33+
<Quantity>2</Quantity>
34+
<USPrice>39.98</USPrice>
35+
<ShipDate>1999-05-21</ShipDate>
36+
</Item>
37+
</Items>
38+
</PurchaseOrder>
39+
<PurchaseOrder PurchaseOrderNumber="99505" OrderDate="1999-10-22">
40+
<?target Example Content?>
41+
<Address Type="Shipping">
42+
<Name>Cristian Osorio</Name>
43+
<Street>456 Main Street</Street>
44+
<City>Buffalo</City>
45+
<State>NY</State>
46+
<Zip>98112</Zip>
47+
<Country>USA</Country>
48+
</Address>
49+
<Address Type="Billing">
50+
<Name>Cristian Osorio</Name>
51+
<Street>456 Main Street</Street>
52+
<City>Buffalo</City>
53+
<State>NY</State>
54+
<Zip>98112</Zip>
55+
<Country>USA</Country>
56+
</Address>
57+
<DeliveryNotes>Please notify me before shipping.</DeliveryNotes>
58+
<Items>
59+
<Item PartNumber="456-NM">
60+
<ProductName>Power Supply</ProductName>
61+
<Quantity>1</Quantity>
62+
<USPrice>45.99</USPrice>
63+
</Item>
64+
</Items>
65+
</PurchaseOrder>
66+
<PurchaseOrder PurchaseOrderNumber="99504" OrderDate="1999-10-22">
67+
<!-- This is the last purchase order -->
68+
<Address Type="Shipping">
69+
<Name>Jessica Arnold</Name>
70+
<Street>4055 Madison Ave</Street>
71+
<City>Seattle</City>
72+
<State>WA</State>
73+
<Zip>98112</Zip>
74+
<Country>USA</Country>
75+
</Address>
76+
<Address Type="Billing">
77+
<Name>Jessica Arnold</Name>
78+
<Street>4055 Madison Ave</Street>
79+
<City>Buffalo</City>
80+
<State>NY</State>
81+
<Zip>98112</Zip>
82+
<Country>USA</Country>
83+
</Address>
84+
<Items>
85+
<Item PartNumber="898-AZ">
86+
<ProductName>Computer Keyboard</ProductName>
87+
<Quantity>1</Quantity>
88+
<USPrice>29.99</USPrice>
89+
</Item>
90+
<Item PartNumber="898-AM">
91+
<ProductName>Wireless Mouse</ProductName>
92+
<Quantity>1</Quantity>
93+
<USPrice>14.99</USPrice>
94+
</Item>
95+
</Items>
96+
</PurchaseOrder>
97+
</PurchaseOrders>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Xml.Linq;
2+
using BenchmarkDotNet.Attributes;
3+
using DevDecoder.DynamicXml.Benchmarks;
4+
5+
public class Search
6+
{
7+
public static IEnumerable<XDocument> Documents => XmlArticles.AllDocuments();
8+
9+
[ParamsSource(nameof(Documents))]
10+
public XDocument Document;
11+
12+
private XElement _target;
13+
14+
[IterationSetup]
15+
public void PickElement()
16+
{
17+
_target = Document.RandomElement();
18+
Console.WriteLine($"Picked Target: {_target.Describe()}");
19+
}
20+
21+
[IterationSetup(Target = nameof(FindByName))]
22+
public void GetNames()
23+
{
24+
var names = new List<XName>();
25+
var current = _target;
26+
while (current is not null)
27+
{
28+
names.Add(current.Name);
29+
current = current.Parent;
30+
}
31+
32+
names.Reverse();
33+
_names = names.ToArray();
34+
}
35+
36+
private IEnumerable<XName> _names;
37+
[Benchmark]
38+
public XElement FindByName() => _names.Aggregate(Document.Root, (current1, name) => current1!.Element(name))!;
39+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System.Diagnostics;
2+
using System.Reflection;
3+
using System.Xml.Linq;
4+
5+
namespace DevDecoder.DynamicXml.Benchmarks;
6+
7+
public static class XmlArticles
8+
{
9+
public static readonly XDocument EmptyRoot = new XDocument(new XElement("Root"));
10+
public static readonly XDocument Simple = XDocument.Parse("<Root><Node attribute=\"value\"/></Root>");
11+
public static XDocument Sample => LazySample.Value;
12+
public static XDocument Large => LazyLarge.Value;
13+
14+
public static IEnumerable<XDocument> AllDocuments()
15+
{
16+
yield return EmptyRoot;
17+
yield return Simple;
18+
yield return Sample;
19+
yield return Large;
20+
}
21+
22+
private static readonly Lazy<XDocument> LazySample =
23+
new Lazy<XDocument>(() =>
24+
{
25+
using var stream = Assembly.GetExecutingAssembly()
26+
.GetManifestResourceStream($"{typeof(XmlArticles).Namespace}.Sample.xml")!;
27+
return XDocument.Load(stream);
28+
}, LazyThreadSafetyMode.ExecutionAndPublication);
29+
30+
31+
private static readonly Lazy<XDocument> LazyLarge =
32+
new Lazy<XDocument>(() =>
33+
{
34+
// Total nodes = (width^(depth - 1)) + (width^(depth - 2)) + (width^(depth - 3)) + ... + (width^0))
35+
// = 2,097,152 + 262,144 + 32,768 + 4,096 + 512 + 64 + 8 + 1
36+
// = 2,396,745 nodes
37+
const int width = 8; // 26 is maximum width (as converted to ASCII 'z').
38+
const int depth = 8;
39+
40+
var xDoc = new XDocument();
41+
42+
// Build large Xml document
43+
var current = new XElement("X");
44+
xDoc.Add(current);
45+
var count = 1;
46+
var stopwatch = Stopwatch.StartNew();
47+
while (true)
48+
{
49+
int childCount;
50+
if (current.Name.LocalName.Length >= depth || (childCount = current.Elements().Count()) >= width)
51+
{
52+
current = current.Parent;
53+
if (current is null) break;
54+
continue;
55+
}
56+
57+
var el = new XElement(XName.Get(current.Name.LocalName + (char) (97 + childCount),
58+
current.Name.NamespaceName));
59+
current.Add(el);
60+
count++;
61+
current = el;
62+
}
63+
64+
Console.WriteLine($"{count} nodes in {stopwatch.ElapsedMilliseconds}ms");
65+
return xDoc;
66+
}, LazyThreadSafetyMode.ExecutionAndPublication);
67+
68+
public static XObject RandomObject(this XDocument document, Random? random = null)
69+
{
70+
random ??= new Random();
71+
var node = RandomElement(document.DescendantNodes(), random);
72+
switch (node)
73+
{
74+
case XElement xElement:
75+
var attributeCount = xElement.Attributes().Count();
76+
if (attributeCount > 0 && random.Next(2) > 0)
77+
{
78+
return xElement.Attributes().ElementAt(random.Next(attributeCount));
79+
}
80+
81+
return xElement;
82+
default:
83+
return node;
84+
}
85+
}
86+
87+
public static XNode RandomNode(this XDocument document, Random? random = null) => RandomElement(document.DescendantNodes(), random);
88+
89+
public static XElement RandomElement(this XDocument document, Random? random = null) => RandomElement(document.Descendants(), random);
90+
91+
public static T RandomElement<T>(this IEnumerable<T> enumerable, Random? random = null)
92+
{
93+
// ReSharper disable PossibleMultipleEnumeration
94+
var count = enumerable.Count();
95+
if (count < 1) throw new ArgumentException("No items in enumeration", nameof(enumerable));
96+
random ??= new Random();
97+
return enumerable.ElementAt(random.Next(count));
98+
// ReSharper restore PossibleMultipleEnumeration
99+
}
100+
101+
public static string Describe(this XObject xObject) => xObject switch
102+
{
103+
XAttribute attribute => $"{attribute.Name}=\"{attribute.Value}\"",
104+
XComment comment => $"<!--{comment.Value}-->",
105+
XDocument document => "<DOCUMENT>",
106+
XElement element => $"<{element.Name}>",
107+
XProcessingInstruction processingInstruction => $"<?{processingInstruction.Target}>",
108+
XText text => text.Value,
109+
XDocumentType documentType => $"<!{documentType.Name}>",
110+
_ => $"<{xObject.GetType()}>"
111+
};
112+
}

DevDecoder.DynamicXml.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Non-project", "Non-project"
99
README.md = README.md
1010
EndProjectSection
1111
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevDecoder.DynamicXml.Benchmarks", "DevDecoder.DynamicXml.Benchmarks\DevDecoder.DynamicXml.Benchmarks.csproj", "{6EAD8BDE-73B0-401C-AAAC-A1F8E3FC0BED}"
13+
EndProject
1214
Global
1315
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1416
Debug|Any CPU = Debug|Any CPU
@@ -23,5 +25,9 @@ Global
2325
{12B3491E-16C1-4F9F-A312-9870770A2669}.Debug|Any CPU.Build.0 = Debug|Any CPU
2426
{12B3491E-16C1-4F9F-A312-9870770A2669}.Release|Any CPU.ActiveCfg = Release|Any CPU
2527
{12B3491E-16C1-4F9F-A312-9870770A2669}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{6EAD8BDE-73B0-401C-AAAC-A1F8E3FC0BED}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{6EAD8BDE-73B0-401C-AAAC-A1F8E3FC0BED}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{6EAD8BDE-73B0-401C-AAAC-A1F8E3FC0BED}.Debug|Any CPU.ActiveCfg = Release|Any CPU
31+
{6EAD8BDE-73B0-401C-AAAC-A1F8E3FC0BED}.Debug|Any CPU.Build.0 = Release|Any CPU
2632
EndGlobalSection
2733
EndGlobal

0 commit comments

Comments
 (0)