是IEnumerable.Any比rest的for循环快吗?
我们在打开表单的代码中经历了一些缓慢,这可能是由于一个带有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); }
第一个实现稍快 (枚举比循环稍慢)。 第二个是更具可读性 。
UPDATE
Oded的回答可能是正确的,并且很好地发现它。 第一个在这里较慢,因为它涉及数据库往返。 否则,就像我说的那样稍快。
更新2 – certificate
这是一个简单的代码,显示为什么第一个更快:
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(); }
这是输出:
Loop花了12198
迭代耗时20922
因此循环是迭代/枚举的两倍快。
称之为有根据的猜测:
this.ProductIDs.Length
这可能是缓慢所在的地方。 如果在每次迭代时从数据库(例如)中检索ProductIDs
列表以获得Length
那么它确实会非常慢。 您可以通过分析您的应用程序来确认这一点。
如果不是这种情况(比如ProductIDs
在内存中并且Length
被缓存),则两者的运行时间应该几乎相同。
我认为它们或多或少相同。 我通常会参考Jon Skeet的Reimplementing LINQ to Objects博客系列来了解扩展方法的工作原理。 这是Any()
和All()
的post
这是该post中Any()
实现的核心部分
public static bool Any( this IEnumerable source, Func predicate) { ... foreach (TSource item in source) { if (predicate(item)) { return true; } } return false; }
本文假定ProductIDs
是List
或数组。 所以我在谈论Linq-to-objects。
Linq通常比传统的基于循环的代码更慢但更短/更易读。 取决于您正在做的事情,因素通常为2-3。
你可以重构你的代码来使这个this.ProductIDs
成为一个HashSet
? 或者至少对数组进行排序,以便您可以使用二进制搜索。 您的问题是您正在执行线性搜索,如果有许多产品,则搜索速度很慢。
我认为下面的实现会比相应的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;