简体   繁体   English

Memory 使用插值与“+”运算符连接字符串

[英]Memory usage of concatenating strings using interpolated vs "+" operator

I see the benefit of using interpolated strings, in terms of readability:在可读性方面,我看到了使用插值字符串的好处:

string myString = $"Hello { person.FirstName } { person.LastName }!"

over a concatenation done this way:通过这种方式进行的连接:

string myString = "Hello " + person.FirstName + " " person.LastName + "!";

The author of this video tutorial claims that the first one makes better use of memory.视频教程的作者声称第一个更好地利用了 memory。

How come?怎么来的?

Here is benchmarks. 这是基准。 String concatenation is a little faster for a very small number of arguments, but requires more memory. 字符串连接对于极少数的参数来说要快一些,但需要更多内存。 After 20+ arguments concat is better by time and memory. 在20多个参数之后,concat在时间和记忆方面更好。

I see the benefit of using interpolated strings, in terms of readability:在可读性方面,我看到了使用内插字符串的好处:

string myString = $"Hello { person.FirstName } { person.LastName }!"

over a concatenation done this way:通过以这种方式完成的串联:

string myString = "Hello " + person.FirstName + " " person.LastName + "!";

The author of this video tutorial claims that the first one makes better use of memory.本视频教程的作者声称第一个可以更好地利用内存。

How come?怎么来的?

I see the benefit of using interpolated strings, in terms of readability:在可读性方面,我看到了使用内插字符串的好处:

string myString = $"Hello { person.FirstName } { person.LastName }!"

over a concatenation done this way:通过以这种方式完成的串联:

string myString = "Hello " + person.FirstName + " " person.LastName + "!";

The author of this video tutorial claims that the first one makes better use of memory.本视频教程的作者声称第一个可以更好地利用内存。

How come?怎么来的?

I see the benefit of using interpolated strings, in terms of readability:在可读性方面,我看到了使用内插字符串的好处:

string myString = $"Hello { person.FirstName } { person.LastName }!"

over a concatenation done this way:通过以这种方式完成的串联:

string myString = "Hello " + person.FirstName + " " person.LastName + "!";

The author of this video tutorial claims that the first one makes better use of memory.本视频教程的作者声称第一个可以更好地利用内存。

How come?怎么来的?

I see the benefit of using interpolated strings, in terms of readability:在可读性方面,我看到了使用内插字符串的好处:

string myString = $"Hello { person.FirstName } { person.LastName }!"

over a concatenation done this way:通过以这种方式完成的串联:

string myString = "Hello " + person.FirstName + " " person.LastName + "!";

The author of this video tutorial claims that the first one makes better use of memory.本视频教程的作者声称第一个可以更好地利用内存。

How come?怎么来的?

I created a memory test program, I had a bug in one of the benchmarks earlier on so I have fixed that and I have posted the source below the results.我创建了一个 memory 测试程序,我在早些时候的一个基准测试中有一个错误,所以我已经修复了这个问题,我已经在结果下方发布了源代码。 A note, this is using C# 7 if you use.,net core you will be using a different version of C# and these results will change.注意,这是使用 C# 7 如果您使用.,net core 您将使用不同版本的 C#,这些结果发生变化。

Further to the immutable arguments above, the allocation is at the point of assignation.除了上面不可变的 arguments 之外,分配是在分配点。 so the var output = "something"+"something else"+" "+"something other" contains 2 assignations, the variable assign on the left and the final string on the right (as it is optimised this way by the compiler when a fixed number of vars is used).所以var output = "something"+"something else"+" "+"something other"包含 2 个赋值,左边的变量 assign 和右边的最后一个字符串(因为它在编译器以这种方式优化时使用固定数量的变量)。

As shown below, these assignations happen every time you use this method (string.format and stringbuilder differ here, format uses less memory and builder has extra overhead initially).如下所示,每次使用此方法时都会发生这些分配(这里的 string.format 和 stringbuilder 不同,format 使用较少的 memory 并且 builder 最初有额外的开销)。

