.net core BenchmarkDotNet StringConcats vs StringFormat

.net core BenchmarkDotNet StringConcats vs StringFormat,.net-core,benchmarking,benchmarkdotnet,.net Core,Benchmarking,Benchmarkdotnet,我正在windows 10 64位操作系统上使用.net core 2.0.5。每次,我都会使用string.Concat或string.Format来表示字符串,因为我学会了使用字符串“abc”+“def”的性能更差。(内存使用是另一个话题。)-我最近对字符串concates进行了基准测试,但结果非常有趣 using System; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes.Columns; usi

我正在windows 10 64位操作系统上使用.net core 2.0.5。每次,我都会使用string.Concat或string.Format来表示字符串,因为我学会了使用字符串“abc”+“def”的性能更差。(内存使用是另一个话题。)-我最近对字符串concates进行了基准测试,但结果非常有趣


using System;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;

namespace TryBenchmark.ConsoleApp.Benchmarks
{
    [RPlotExporter, RankColumn]
    public class StringConcatVsStringFormat
    {
        [Benchmark]
        public void StringConcat001()
        {
            string result = "test1 " + "test2 " + "test3";

            if (result != "test1 test2 test3")
            {
                throw new InvalidOperationException("Tests are faulty !");
            }
        }

        [Benchmark]
        public void StringConcat002()
        {
            string result = String.Concat("test1 ", "test2 ", "test3");

            if (result != "test1 test2 test3")
            {
                throw new InvalidOperationException("Tests are faulty !");
            }
        }

        [Benchmark]
        public void StringConcat003()
        {
            string name1 = "test1";
            string name2 = "test2";
            string name3 = "test3";

            string result = $"{name1} {name2} {name3}";

            if (result != "test1 test2 test3")
            {
                throw new InvalidOperationException("Tests are faulty !");
            }
        }

        [Benchmark]
        public void StringFormat()
        {
            string result = String.Format("{0} {1} {2}", "test1", "test2", "test3");

            if (result != "test1 test2 test3")
            {
                throw new InvalidOperationException("Tests are faulty !");
            }
        }
    }
}

结果


StringConcatVsStringFormat.StringFormat: DefaultJob
Runtime = .NET Core 2.0.5 (CoreCLR 4.6.26020.03, CoreFX 4.6.26018.01), 64bit RyuJIT; GC = Concurrent Workstation
Mean = 149.2378 ns, StdErr = 0.9310 ns (0.62%); N = 79, StdDev = 8.2749 ns
Min = 140.0365 ns, Q1 = 143.8430 ns, Median = 145.9400 ns, Q3 = 150.4091 ns, Max = 172.1811 ns
IQR = 6.5661 ns, LowerFence = 133.9939 ns, UpperFence = 160.2582 ns
ConfidenceInterval = [146.0541 ns; 152.4215 ns] (CI 99.9%), Margin = 3.1837 ns (2.13% of Mean)
Skewness = 1.29, Kurtosis = 3.57, MValue = 2
-------------------- Histogram --------------------
[139.525 ns ; 142.434 ns) | @@@@@@@@@@@
[142.434 ns ; 145.809 ns) | @@@@@@@@@@@@@@@@@@@@@@@@@@@@
[145.809 ns ; 150.658 ns) | @@@@@@@@@@@@@@@@@@@@@@
[150.658 ns ; 156.460 ns) | @@@@
[156.460 ns ; 160.840 ns) | @
[160.840 ns ; 164.215 ns) | @@@@@@@
[164.215 ns ; 169.531 ns) | @@@@
[169.531 ns ; 173.869 ns) | @@
---------------------------------------------------

Total time: 00:03:26 (206.65 sec)

