C# 为什么String.Concat没有优化为StringBuilder.Append?

C# 为什么String.Concat没有优化为StringBuilder.Append?,c#,.net,string,optimization,clr,C#,.net,String,Optimization,Clr,我发现编译器将常量字符串表达式的串联优化为一个字符串 既然只有在运行时才知道字符串的串接,那么为什么编译器不优化循环中的字符串串接和10个以上字符串的串接以使用StringBuilder.Append?我是说,这是可能的,对吧?实例化一个StringBuilder并将每个连接转换为Append()调用 是否有任何理由认为应该或可能不会对其进行优化?我错过了什么?两个原因: 您无法以编程方式确定严格来说性能更高的位置 如果执行不正确,“优化”将减慢速度 您可以建议人们为他们的应用程序使用正确的

我发现编译器将常量字符串表达式的串联优化为一个字符串

既然只有在运行时才知道字符串的串接,那么为什么编译器不优化循环中的字符串串接和10个以上字符串的串接以使用
StringBuilder.Append
?我是说,这是可能的,对吧?实例化一个
StringBuilder
并将每个连接转换为
Append()
调用

是否有任何理由认为应该或可能不会对其进行优化?我错过了什么?

两个原因:

  • 您无法以编程方式确定严格来说性能更高的位置
  • 如果执行不正确,“优化”将减慢速度
您可以建议人们为他们的应用程序使用正确的调用,但在某些情况下,开发人员有责任使其正确

编辑:关于截止,我们还有两个问题:

  • 唯一能确定是否达到截止点的方法是复杂的流量分析。能够找到可以转换的部分的地方非常少
  • 流量分析是昂贵的。如果您在运行时执行此操作,整个程序将运行得较慢,因为很少有可能一段写得不好的代码会更快。如果您在编译时执行此操作,根据语言语法,这不是错误,但您可以发出警告-这正是FXCop所做的(一个缓慢但可用的流分析工具)。试想一下,如果FXCop总是必须与编译器一起运行;那么多时间人们都在等待运行代码。如果是在运行时,那么欢迎来到JVM启动时间

我认为这对编译器编写人员来说太复杂了一点。当您在循环中引用连接以外的中间字符串时(例如,将它们传递给其他方法等),这种优化是不可能的。

对于多个字符串的单个连接(例如a+b+c+d+e+f+g+h+i+j)您确实希望使用
String.Concat
IMO。它有为每个调用构建数组的开销,但它的好处是,该方法可以在需要分配任何内存之前计算出结果字符串的确切长度<代码>StringBuilder.Append(a).Append(b).一次只给出一个值,因此生成器不知道要分配多少内存


至于在循环中执行,此时您已经添加了一个新的局部变量,并且必须添加代码,以便在正确的时间(调用
StringBuilder.ToString()
)回写字符串变量。在调试器中运行时会发生什么?如果没有看到价值的积累,只是在循环结束时才变得可见,这不是很令人困惑吗?哦,当然,您必须执行适当的验证,确保在循环结束之前的任何时候都没有使用该值…

明确的答案必须来自编译器设计团队。但是让我在这里尝试一下

如果您的问题是,为什么编译器不打开此选项:

string s = "";
for( int i = 0; i < 100; i ++ )
    s = string.Concat( s, i.ToString() );
string s=”“;
对于(int i=0;i<100;i++)
s=string.Concat(s,i.ToString());
为此:

StringBuilder sb = new StringBuilder();
for( int i = 0; i < 100; i++ )
    sb.Append( i.ToString() );
string s = sb.ToString();
StringBuilder sb=新建StringBuilder();
对于(int i=0;i<100;i++)
某人附加(i.ToString());
字符串s=sb.ToString();
最有可能的答案是这不是优化。这是对代码的重写,它根据开发人员(而不是编译器)拥有的知识和意图引入新的结构

这种类型的更改需要编译器对BCL有更多的了解。如果明天有更好的字符串汇编服务可用怎么办?编译器应该使用它吗

如果循环条件更复杂,编译器是否应该尝试执行一些静态分析,以确定这种重写的结果在功能上是否仍然相同?在许多方面,这就像解决问题一样

最后,我不确定在所有情况下,这都会导致代码执行速度更快。实例化
StringBuilder
并在追加文本时调整其内部缓冲区的大小是有代价的。事实上,追加的成本与被连接字符串的大小、有多少个字符串以及内存压力的大小密切相关。这些是编译器无法提前预测的事情


作为开发人员,您的工作是编写性能良好的代码。编译器只能通过进行某些安全、不变的优化来提供帮助。不为您重写代码。

可能是因为在代码中匹配这样的模式很复杂,如果编译器由于某种原因无法进行匹配,代码的性能会突然变得很糟糕。优化这样的代码会鼓励编写这样的代码,这甚至会在编译器无法再进行优化的情况下进一步增加负面影响


对于串联一组已知字符串,
StringBuilder
的速度不快于
String。Concat
字符串是不可变的类型,因此使用串联字符串比使用
StringBuilder.Append
要慢

编辑:为了进一步澄清我的观点,当您谈到为什么
String.Concat
没有优化到
StringBuilder.Append
,一个
StringBuilder
类的语义与不可变的
String
类型完全不同。既然它们显然是两件不同的事情,为什么要期望编译器对其进行优化呢?此外,
StringBuilder
是一种可变类型,可以动态更改其长度,为什么