Simple简单的

So if you are simply adding vars into a single string yes Interp and Inline Concat use the same amount of RAM, string.format uses the least RAM though so there is obviously some extra allocations occurring with concat & interp that string format avoids.因此,如果您只是将 vars 添加到单个字符串中,则 Interp 和 Inline Concat 使用相同数量的 RAM,但 string.format 使用的 RAM 最少,因此字符串格式避免的 concat 和 interp 显然会发生一些额外的分配。

Using the 1 var and assigning to it multiple times使用 1 var 并多次分配给它

Interestingly, in the multiline assigns (where you assign the same value to the var multiple times) even with 3 clears and appendformats added to the stringbuilder it is the most efficient in the multiline assigns and is still faster in CPU time than format, easiest on cpu is interp and concat, however the memory is nearing 1MB.有趣的是,在多行分配中(您多次为 var 分配相同的值),即使在 stringbuilder 中添加了 3 个清除和附加格式,它也是多行分配中最有效的,并且 CPU 时间仍然比格式更快,最简单cpu 是 interp 和 concat,但是 memory 接近 1MB。

Appending to the var附加到 var

When constructing a string over successive lines (appending separately in the builtbylines tests as you may for error code messages) String format slips behind the others when using += to append to the output var.在连续行上构造字符串时(在builtbylines 测试中单独附加,因为您可能会针对错误代码消息)使用 += 到 append 到 output var 时,字符串格式会落后于其他格式。 Stringbuilder in this instance is the clear winner.在这种情况下,Stringbuilder 显然是赢家。

Multiple runs of the functions Here we can see the difference in a very simple 20x run of the line concat that could be found in a function if you wanted to track progress or the part of the function you are attempting to do.函数的多次运行 在这里,我们可以看到非常简单的 20 倍行 concat 运行的差异,如果您想跟踪进度或您尝试执行的 function 的一部分,可以在 function 中找到。 The difference between using a builder vs a string is nearly 25%.使用构建器与使用字符串之间的差异接近 25%。 If you had even a small amount of strings assigned inside a loop of lots of records then the potential memory impact could be quite high by using interp/+=.如果您在包含大量记录的循环中分配了少量字符串,那么使用 interp/+= 对 memory 的潜在影响可能会非常高。 For example if I were importing a relatively small file of records into a database and was using strings then the number of records could easily exceed 50000 in a very short period of time (let alone the 4gb compressed files I used to have to import), which means the system as a whole could easily crash or end up very slow as it is forced the GC repeatedly within a very short period of time.例如,如果我将一个相对较小的记录文件导入数据库并使用字符串,那么记录数很容易在很短的时间内超过 50000 条(更不用说我以前必须导入的 4gb 压缩文件),这意味着整个系统很容易崩溃或最终变得非常缓慢,因为它在很短的时间内被反复强制 GC。 In those cases I would se a stringbuilder ref and simply refresh and re-assign that OR use a Span.在这些情况下,我会设置一个 stringbuilder ref 并简单地刷新并重新分配它或使用 Span。

结果

