使用StopWatch和foreach循环的C#* Strange *问题

时间:2009-12-13 15:25:51

标签: c# performance loops foreach stopwatch

我有这段代码:

var options = GetOptions(From, Value, SelectedValue);     
var stopWatch = System.Diagnostics.Stopwatch.StartNew();

foreach (Option option in options)
{
    stringBuilder.Append("<option");

    stringBuilder.Append(" value=\"");
    stringBuilder.Append(option.Value);
    stringBuilder.Append("\"");

    if (option.Selected)
        stringBuilder.Append(" selected=\"selected\"");

    stringBuilder.Append('>');

    stringBuilder.Append(option.Text);
    stringBuilder.Append("</option>");
}

HttpContext.Current.Response.Write("<b>" + stopWatch.Elapsed.ToString() + "</b><br>");

写作:
 00:00:00.0004255在第一次尝试(不在调试中)
 00:00:00.0004260在第二次尝试和中  在第三次尝试中00:00:00.0004281。

现在,如果我更改代码,那么度量将 in foreach循环:

var options = GetOptions(From, Value, SelectedValue);     

foreach (Option option in options)
{
    var stopWatch = System.Diagnostics.Stopwatch.StartNew();

    stringBuilder.Append("<option");

    stringBuilder.Append(" value=\"");
    stringBuilder.Append(option.Value);
    stringBuilder.Append("\"");

    if (option.Selected)
        stringBuilder.Append(" selected=\"selected\"");

    stringBuilder.Append('>');

    stringBuilder.Append(option.Text);
    stringBuilder.Append("</option>");

    HttpContext.Current.Response.Write("<b>" + stopWatch.Elapsed.ToString() + "</b><br>");
}

......我得到了 [00:00:00.0000014,00:00:00.0000011] = 00:00:00.0000025第一次尝试(不在调试中),
[00:00:00.0000016,00:00:00.0000011] = 00:00:00.0000027在第二次尝试和中 [00:00:00.0000013,00:00:00.0000011] = 00:00:00.0000024第三次尝试。


根据第一个结果,这完全是胡说八道......我听说foreach循环很慢,但从来没有想到它是那么慢......是吗? / p>

options有2个选项。 如果需要,这是option类:

public class Option
{
    public Option(string text, string value, bool selected)
    {
        Text = text;
        Value = value;
        Selected = selected;
    }

    public string Text
    {
        get;
        set;
    }

    public string Value
    {
        get;
        set;
    }

    public bool Selected
    {
        get;
        set;
    }
}

感谢。

4 个答案:

答案 0 :(得分:7)

foreach循环本身与时差无关。

什么是GetOptions方法返回?我的猜测是它没有返回一个选项集合,而是一个能够获得选项的枚举器。这意味着在您开始迭代它们之前,实际上不会完成获取选项。

在第一种情况下,您在开始迭代选项之前开始计时,这意味着获取选项的时间包含在时​​间中。

在第二种情况下,您在开始迭代选项后启动时钟,这意味着获取选项的时间包含在时间中。

所以,你看到它的时差不是由于foreach循环本身,而是获取选项所需的时间。

您可以通过将选项读入集合来确保立即获取选项:

var options = GetOptions(From, Value, SelectedValue).ToList();

现在测量性能,你会发现差别很小。

答案 1 :(得分:1)

如果你测量做160次的时间,通常比测量一次做的时间长160倍。你是建议循环的内容只执行一次,还是你想比较粉笔和奶酪?

在第一种情况下,尝试更改代码的最后一行使用     stopWatch.Elapsed.ToString() 至     stopWatch.Elapsed.ToString()/ options.Count

这至少意味着你将一次迭代与一次迭代进行比较。

但是,您的结果仍然无用。一次非常短暂的操作定时效果不佳 - 你必须重复这样的事情数万次以获得统计上有意义的平均时间。否则,系统时钟的不准确性以及启动和停止计时器所涉及的开销将会影响您的结果。

此外,当这一切发生时,PC正在做什么?如果有其他进程加载CPU,那么它们很容易干扰您的时间。如果您在繁忙的服务器上运行它,那么您可能会获得完全随机的结果。

最后,你如何区分测试可以改变一切。如果您始终运行测试1,然后运行测试2,则运行第一个测试可能会影响CPU缓存(例如,选项列表中的数据)等,以便以下代码能够更快地执行。如果在您的某个测试期间发生垃圾收集,则会导致结果偏差。

在您拥有值得比较的数字之前,您需要消除所有这些因素。只有这样你才能问“为什么测试1的运行速度比测试2慢得多”?

答案 2 :(得分:0)

第一个代码示例在迭代完所有选项之后不输出任何内容,而第二个代码示例在处理完第一个选项后输出一个时间。如果有多个选项,您可能会看到这样的差异。

答案 3 :(得分:0)

只需pause it a few times in the IDE,您就会看到时间的去向。

有一种非常自然而强烈的诱惑,认为事情所采取的时间与它们的代码量成正比。例如,您认为哪个更快?

for (MyClass x in y)

for (MyClass theParticularInstanceOfClass in MyCollectionOfInstances)

很自然地认为第一个更快,实际上代码大小无关紧要并且可能隐藏了大量昂贵的操作。