5

因此,出于好奇和无聊,我在对画家的算法 Shlemiel进行基准测试。我从一个空白字符串开始,在 1000 个空格中创建另一个,然后开始将一个添加到另一个,使用普通的旧低效字符串连接,计算每次花费的时间。

string s1 = "";
string s2 = "";
while (s2.Length < 1000)
{
    s2 += " ";
}

while (true)
{
    Stopwatch sw = Stopwatch.StartNew();
    s1 += s2;
    sw.Stop();

    Console.WriteLine(" {0}| {1}", s1.Length, sw.ElapsedMilliseconds);
}

正如预期的那样,字符串越长,连接所需的时间就越长(它的影响比我预期的要小得多,但这是另一天的另一个问题)。然而,令人惊讶的,它所花费的时间持续飙升。每六个连接所用的时间大约是前五个连接的两到三倍。

 Length     | Time (ms)
 -----------------------
 32250000   | 117
 32251000   | 44
 32252000   | 31
 32253000   | 30
 32254000   | 30
 32255000   | 32
 32256000   | 129
 32257000   | 35
 32258000   | 43
 32259000   | 34
 32260000   | 30
 32261000   | 29
 32262000   | 107
 32263000   | 47
 32264000   | 29
 32265000   | 30
 32266000   | 31
 32267000   | 29
 32268000   | 110
 32269000   | 46
 32270000   | 31
 32271000   | 30
 32272000   | 30
 32273000   | 30
 32274000   | 113

这些样本来自于字符串开始变得非常大时,但模式从一开始就成立。在很大程度上,前一千个左右的样本太小而无法注意到该模式,但在 1.8k 标记附近它是可识别的。

我的第一个假设是,在幕后,字符被存储在某种 ArrayList/vector 类型的交易中,一旦填满,它的大小就会翻倍,但当我想得更多时,这不合适——如果那是在这种情况下,尖峰将出现在指数周期中,而不是线性的。

所以,简而言之:这到底是怎么回事?

4

1 回答 1

4

创建字符串并丢弃它们会产生垃圾。一旦您使用了一定数量的内存,就会发生垃圾收集并暂停您的进程。由于您的过程中没有其他任何事情发生,并且您总是使字符串长度相同,因此 GC 总是同时发生(每 6 次运行)。

为避免对您的计时产生这种影响,请在每次运行时启动计时器之前调用GC.Collect 。

于 2013-11-04T15:10:39.380 回答