// * Summary *

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.16299.248 (1709/FallCreatorsUpdate/Redstone3)
Intel Core i7-7500U CPU 2.70GHz (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
Frequency=2835939 Hz, Resolution=352.6169 ns, Timer=TSC
.NET Core SDK=2.1.100
  [Host]     : .NET Core 2.0.5 (CoreCLR 4.6.26020.03, CoreFX 4.6.26018.01), 64bit RyuJIT  [AttachedDebugger]
  DefaultJob : .NET Core 2.0.5 (CoreCLR 4.6.26020.03, CoreFX 4.6.26018.01), 64bit RyuJIT


          Method |       Mean |     Error |    StdDev |      Median | Rank |
---------------- |-----------:|----------:|----------:|------------:|-----:|
 StringConcat001 |   1.043 ns | 0.0608 ns | 0.1558 ns |   0.9972 ns |    1 |
 StringConcat002 |  26.680 ns | 0.5303 ns | 0.5445 ns |  26.7079 ns |    2 |
 StringConcat003 | 143.028 ns | 2.4180 ns | 2.2618 ns | 143.9356 ns |    3 |
    StringFormat | 149.238 ns | 3.1837 ns | 8.2749 ns | 145.9400 ns |    4 |

// * Warnings *
Environment
  Summary -> Benchmark was executed with attached debugger
StringConcat001有多快?
我做错什么了吗?或者我错误地配置了benchmarkdotnet?

C#编译器足够聪明,可以检测
string result=“test1”+“test2”+“test3”的值是一个常量

您可以使用ILSpy查看编译器生成的内容:

[Benchmark]
public void StringConcat001()
{
    if (!("test1 test2 test3" != "test1 test2 test3"))
    {
        return;
    }
    throw new InvalidOperationException("Tests are faulty !");
}
要欺骗编译器,您必须将这些值放入公共的非只读字段,或者将它们用作给定方法的参数。BenchmarkDotNet
0.10.14
允许您提供以下值:

在我的电脑上显示以下结果:

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.16299.309 (1709/FallCreatorsUpdate/Redstone3)
Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores
Frequency=3507503 Hz, Resolution=285.1031 ns, Timer=TSC
  [Host]     : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2633.0
  DefaultJob : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2633.0


          Method |   arg1 |   arg2 |  arg3 |      Mean |     Error |    StdDev | Rank |  Gen 0 | Allocated |
---------------- |------- |------- |------ |----------:|----------:|----------:|-----:|-------:|----------:|
 StringConcat001 | test1  | test2  | test3 |  25.95 ns | 0.1755 ns | 0.1642 ns |    2 | 0.0091 |      48 B |
 StringConcat002 | test1  | test2  | test3 |  25.66 ns | 0.3480 ns | 0.3085 ns |    1 | 0.0091 |      48 B |
 StringConcat003 | test1  | test2  | test3 | 112.32 ns | 0.9539 ns | 0.8923 ns |    4 | 0.0098 |      52 B |
    StringFormat | test1  | test2  | test3 | 111.62 ns | 0.9982 ns | 0.8849 ns |    3 | 0.0098 |      52 B |
C#编译器足够聪明,可以检测
string result=“test1”+“test2”+“test3”的值是一个常量

您可以使用ILSpy查看编译器生成的内容:

[Benchmark]
public void StringConcat001()
{
    if (!("test1 test2 test3" != "test1 test2 test3"))
    {
        return;
    }
    throw new InvalidOperationException("Tests are faulty !");
}
要欺骗编译器,您必须将这些值放入公共的非只读字段,或者将它们用作给定方法的参数。BenchmarkDotNet
0.10.14
允许您提供以下值:

在我的电脑上显示以下结果:

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.16299.309 (1709/FallCreatorsUpdate/Redstone3)
Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores
Frequency=3507503 Hz, Resolution=285.1031 ns, Timer=TSC
  [Host]     : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2633.0
  DefaultJob : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2633.0


          Method |   arg1 |   arg2 |  arg3 |      Mean |     Error |    StdDev | Rank |  Gen 0 | Allocated |
---------------- |------- |------- |------ |----------:|----------:|----------:|-----:|-------:|----------:|
 StringConcat001 | test1  | test2  | test3 |  25.95 ns | 0.1755 ns | 0.1642 ns |    2 | 0.0091 |      48 B |
 StringConcat002 | test1  | test2  | test3 |  25.66 ns | 0.3480 ns | 0.3085 ns |    1 | 0.0091 |      48 B |
 StringConcat003 | test1  | test2  | test3 | 112.32 ns | 0.9539 ns | 0.8923 ns |    4 | 0.0098 |      52 B |
    StringFormat | test1  | test2  | test3 | 111.62 ns | 0.9982 ns | 0.8849 ns |    3 | 0.0098 |      52 B |

您可能会受到死代码消除的影响。看看使方法返回在方法中计算的值。也要去掉异常测试,它们只是在微基准测试中令人困惑。这是在C#编译器本身中进行微优化的。用于将运算符+()调用序列转换为相应的字符串。Concat()重载。因为您使用的是字符串文字,所以它甚至知道如何在编译时连接。使用ildasm.exe查看生成的msil时非常明显。Eric Lippert对此进行了研究。@omajid感谢您的评论,但如果我返回字符串并删除抛出异常,我会看到类似的结果。您可能会受到死代码消除的影响。看看使方法返回在方法中计算的值。也要去掉异常测试,它们只是在微基准测试中令人困惑。这是在C#编译器本身中进行微优化的。用于将运算符+()调用序列转换为相应的字符串。Concat()重载。因为您使用的是字符串文字,所以它甚至知道如何在编译时连接。使用ildasm.exe查看生成的msil时非常明显。Eric Lippert对此进行了研究。@omajid感谢您的评论,但如果我返回字符串并删除抛出异常,我会看到类似的结果。谢谢您的回答。非常有趣的是,编译器看到常量值并对其进行优化。很好。我学到了一些新东西,谢谢。但是,StringConcat001仍然是最快的(几乎)。但这怎么可能呢?我认为带“+”号的concating字符串应该是最慢的。因为“.Concat()”和“.Format()”方法和已为此目的编写(concating)。我错了吗?
String.Format
必须解析字符串,然后将其连接起来。这就是concat速度更快的原因。如果比较IL代码,您将看到
StringConcat001
StringConcat002
的IL是相同的。这同样适用于
StringConcat003
StringFormat
。您应该尝试比较concat与stringbuilder和缓存的stringbuilder。可以找到字符串生成器缓存的示例代码。如果你想了解更多关于字符串操作的信息,你应该从CoreCLR repo中查看。谢谢你的回答。非常有趣的是,编译器看到常量值并对其进行优化。很好。我学到了一些新东西,谢谢。但是,StringConcat001仍然是最快的(几乎)。但这怎么可能呢?我认为带“+”号的concating字符串应该是最慢的。因为“.Concat()”和“.Format()”方法和已为此目的编写(concating)。我错了吗?
String.Format
必须解析字符串,然后将其连接起来。这就是concat速度更快的原因。如果比较IL代码,您将看到
StringConcat001
StringConcat002
的IL是相同的。这同样适用于
StringConcat003
StringFormat
。您应该尝试比较concat与stringbuilder和缓存的stringbuilder。可以找到字符串生成器缓存的示例代码。如果您想了解有关字符串操作的更多信息,您应该从CoreCLR repo中进行检查。