4

我有这个代码:

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。

现在,如果我更改代码,那么该度量将位于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循环很慢,但没想到它这么慢......是这样吗?

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

4 回答 4

9

foreach 循环本身与时差无关。

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

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

在第二种情况下,您在开始迭代选项开始计时,这意味着获取选项的时间包括在时间中。

因此,您看到的时间差异不是由于 foreach 循环本身,而是获取选项所需的时间。

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

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

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

于 2009-12-13T16:33:54.457 回答
1

如果你测量做某件事所花费的时间 160 次,它通常会比测量做一次所花费的时间长 160 倍。您是建议循环的内容只执行一次,还是要比较粉笔和奶酪?

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

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

但是,您的结果仍然毫无用处。将一个非常短的操作计时一次会产生很差的结果——您必须重复这样的事情数万次才能获得具有统计意义的平均时间。否则,系统时钟的不准确性以及启动和停止计时器所涉及的开销将淹没您的结果。

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

最后,你如何执行测试可以改变事情。如果您总是先运行测试 1,然后再运行测试 2,则运行第一个测试可能会影响 CPU 缓存(例如选项列表中的数据)等,以便以下代码能够更快地执行。如果在您的一项测试期间发生垃圾收集,它会扭曲结果。

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

于 2009-12-13T16:20:22.227 回答
0

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

于 2009-12-13T15:29:51.077 回答
0

只需在 IDE 中暂停几次,您就会看到时间流逝了。

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

for (MyClass x in y)

for (MyClass theParticularInstanceOfClass in MyCollectionOfInstances)

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

于 2009-12-13T20:47:16.167 回答