C#中的慢速While循环

5

我有一个while循环,它只是调用一个方法。我在循环外面放了一个计时器,另一个计时器则逐步累加循环内方法调用所需的时间。外部计时器大约需要17秒,内部计时器的总时间为40毫秒。这个循环执行了50,000次。以下是代码示例:

long InnerTime = 0;
long OutterTime = 0;
Stopw1.Start();
int count = 1;
while (count <= TestCollection.Count) {
    Stopw2.Start();
    Medthod1();
    Stopw2.Stop();
    InnerTime = InnerTime + Stopw2.ElapsedMilliseconds;
    Stopw2.Reset();
    count++;
}
Stopw1.Stop();
OutterTime = Stopw1.ElapsedMilliseconds;
Stopw1.Reset();

非常感谢您的帮助。 Massimo


你尝试过移除内部循环计时代码并查看是否加快了速度吗? - Thom Smith
代码看起来很好,除了“Method1”,我们不知道它是做什么的。否则,关于计时器似乎一切正常! - Mitja Bonca
1
TestCollection是什么 - 是一个方法、变量/属性还是其他什么?尝试用常量替换TestCollection.Count(显然要低到不会出错)。 - Jon Egerton
1
尝试在循环外部执行 int countLimit = TestCollection.Count 并在条件语句中使用 countLimit。如果集合非常动态,这可能是一个相当昂贵的计算。 - JamieSee
3
如果TestCollection是Collection<T>或List<T>的实例,Count操作的时间复杂度为O(1)。除非它是某种低效的自定义类,否则不应该引起问题。 - Tim Copenhaver
6个回答

8
你正在比较苹果和橙子。你的外部计时器测量总共用了多少时间。你的内部计时器测量调用Method1所花费的整毫秒数。 ElapsedMilliseconds属性“代表向下舍入到最近的整毫秒值的经过时间”。因此,您大约会将50,000次舍入到最近的毫秒数。
如果你对Method1的调用平均耗时少于1毫秒,那么大多数时间,'ElapsedMilliseconds'属性将返回0,而你的内部计数将远远小于实际时间。实际上,你的方法平均需要0.3毫秒,所以你即使在40次使用中也很幸运超过1毫秒。
使用Elapsed.TotalMillisecondsElapsedTicks属性来代替ElapsedMilliseconds。一个毫秒等价于10,000个滴答声。

使用ElapsedTicks显示了更准确的时间反映。实际上只有72毫秒的时间差。谢谢Jeffrey。 - Massimo Guerrera

2
这段代码 TestCollection.Count 是在做什么呢?
我怀疑你的程序在反复计算 50,000 个项目的数量,导致花费了17秒。

你认为它正在计算50000次的faculty吗?很有趣,TestCollection是什么类型。 - Mare Infinitus
你可能在想Linq的Count()表达式而不是ICollection.Count属性。 - scottm

1
补充一下其他人已经说过的,通常情况下C#编译器必须重新评估任何属性,包括。
TestCollection.Count

对于每个单独的循环迭代,属性的值可能会从一次迭代到另一次迭代发生变化。

将该值分配给一个本地变量可以消除编译器在每次循环迭代时重新评估的需要。

我所知道的唯一例外是针对数组的优化,即数组边界检查消除


1
ICollection.Count通常只返回一个私有字段值。虽然该值可能会更改并导致问题,但我认为“重新评估属性”不是问题所在。 - scottm
当然,我认为真正的问题在于他没有像@Jeffrey指出的那样正确地进行测量。我只是为了完整性而添加了这个。有时相对于循环体来说,属性评估确实是昂贵的(但在这里不是)。 - Eric J.

1

尝试更改这个:

while (count <= TestCollection.Count) {
...
}

转换为:

int total = TestCollection.Count;
while (count <= total) {
...
}

0
为了正确测量您的调用所需的时间,您应该使用Ticks。
请尝试以下操作:
long InnerTime = 0;
long OutterTime = 0;

Stopwatch Stopw1 = new Stopwatch();
Stopwatch Stopw2 = new Stopwatch();

Stopw1.Start();
int count = 1;
int run = TestCollection.Count;
while (count <= run) {
    Stopw2.Start();
    Medthod1();
    Stopw2.Stop();
    InnerTime = InnerTime + Stopw2.ElapsedTicks;
    Stopw2.Reset();
    count++;
}
Stopw1.Stop();
OutterTime = Stopw1.ElapsedTicks;
Stopw1.Reset();

0

你不应该单独测量如此微小的方法。但如果你真的想这样做,可以尝试以下操作:

long innertime = 0;

while (count <= TestCollection.Count) 
{     
    innertime -= Stopw2.GetTimestamp();
    Medthod1();
    innertime += Stopw2.GetTimestamp();
    count++; 
} 

Console.WriteLine("{0} ms", innertime * 1000.0 / Stopw2.Frequency);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接