-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
So i got nerd sniped on IRC with how fast you can append characters to create a string.
I created the following benchmark to measure a few alternatives just before i got told it's for an old MVC4 app. Then I've noticed that the performance is consistently worse on .NET Core, in particular for StringBuilder
and even more so if you supply a capacity
for StringBuilder
. I then suspected Tiered Compilation but i couldn't figure how to really disable it for BDN.NET, <TieredCompilation>false</TieredCompilation>
had at best an effect on the InProcess
Benchmark. Also it appears that BDN.NET or the API it is using for measuring the Allocated
number is incorrect on .NET Core, the .NET Framework numbers seem more plausible (allocating an char[]
of 50k elements and a string
from that char[]
certainly doesn't allocate exactly as much as just allocating the string
).
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text;
namespace ConsoleApp37
{
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<ConcatRace>();
}
}
//[InProcess]
[MemoryDiagnoser]
public class ConcatRace
{
const int length = 50000;
[Benchmark]
public string StringBuilderAddChar()
{
var result = new StringBuilder();
for (int i = 0; i < length; i++)
{
result.Append('s');
}
return result.ToString();
}
[Benchmark]
public string StringBuilderAddCharOptimized()
{
var result = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
result.Append('s');
}
return result.ToString();
}
[Benchmark]
public unsafe string PointerAddChar()
{
var result = new char[length];
fixed (char* fixedPointer = result)
{
var pointer = fixedPointer;
for (int i = 0; i < length; i++)
{
*pointer++ = 's';
}
}
return new string(result);
}
[Benchmark]
public unsafe string NormalAddChar()
{
var result = new char[length];
for (int i = 0; i < result.Length; i++)
{
result[i] = 's';
}
return new string(result);
}
[Benchmark]
public unsafe string FastPointerAddChar()
{
var result = new string('\0', length);
fixed (char* fixedPointer = result)
{
var pointer = fixedPointer;
for (int i = 0; i < length; i++)
{
*pointer++ = 's';
}
}
return result;
}
}
}
.NET Framework 4.7.2
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-4670K CPU 3.40GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
[Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.8.3815.0
DefaultJob : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.8.3815.0
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
StringBuilderAddChar | 109.43 us | 0.3330 us | 0.2952 us | 62.3779 | 31.1279 | 31.1279 | 209.02 KB |
StringBuilderAddCharOptimized | 100.69 us | 0.5440 us | 0.5089 us | 62.3779 | 62.3779 | 62.3779 | 195.87 KB |
PointerAddChar | 33.79 us | 0.2182 us | 0.1934 us | 62.4390 | 62.4390 | 62.4390 | 195.37 KB |
NormalAddChar | 38.40 us | 0.1843 us | 0.1539 us | 62.4390 | 62.4390 | 62.4390 | 195.37 KB |
FastPointerAddChar | 24.62 us | 0.2241 us | 0.2096 us | 31.2195 | 31.2195 | 31.2195 | 97.69 KB |
.NET Core 3.0 RC1 (OutOfProcess)
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-4670K CPU 3.40GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=3.0.100-rc1-014190
[Host] : .NET Core 3.0.0-rc1-19456-20 (CoreCLR 4.700.19.45506, CoreFX 4.700.19.45604), 64bit RyuJIT
DefaultJob : .NET Core 3.0.0-rc1-19456-20 (CoreCLR 4.700.19.45506, CoreFX 4.700.19.45604), 64bit RyuJIT
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
StringBuilderAddChar | 120.80 us | 0.5277 us | 0.4936 us | 62.2559 | 31.0059 | 31.0059 | 208.55 KB |
StringBuilderAddCharOptimized | 142.44 us | 1.1631 us | 1.0879 us | 62.2559 | 62.2559 | 62.2559 | 97.73 KB |
PointerAddChar | 35.33 us | 0.2321 us | 0.2057 us | 62.4390 | 62.4390 | 62.4390 | 97.68 KB |
NormalAddChar | 40.27 us | 0.4038 us | 0.3777 us | 62.4390 | 62.4390 | 62.4390 | 97.68 KB |
FastPointerAddChar | 29.78 us | 0.1331 us | 0.1245 us | 31.2195 | 31.2195 | 31.2195 | 97.68 KB |
.NET Core 3.0 RC1 (InProcess)
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-4670K CPU 3.40GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=3.0.100-rc1-014190
[Host] : .NET Core 3.0.0-rc1-19456-20 (CoreCLR 4.700.19.45506, CoreFX 4.700.19.45604), 64bit RyuJIT
Job=InProcess Toolchain=InProcessEmitToolchain
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
StringBuilderAddChar | 121.68 us | 0.4015 us | 0.3352 us | 62.3779 | 31.1279 | 31.1279 | 208.55 KB |
StringBuilderAddCharOptimized | 111.83 us | 1.4062 us | 1.3154 us | 62.3779 | 62.3779 | 62.3779 | 97.73 KB |
PointerAddChar | 43.64 us | 0.5115 us | 0.4784 us | 62.4390 | 62.4390 | 62.4390 | 97.68 KB |
NormalAddChar | 48.63 us | 0.6182 us | 0.5782 us | 62.4390 | 62.4390 | 62.4390 | 97.68 KB |
FastPointerAddChar | 29.98 us | 0.3981 us | 0.3724 us | 31.2195 | 31.2195 | 31.2195 | 97.68 KB |