我的问题是,当numbers
具体化numbers
集合时,下面代码中的LINQ会读取flag
值三次吗?我正在尝试优化我的代码。这里我希望Where
子句只计算一次,如果flag == true
List<int> list = new(){1, 2, 3};
bool flag = true;
bool IsNumberBig(int num)
{
return num > 100;
}
var numbers = list.Where(l => flag || IsNumberBig(l)).ToList();
我没能找到一个相关的问题。将感谢看看我如何可以检查这个自己。
3条答案
按热度按时间mzillmmw1#
flag
的值将在每次调用lambda时被求值,显然,这比求值IsNumberBig()
(或其他更复杂的方法)要便宜,但仍然不是免费的。要优化此功能,可以编写如下代码
与此类似,如果flag为true,则不进行迭代(在您的情况下,无论如何,将返回所有元素)
kkih6yb82#
我认为需要注意的是LINQ主要是语法糖。它不做优化。绝大多数优化是由编译器完成的,或者更具体地说,是抖动。
讨论优化时的一个问题是,只要结果相同,抖动就可以执行任何类型的优化。但它也必须尽可能快地执行任何优化,所以它很少做所有允许它做的事情。它还取决于编译器的版本,最近的版本具有分层编译功能,以便更好地优化频繁使用的循环。
由于所有这些原因,很难猜测编译器会优化什么,不会优化什么,最好的方法是只对代码进行基准测试。使用Benchmark.Net进行或不进行检查,这样你都会得到正确的答案。它还应该告诉你是否需要担心性能差异。
即使你很难猜到编译器会做什么,但还是有一些事情值得注意。大多数优化都是在一个方法中完成的,编译器不会试图重写方法签名。然而,小方法往往是 * 内联的 *,因此可以作为调用方法的一部分进行优化。因此,如果所有代码都是内联的,那么很可能会删除
flag
检查。然而,阻止内联的因素之一是间接调用,例如通过接口调用方法,或者在本例中调用 delegate。2 LINQ中几乎所有的东西都是委托和接口,这往往会妨碍性能。3所以一般来说,使用LINQ是为了方便,而不是性能。尽管如此,现代处理器拥有相当惊人的分支预测器,所以我认为这种容易预测的分支的影响相当小,可能还有其他因素对性能有更大的影响。
但最重要的是对代码进行基准测试和/或分析,而不是仅仅猜测性能。对于试图优化完全错误的东西的人来说,这是很常见的,即使是有经验的开发人员。如果你想开始,请查看Measure app performance in visual studio和Benchmark .net。
sxpgvts33#
您可以执行以下扩展:
它基本上将@PMF的答案封装在一个扩展方法中,所以你可以像使用你已经知道的
Where
一样使用它。它只需要一个额外的condition
参数,用于打开/关闭predicate
的应用程序。这样做的好处是你可以像使用普通的Where
一样将它与其他Linq方法链接起来。然后像这样使用它:
在此Fiddle中查看实际应用。
请注意,这在与数据库相关(Linq to SQL)的设置中会更有意义,因为这实际上是一个微观优化。要显示效果,您必须有许多项和一个非常昂贵的 predicate 。
你的密码里也有一个词:
如果
flag
为真,则flag || X
将计算为真,而不考虑X。X甚至不会被计算。因此,您基本上实现了与您的需求相反的内容。
另请参阅:条件逻辑OR运算符||