Source code源代码

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnostics.Windows.Configs;
using BenchmarkDotNet.Running;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    [AsciiDocExporter]
    [MemoryDiagnoser]
    public class Program
    {
        private string str1 = "test string";
        private string str2 = "this is another string";
        private string str3 = "helo string 3";
        private string str4 = "a fourth string";
                
        [Benchmark]
        public void TestStringConcatStringsConst()
        {
            var output = str1 + " " + str2 + " " + str3 + " " + str4;
        }


        [Benchmark]
        public void TestStringInterp()
        {
            var output = $"{str1} {str2} {str3} {str4}";
        }

        [Benchmark]
        public void TestStringFormat()
        {            
            var output = String.Format("{0} {1} {2} {3}", str1, str2, str3, str4);
        }

        [Benchmark]
        public void TestStringBuilder()
        {
            var output = new StringBuilder().AppendFormat("{0} {1} {2} {3}", str1, str2, str3, str4);
        }

        [Benchmark]
        public void TestStringConcatStrings_FourMultiLineAssigns()
        {
            var output = str1 + " " + str2 + " " + str3 + " " + str4;
            output = str1 + " " + str2 + " " + str3 + " " + str4;
            output = str1 + " " + str2 + " " + str3 + " " + str4;
            output = str1 + " " + str2 + " " + str3 + " " + str4;
        }

        [Benchmark]
        public void TestStringInterp_FourMultiLineAssigns()
        {
            var output = $"{str1} {str2} {str3} {str4}";
            output = $"{str1} {str2} {str3} {str4}";
            output = $"{str1} {str2} {str3} {str4}";
            output = $"{str1} {str2} {str3} {str4}";
        }

        [Benchmark]
        public void TestStringFormat_FourMultiLineAssigns()
        {
            var output = String.Format("{0} {1} {2} {3}", str1, str2, str3, str4);
            output = String.Format("{0} {1} {2} {3}", str1, str2, str3, str4);
            output = String.Format("{0} {1} {2} {3}", str1, str2, str3, str4);
            output = String.Format("{0} {1} {2} {3}", str1, str2, str3, str4);
        }

        [Benchmark]
        //This also clears and re-assigns the data, I used the stringbuilder until the last line as if you are doing multiple assigns with stringbuilder you do not pull out a string until you need it.
        public void TestStringBuilder_FourMultilineAssigns()
        {
            var output = new StringBuilder().AppendFormat("{0} {1} {2} {3}", str1, str2, str3, str4);
            output = output.Clear().AppendFormat("{0} {1} {2} {3}", str1, str2, str3, str4);
            output = output.Clear().AppendFormat("{0} {1} {2} {3}", str1, str2, str3, str4);
            output = output.Clear().AppendFormat("{0} {1} {2} {3}", str1, str2, str3, str4);
        }

        [Benchmark]
        public void TestStringConcat_BuiltByLine()
        {
            var output = str1;
            output += " " + str2;
            output += " " + str3;
            output += " " + str4;
        }

        [Benchmark]
        public void TestStringInterp_BuiltByLine1()
        {
            var output = str1;
            output = $"{output} {str2}";
            output = $"{output} {str3}";
            output = $"{output} {str4}";
        }

        [Benchmark]
        public void TestStringInterp_BuiltByLine2()
        {
            var output = str1;
            output += $" {str2}";
            output += $" {str3}";
            output += $" {str4}";
        }

        [Benchmark]
        public void TestStringFormat_BuiltByLine1()
        {
            var output = str1;
            output = String.Format("{0} {1}", output, str2);
            output = String.Format("{0} {1}", output, str3);
            output = String.Format("{0} {1}", output, str4);
        }

        [Benchmark]
        public void TestStringFormat_BuiltByLine2()
        {
            var output = str1;
            output += String.Format(" {0}", str2);
            output += String.Format(" {0}", str3);
            output += String.Format(" {0}", str4);
        }

        [Benchmark]
        public void TestStringBuilder_BuiltByLine()
        {
            var output = new StringBuilder(str1);
            output.AppendFormat(" {0}", str2);
            output.AppendFormat(" {0}", str3);
            output.AppendFormat(" {0}", str4);
        }

        [Benchmark]
        public void TestConcatLine20x()
        {
            for (int i = 0; i < 20; i++) {
                TestStringConcat_BuiltByLine();
            }
        }

        [Benchmark]
        public void TestInterpLine20x()
        {
            for (int i = 0; i < 20; i++)
            {
                TestStringInterp_BuiltByLine2();
            }
        }

        [Benchmark]
        public void TestBuilderLine20x()
        {
            for (int i = 0; i < 20; i++)
            {
                TestStringBuilder_BuiltByLine();
            }
        }

        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Program>(null, args);
            //var summary = BenchmarkRunner.Run())
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM