【问题标题】:Performance optimizing looped string concatenations in c#c# 中的性能优化循环字符串连接
【发布时间】:2020-11-30 04:20:00
【问题描述】:

我有一个处理一些历史数据的应用程序。 我做了一些性能分析,并将以下函数确定为新的瓶颈:

public string GenerateSaveStringOptimized()
{
    StringBuilder saveString = new StringBuilder();
    saveString.Append( $"{this.TimeOfDay},{this.DayOfMonth},{this.DayOfYear},");
    foreach (float value in this.MovingAverage3h) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage6h) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage1d) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage6d) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage12d) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage24d) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage48d) saveString.Append($"{value},");
    foreach (float value in this.MovingAverage96d) saveString.Append($"{value},");
    foreach (float value in this.RSI3h) saveString.Append($"{value},");
    foreach (float value in this.RSI6h) saveString.Append($"{value},");
    foreach (float value in this.RSI1d) saveString.Append($"{value},");
    foreach (float value in this.RSI6d) saveString.Append($"{value},");
    foreach (float value in this.RSI12d) saveString.Append($"{value},");
    foreach (float value in this.Momentum1h) saveString.Append($"{value},");
    foreach (float value in this.SpotPrices1h) saveString.Append($"{value},");
    foreach (float value in this.BuyPrices1h) saveString.Append($"{value},");
    foreach (float value in this.SellPrices1h) saveString.Append($"{value},");
    saveString.Append($"{this.Label}");
    return saveString.ToString();
}

结果将是一个 csv 格式的字符串,例如:

0,1,1,0.4916667,0.4916667,0.4916667,0.4916667,0.4916667,0.49,0.49,0.49,0.49,0.4897351, [...]

每个数组都包含 25 个浮点值,因此代码类似于以下伪代码:

for (int i = 0; i < 17; i++)
{
    for (b = 0; b < 25 b++)
    {
        saveString.Append("Value,")
    }
}

对于该函数的每次调用都会产生 17 * 25 = 425 个字符串连接!

有什么办法可以优化吗?

顶级性能消耗者似乎是“等待 cpu”和“AppendformatHelper”,这是 StringBuilder 的内置方法。不幸的是,我不知道如何轻松处理它

也许有如下硬编码设置?

saveString.Append($"{MA3h[0]},{MA3h[1]},{MA3h[2]},...");
saveString.Append($"{MA6h[0]},{MA6h[1]},{MA6h[2]},...");
saveString.Append($"{MA1d[0]},{MA1d[1]},{MA1d[2]},...");

改用以下循环取得了巨大的成功:

foreach (float value in this.RSI1d)
{
    saveString.Append(value.ToString());
    saveString.Append(',');
}

非常感谢 user1672994、Guru Stron 和 Theodor Zoulias

cmets 中的其他提及也将被尝试。

【问题讨论】:

  • 我会先考虑使用.AppendFormat。通过运行所有内容以通过字符串插值附加,您会产生大量额外的开销/垃圾。还值得调查一下您使用StringBuilder 的用途——如果您最终(比如说)写入文件,那么直接使用StreamWriter 比先在内存中构建整个东西要高效得多.
  • 这并在其构造函数中为StringBuilder 提供初始长度,以便它可以预先分配一堆内存而不是在执行期间扩展。
  • 由于您似乎在连接一堆可枚举的值,您可能需要查看String.Join。不知道它是否更快,但我无法想象它会更慢......
  • 如果您需要比默认流应用更多的缓冲,可以使用BufferedStreamFileStream 也允许在构造函数中自定义缓冲区,但可能存在操作系统限制)。在 StringBuilder 中构建大字符串只是为了将它们折叠回字符串实例,然后存储在数组中,这对垃圾收集器来说是一个很大的负担。
  • @HereticMonkey 在研究了一下source code 之后,String.Join 应该更快,因为它预先分配了一个大小正好合适的“不安全”字符缓冲区。

标签: c# string performance string-concatenation


【解决方案1】:

预先分配足够的容量。

StringBuilder saveString = new StringBuilder(100000);

不要使用格式化或插值。

saveString.Append(this.TimeOfDay).Append(',').Append(this.DayOfMonth).Append(',').Append(this.DayOfYear).Append(',');

在所有循环中执行类似的替换。

foreach (float value in this.MovingAverage3h) saveString.Append(value).Append(',');

不要使用格式化或插值。

saveString.Append(this.Label);

Append 方法有一个接受float 的重载,因此不会出现装箱。

【讨论】:

  • 我读了 cmets。事实证明,所有这些技巧都已经有了。我应该删除答案吗?
  • 没有,因为还没有。容量,是最终字符串的近似长度还是位/字节?
  • string.join 似乎要快得多。但它仍然是应用程序性能的 4 倍...
猜你喜欢
  • 2017-06-10
  • 1970-01-01
  • 2015-07-07
  • 1970-01-01
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
  • 2017-01-20
  • 2010-12-22
相关资源
最近更新 更多