C# 谁是短字符串拼接之王

在开发过程中,我们经常会遇到短字符串拼接的情况,例如需要打印状态信息,进度信息(“20/100”)等,那么多的拼接方法,哪个又好又快呢。今天写了一个benchmark看一下。

.Net测试代码

using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using LinkDotNet.StringBuilder;

public enum TestState
{
    Idle,
    Running,
    Jumping
}

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net80)]
public class IntToStringBenchmark
{
    private readonly TestState _value = TestState.Idle;
    private readonly int _maxValue = 100;

    [Benchmark(Baseline = true)]
    public string IntToString()
    {
        var val = $"{_value.ToString()}/{_maxValue.ToString()}";
        return val;
    }

    [Benchmark]
    public string IntInterpolation()
    {
        var val = $"{_value}/{_maxValue}";
        return val;
    }

    [Benchmark]
    public string IntStringBuilder()
    {
        var stringBuilder = new StringBuilder();
        stringBuilder.Append(_value);
        stringBuilder.Append("/");
        stringBuilder.Append(_maxValue);
        return stringBuilder.ToString();
    }

    [Benchmark]
    public string IntValueStringBuilder()
    {
        using ValueStringBuilder stringBuilder = new();
        stringBuilder.Append(_value);
        stringBuilder.Append("/");
        stringBuilder.Append(_maxValue);
        return stringBuilder.ToString();
    }

    [Benchmark]
    public string IntValueStringBuilderstackalloc()
    {
        using ValueStringBuilder stringBuilder = new(stackalloc char[8]);
        stringBuilder.Append(_value.ToString());
        stringBuilder.Append("/");
        stringBuilder.Append(_maxValue);
        return stringBuilder.ToString();
    }
}

测试结果

MethodMeanErrorStdDevRatioRatioSDGen0AllocatedAlloc Ratio
IntToString23.84 ns0.391 ns0.365 ns1.000.000.010264 B1.00
IntInterpolation42.59 ns0.485 ns0.453 ns1.790.030.006340 B0.62
IntStringBuilder35.15 ns0.344 ns0.305 ns1.480.030.0268168 B2.62
IntValueStringBuilder70.04 ns1.132 ns1.059 ns2.940.060.010164 B1.00
IntValueStringBuilderstackalloc69.39 ns0.459 ns0.430 ns2.910.040.010164 B1.00

结论

短字符串用StringBuilder有点多此一举了
64 B 基本上就是结果字符串的大小
测试例子中,直接使用内插字符串内存分配最少,只有40 B
所以一般情况下,直接用内插字符串最方便

看一下sharplab
sharplab
可以看到,在高版本的.Net中,内插字符串会转化为DefaultInterpolatedStringHandler,这是一个结构体处理器。

而在内插字符串中执行ToString()会导致退化成string.Concat()。

Unity测试代码

using System.Text;
using Cysharp.Text;
using NUnit.Framework;
using Unity.PerformanceTesting;
 
 public enum TestState
 {
     Idle,
     Running,
     Jumping
 }

 public class IntToStringBenchmark
 {
     private readonly TestState _value = TestState.Idle;
     private readonly int _maxValue = 100;

     [Test, Performance]
     public void IntToString()
     {
         Measure.Method(() =>
         {
             var val = $"{_value.ToString()}/{_maxValue.ToString()}";
             _ = val; // 防止编译器优化掉结果
         })
         .WarmupCount(10)
         .MeasurementCount(100)
         .GC()
         .Run();
     }

     [Test, Performance]
     public void IntInterpolation()
     {
         Measure.Method(() =>
         {
             var val = $"{_value}/{_maxValue}";
             _ = val;
         })
         .WarmupCount(10)
         .MeasurementCount(100)
         .GC()
         .Run();
     }

     [Test, Performance]
     public void IntStringBuilder()
     {
         Measure.Method(() =>
         {
             var stringBuilder = new StringBuilder();
             stringBuilder.Append(_value.ToString());
             stringBuilder.Append("/");
             stringBuilder.Append(_maxValue);
             var val = stringBuilder.ToString();
             _ = val;
         })
         .WarmupCount(10)
         .MeasurementCount(100)
         .GC()
         .Run();
     }

     [Test, Performance]
     public void IntStringZString()
     {
         Measure.Method(() =>
         {
             using var stringBuilder = ZString.CreateStringBuilder(true);
             stringBuilder.Append(_value.ToString());
             stringBuilder.Append("/");
             stringBuilder.Append(_maxValue);
             var val = stringBuilder.ToString();
             _ = val;
         })
         .WarmupCount(10)
         .MeasurementCount(100)
         .GC()
         .Run();

     }
 }

测试结果

IntInterpolation
Time.GC() in Undefineds
Min: 4.00
Median: 4.00
Max: 11.00
Avg: 4.20
StdDev: 0.77
SampleCount: 110
Sum: 462.00

Time in Milliseconds
Min: 0.02 ms
Median: 0.02 ms
Max: 0.03 ms
Avg: 0.02 ms
StdDev: 0.00 ms
SampleCount: 100
Sum: 2.17 ms

IntStringBuilder

Time.GC() in Undefineds
Min: 6.00
Median: 6.00
Max: 9.00
Avg: 6.10
StdDev: 0.45
SampleCount: 110
Sum: 671.00

Time in Milliseconds
Min: 0.02 ms
Median: 0.02 ms
Max: 0.04 ms
Avg: 0.02 ms
StdDev: 0.00 ms
SampleCount: 100
Sum: 2.23 ms

IntStringZString

Time.GC() in Undefineds
Min: 3.00
Median: 3.00
Max: 14.00
Avg: 3.11
StdDev: 1.05
SampleCount: 110
Sum: 342.00

Time in Milliseconds
Min: 0.02 ms
Median: 0.02 ms
Max: 0.03 ms
Avg: 0.02 ms
StdDev: 0.00 ms
SampleCount: 100
Sum: 1.96 ms

IntToString

Time.GC() in Undefineds
Min: 4.00
Median: 4.00
Max: 7.00
Avg: 4.05
StdDev: 0.31
SampleCount: 110
Sum: 445.00

Time in Milliseconds
Min: 0.01 ms
Median: 0.02 ms
Max: 0.03 ms
Avg: 0.02 ms
StdDev: 0.00 ms
SampleCount: 100
Sum: 1.80 ms

结论

内插字符串在内存分配和速度上的表现依然很好。
ZString库虽然慢了一丢丢,但是内存分配少了很多。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值