Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Spanify AntiFormat function in AvTrace, decrease allocations, improve perf #9361

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

h3xds1nz
Copy link
Contributor

@h3xds1nz h3xds1nz commented Jul 5, 2024

Description

Improves performance of the AntiFormat function in AvTrace, reducing allocations and improving the overall performance up to 2 times in some cases. Also changes FormatChars from char[] to ReadOnlySpan<char>, occurrence I've missed in #9230.

Second commit features added documentation for the function. I've included some basic asserts at the Testing section.

Small strings within StringBuilder default capacity

Method Mean [ns] Error [ns] StdDev [ns] Gen0 Allocated [B]
PR__Edit 236.1 ns 4.75 ns 5.84 ns 0.0730 1224 B
Original 400.7 ns 8.04 ns 15.29 ns 0.1040 1744 B
Benchmark code
AntiFormat2("{aaaa}");
AntiFormat2("{aaaa}}");
AntiFormat2("{{aaaa}}");
AntiFormat2("{aa{{aa}}");
AntiFormat2("{aa{{aa}}ab}c}d");
AntiFormat2("aaaa}");
AntiFormat2("aaaa");
AntiFormat2("a{{{aaa");
AntiFormat2("a{{{aaa}}}");

Large strings, mostly without any replacements

Method Mean [ns] Error [ns] StdDev [ns] Gen0 Allocated [B]
PR__Edit 127.5 ns 1.29 ns 1.08 ns 0.0880 1472 B
Original 242.9 ns 4.87 ns 5.00 ns 0.1094 1832 B
Benchmark code
AntiFormat("aaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
AntiFormat("aaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
AntiFormat("aaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxa");
AntiFormat("aaaaxxxc ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc");
AntiFormat("aaaaabcd}yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy");
AntiFormat("aaaa}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
AntiFormat("aaaa}6666666666666666666666666666666666666666666666666666666666666");
AntiFormat("aaaa7777777777777777777777777777777777777777777777777777777777777");
AntiFormat("aaaaggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg");

Large strings, with replacements (up to two times the perf)

Method Mean [ns] Error [ns] StdDev [ns] Gen0 Allocated [B]
PR__Edit 394.7 ns 7.73 ns 10.05 ns 0.3271 5472 B
Original 798.9 ns 11.30 ns 10.57 ns 0.4425 7408 B
Benchmark code
AntiFormat("aaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}");
AntiFormat("aaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}");
AntiFormat("aaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxa}");
AntiFormat("aaaaxxxc ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}");
AntiFormat("aaaaabcd}yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy");
AntiFormat("aaaa}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
AntiFormat("aaaa}6666666666666666666666666666666666666666666666666666666666666");
AntiFormat("aaaa7777777777777777777777777777777777777777777777777777777777777{");
AntiFormat("aaaaggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg}");
AntiFormat("aaaa77777777777777777777777777{}777777777777777777777777777777777{");
AntiFormat("aaaaggggggggggggggggggggggggggggggggggggggggggggggggg{gggggggggggggg}");

Customer Impact

Improved performance, decreased allocations.

Regression

No.

Testing

Local build, base testing between the two functions.

Assert.AreEqual(AntiFormat("{aaaa}}"), AntiFormat2("{aaaa}}"));
Assert.AreEqual(AntiFormat("{aa{{aa}}"), AntiFormat2("{aa{{aa}}"));
Assert.AreEqual(AntiFormat("{aa{{aa}}ab}c}d"), AntiFormat2("{aa{{aa}}ab}c}d"));
Assert.AreEqual(AntiFormat("aaaa}"), AntiFormat2("aaaa}"));
Assert.AreEqual(AntiFormat("aaaa"), AntiFormat2("aaaa"));
Assert.AreEqual(AntiFormat("a{{{aaa"), AntiFormat2("a{{{aaa"));
Assert.AreEqual(AntiFormat("a{{{aaa}}}"), AntiFormat2("a{{{aaa}}}"));
Assert.AreEqual(AntiFormat("{aaaa}"), AntiFormat2("{aaaa}"));
Assert.AreEqual(AntiFormat("{{aaaa}}"), AntiFormat2("{{aaaa}}"));
Assert.AreEqual(AntiFormat("{{{}aaaa}}"), AntiFormat2("{{{}aaaa}}"));
Assert.AreEqual(AntiFormat(string.Empty), AntiFormat2(string.Empty));
Assert.AreEqual(AntiFormat("a{{{}aaaa}}"), AntiFormat2("a{{{}aaaa}}"));
Assert.AreEqual(AntiFormat("a}aaaa}"), AntiFormat2("a}aaaa}"));
Assert.AreEqual(AntiFormat("}aaaa}"), AntiFormat2("}aaaa}"));

Risk

Low.

Microsoft Reviewers: Open in CodeFlow

@h3xds1nz h3xds1nz requested review from a team as code owners July 5, 2024 19:53
@dotnet-policy-service dotnet-policy-service bot added PR metadata: Label to tag PRs, to facilitate with triage Community Contribution A label for all community Contributions labels Jul 5, 2024
@h3xds1nz h3xds1nz changed the title [AvTrace.cs] Use ReadOnlySpan<char>/Slice instead of String/Substring in AntiFormat Spanify AntiFormat function in AvTrace, decrease allocations, improve perf Sep 1, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Community Contribution A label for all community Contributions Included in test pass PR metadata: Label to tag PRs, to facilitate with triage Status:Completed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants