是IEnumerable.比带break的for循环快
本文关键字:for 循环 break IEnumerable 比带 | 更新日期: 2023-09-27 17:59:17
我们在打开表单的代码中遇到了一些缓慢的问题,这可能是由于带有break
的for
循环需要很长时间才能执行。我把它换成了IEnumerable.Any()
,看到表单很快就打开了。我现在正试图弄清楚是单独进行此更改提高了性能,还是更有效地访问了ProductIDs
属性。这种实施应该更快吗?如果是,为什么?
最初实施:
public bool ContainsProduct(int productID) {
bool containsProduct = false;
for (int i = 0; i < this.ProductIDs.Length; i++) {
if (productID == this.ProductIDs[i]) {
containsProduct = true;
break;
}
}
return containsProduct;
}
新实现:
public bool ContainsProduct(int productID) {
return this.ProductIDs.Any(t => productID == t);
}
称之为有根据的猜测:
this.ProductIDs.Length
这可能就是缓慢的地方。如果在每次迭代中(例如)都从数据库中检索ProductIDs
的列表以获得Length
,那么它确实会非常慢。您可以通过分析应用程序来确认这一点。
如果不是这种情况(比如ProductIDs
在内存中,Length
在缓存中),那么两者的运行时间应该几乎相同。
第一个实现是稍微快(枚举比for
循环稍微慢)。第二个是可读性更强。
更新
Oded的答案可能是正确的,并且在发现它方面做得很好。第一个答案在这里较慢,因为它涉及数据库往返。否则,它会像我说的那样稍微快一点
更新2-证明
下面是一个简单的代码,说明为什么第一个更快:
public static void Main()
{
int[] values = Enumerable.Range(0, 1000000).ToArray();
int dummy = 0;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < values.Length; i++)
{
dummy *= i;
}
stopwatch.Stop();
Console.WriteLine("Loop took {0}", stopwatch.ElapsedTicks);
dummy = 0;
stopwatch.Reset();
stopwatch.Start();
foreach (var value in values)
{
dummy *= value;
}
stopwatch.Stop();
Console.WriteLine("Iteration took {0}", stopwatch.ElapsedTicks);
Console.Read();
}
输出如下:
环路占用12198
迭代耗时20922
所以循环是迭代/枚举的两倍
我认为它们或多或少是相同的。我通常参考Jon Skeet的"重新实现LINQ到对象"博客系列来了解扩展方法的工作原理。这是Any()
和All()
的帖子
以下是后中Any()
实现的核心部分
public static bool Any<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
...
foreach (TSource item in source)
{
if (predicate(item))
{
return true;
}
}
return false;
}
本文假设ProductIDs
是List<T>
或数组。所以我说的是林克对物体。
Linq通常比传统的基于循环的代码更慢,但更短/更可读。根据你所做的事情,2-3的因素是典型的。
你能重构你的代码使this.ProductIDs
成为HashSet<T>
吗?或者至少对数组进行排序,以便使用二进制搜索。你的问题是你在执行线性搜索,如果有很多产品,这是很慢的。
我认为下面的实现会比相应的linq实现快一点,但非常小尽管
public bool ContainsProduct(int productID) {
var length = this.ProductIDs.Length;
for (int i = 0; i < length; i++) {
if (productID == this.ProductIDs[i]) {
return true;
}
}
return false;
}
区别通常在于内存使用率和速度。
但通常情况下,当您知道将使用数组的所有元素时,您应该使用for循环。在其他情况下,您应该尝试使用while或do while。
我认为这个解决方案使用最少的资源
int i = this.ProductIDs.Length - 1;
while(i >= 0) {
if(this.ProductIDs[i--] == productId) {
return true;
}
}
return false;