Given an existing long-ish UTF8 string, add a newline char and turn it into a byte array (eg. for sending over a socket).
I added some UTF16 characters, so the string length is 500 but the resulting byte length is 606.
Inspired by https://dotnetcoretutorials.com/2020/02/06/performance-of-string-concatenation-in-c/
Results:
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1526 (21H2)
Intel Core i7-7820X CPU 3.60GHz (Kaby Lake), 1 CPU, 16 logical and 8 physical cores
.NET SDK=5.0.404
[Host] : .NET 5.0.13 (5.0.1321.56516), X64 RyuJIT
DefaultJob : .NET 5.0.13 (5.0.1321.56516), X64 RyuJIT
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated |
---|---|---|---|---|---|---|
BlockCopyToNewArray | 265.7 ns | 1.96 ns | 1.74 ns | 0.2360 | 0.0005 | 1 KB |
MemcpyToNewArray | 277.6 ns | 1.43 ns | 1.27 ns | 0.2360 | 0.0005 | 1 KB |
PlusAppendToString | 304.4 ns | 2.62 ns | 2.45 ns | 0.3042 | 0.0014 | 2 KB |
StringConcat | 303.2 ns | 2.13 ns | 1.99 ns | 0.3042 | 0.0014 | 2 KB |
StringBulderAppend | 461.2 ns | 1.67 ns | 1.39 ns | 0.6771 | 0.0067 | 4 KB |
StringBulderAppendLine | 491.1 ns | 5.02 ns | 4.69 ns | 0.6905 | 0.0067 | 4 KB |
Code:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Runtime.InteropServices;
using System.Text;
[MemoryDiagnoser]
public class Bench
{
public volatile string input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut " +
"labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " +
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " +
"Excepteur sint occaecat cupidatat proident. " +
"Разнообразный и богатый опыт консультация с широким активом в значительной степени обуславливает создание форм развития.";
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr memcpy(IntPtr dest, IntPtr src, int count);
[Benchmark]
public byte[] BlockCopyToNewArray()
{
byte[] msgbytes = new UTF8Encoding().GetBytes(input);
int len = msgbytes.Length;
byte[] bytes = new byte[len + 1];
Buffer.BlockCopy(msgbytes, 0, bytes, 0, len);
bytes[len] = 10;
return bytes;
}
[Benchmark]
unsafe public byte[] MemcpyToNewArray()
{
byte[] msgbytes = new UTF8Encoding().GetBytes(input);
int len = msgbytes.Length;
byte[] bytes = new byte[len + 1];
fixed (byte* pDest = bytes)
fixed (byte* pSrc = msgbytes) {
IntPtr ipSrc = (IntPtr)pSrc;
IntPtr ipDest = (IntPtr)pDest;
memcpy(ipDest, ipSrc, len);
}
bytes[len] = 10;
return bytes;
}
[Benchmark]
public byte[] PlusAppendToString() =>new UTF8Encoding().GetBytes(input + "\n");
[Benchmark]
public byte[] StringConcat() => new UTF8Encoding().GetBytes(string.Concat(input, "\n"));
[Benchmark]
public byte[] StringBulderAppend()
{
string line = new StringBuilder(input).Append('\n').ToString();
return new UTF8Encoding().GetBytes(line);
}
[Benchmark]
public byte[] StringBulderAppendLine()
{
var line = new StringBuilder().AppendLine(input).ToString();
return new UTF8Encoding().GetBytes(line);
}
}
class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Bench>();
var encoding = new UTF8Encoding();
Console.OutputEncoding = encoding;
Bench bench = new();
byte[] result = bench.BlockCopyToNewArray();
Console.WriteLine($"\n\nInput:\n{bench.input}\n");
Console.WriteLine($"Output:\n{encoding.GetString(result)}\n");
Console.WriteLine($"String len: {bench.input.Length}; Bytes len: {result.Length}");
}
}