runtime: ValueStringBuilder is slower at appending short strings than StringBuilder
Investigating why my PR dotnet/corefx#27250 makes HtmlEncode slower, I wrote a quick benchmark (using BenchmarkDotNet) to compare StringBuilder and ValueStringBuilder:
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
public class StringBuilderBench
{
[Benchmark(Baseline = true), BenchmarkCategory("AppendChar")]
public void StringBuilder_AppendChar()
{
var builder = new StringBuilder();
for (var i = 0; i < 100000; i++)
{
builder.Append('<');
}
builder.ToString();
builder.Clear();
}
[Benchmark, BenchmarkCategory("AppendChar")]
public void ValueStringBuilder_AppendChar()
{
var builder = new ValueStringBuilder();
for (var i = 0; i < 100000; i++)
{
builder.Append('<');
}
builder.ToString();
builder.Dispose();
}
[Benchmark(Baseline = true), BenchmarkCategory("AppendString")]
public void StringBuilder_AppendString()
{
var builder = new StringBuilder();
for (var i = 0; i < 100000; i++)
{
builder.Append("<");
}
builder.ToString();
builder.Clear();
}
[Benchmark, BenchmarkCategory("AppendString")]
public void ValueStringBuilder_AppendString()
{
var builder = new ValueStringBuilder();
for (var i = 0; i < 100000; i++)
{
builder.Append("<");
}
builder.ToString();
builder.Dispose();
}
}
Here are the results:
Method | Categories | Mean | Error | StdDev | Scaled | ScaledSD |
-------------------------------- |------------- |-----------:|----------:|----------:|-------:|---------:|
StringBuilder_AppendChar | AppendChar | 430.9 us | 4.072 us | 3.610 us | 1.00 | 0.00 |
ValueStringBuilder_AppendChar | AppendChar | 359.0 us | 5.030 us | 4.459 us | 0.83 | 0.01 |
| | | | | | |
StringBuilder_AppendString | AppendString | 1,518.1 us | 17.672 us | 16.530 us | 1.00 | 0.00 |
ValueStringBuilder_AppendString | AppendString | 2,230.0 us | 31.823 us | 29.767 us | 1.47 | 0.02 |
(This is using .NET Core 2.1 RC1 on OSX.) So ValueStringBuilder is faster at appending single characters but slower at appending strings than StringBuilder.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 33 (32 by maintainers)
Commits related to this issue
- Addressed https://github.com/dotnet/corefx/issues/29921#issuecomment-394403847 — committed to gfoidl/Benchmarks by gfoidl 6 years ago
ValueStringBuilder should be as good with appending strings as it is with appending spans. You are likely just seeing a difference in fixed overhead for small strings. https://github.com/dotnet/coreclr/issues/18005 may be contributing to it as well. Make sure to pick it up when testing this.
@gfoidl please the next time post diff links instead of the direct commit as it helps us to easily spot the differences. https://help.github.com/articles/comparing-commits-across-time/
plus
Length)Without the hint to inline it is faster than the current implementation, but still slower compared to StringBuilder.
I wonder what the numbers would be like if they were both presized.
There does not seem to be anything actionable here. dotnet/corefx#27250 that this issue is a follow up on was merged with nice improvements.
👍 Until the JIT is fixed we could use the workaround, then switch back to proper code.
Normally I would do a PR, so the diff is right there. Here I don’t know if it is good to go and the code lives in no fork, the implementations are side-by-side in files. Couldn’t figure out how to compare them in github. But I could post a (file-) diff… Anyway thanks for the link – didn’t know about the /compare in the url. Next time I’ll make it better.
This looks like a workaround for JIT bug. I think we would rather want to fix the JIT bug than write unnatural code like this.
StringBuilder does not use
AggressiveInliningand it uses much more complex internal data structure than ValueStringBuilder, but it still faster? It sounds suspect to me. Also,AggressiveInliningworks great for microbenchmarks, but it hurts at scale because of it introduces code bloat that makes things slower.