Search code examples
c#stringperformanceresharperstring-interpolation

String Interpolation vs String.Format


Is there a noticeable performance difference between using string interpolation:

myString += $"{x:x2}";

vs String.Format()?

myString += String.Format("{0:x2}", x);

I am only asking because ReSharper is prompting the fix, and I have been fooled before.


Solution

  • The answer is both yes and no. ReSharper is fooling you by not showing a third variant, which is also the most performant. The two listed variants produce equal IL code, but the following will indeed give a boost:

    myString += $"{x.ToString("x2")}";
    

    Full test code

    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Configs;
    using BenchmarkDotNet.Diagnosers;
    using BenchmarkDotNet.Diagnostics.Windows;
    using BenchmarkDotNet.Running;
    
    namespace StringFormatPerformanceTest
    {
        [Config(typeof(Config))]
        public class StringTests
        {
            private class Config : ManualConfig
            {
                public Config() => AddDiagnoser(MemoryDiagnoser.Default, new EtwProfiler());
            }
    
            [Params(42, 1337)]
            public int Data;
    
            [Benchmark] public string Format() => string.Format("{0:x2}", Data);
            [Benchmark] public string Interpolate() => $"{Data:x2}";
            [Benchmark] public string InterpolateExplicit() => $"{Data.ToString("x2")}";
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var summary = BenchmarkRunner.Run<StringTests>();
            }
        }
    }
    

    Test results

    |              Method | Data |      Mean |  Gen 0 | Allocated |
    |-------------------- |----- |----------:|-------:|----------:|
    |              Format |   42 | 118.03 ns | 0.0178 |      56 B |
    |         Interpolate |   42 | 118.36 ns | 0.0178 |      56 B |
    | InterpolateExplicit |   42 |  37.01 ns | 0.0102 |      32 B |
    |              Format | 1337 | 117.46 ns | 0.0176 |      56 B |
    |         Interpolate | 1337 | 113.86 ns | 0.0178 |      56 B |
    | InterpolateExplicit | 1337 |  38.73 ns | 0.0102 |      32 B |
    

    New test results (.NET 6)

    Re-ran the test on .NET 6.0.9.41905, X64 RyuJIT AVX2.

    |              Method | Data |      Mean |   Gen0 | Allocated |
    |-------------------- |----- |----------:|-------:|----------:|
    |              Format |   42 |  37.47 ns | 0.0089 |      56 B |
    |         Interpolate |   42 |  57.61 ns | 0.0050 |      32 B |
    | InterpolateExplicit |   42 |  11.46 ns | 0.0051 |      32 B |
    |              Format | 1337 |  39.49 ns | 0.0089 |      56 B |
    |         Interpolate | 1337 |  59.98 ns | 0.0050 |      32 B |
    | InterpolateExplicit | 1337 |  12.85 ns | 0.0051 |      32 B |
    

    The InterpolateExplicit() method is faster since we now explicitly tell the compiler to use a string. No need to box the object to be formatted. Boxing is indeed very costly. Also note that NET 6 reduced both CPU and memory allocations - for all methods.

    New test results (.NET 7)

    Re-ran the test on .NET 7.0.122.56804, X64 RyuJIT AVX2.

    |              Method | Data |      Mean |   Gen0 | Allocated |
    |-------------------- |----- |----------:|-------:|----------:|
    |              Format |   42 |  41.04 ns | 0.0089 |      56 B |
    |         Interpolate |   42 |  65.82 ns | 0.0050 |      32 B |
    | InterpolateExplicit |   42 |  12.19 ns | 0.0051 |      32 B |
    |              Format | 1337 |  41.02 ns | 0.0089 |      56 B |
    |         Interpolate | 1337 |  59.61 ns | 0.0050 |      32 B |
    | InterpolateExplicit | 1337 |  13.28 ns | 0.0051 |      32 B |
    

    No significant changes since .NET 6.