回到我在 C 和 C++ 中完成大部分工作的那一天,当然,我会手动应用德摩根定理来优化任何重要的布尔表达式。
在 C# 中执行此操作是否有用,或者优化器是否不需要这样做?
回到我在 C 和 C++ 中完成大部分工作的那一天,当然,我会手动应用德摩根定理来优化任何重要的布尔表达式。
在 C# 中执行此操作是否有用,或者优化器是否不需要这样做?
在这么快的处理器上,重新排列布尔表达式几乎不可能在速度上产生任何实际差异。而且 C# 编译器非常聪明,它也会对其进行优化。优化可读性和清晰度!
您的首要目标应该是优化此类语句,以便开发人员理解和易于维护。
德摩根定理可以是一个有用的工具。
JIT 中的优化,以其当前形式,并没有(从我读过的内容)为您优化。如果你需要优化它,你仍然需要考虑到这一点。
话虽如此,这是一个相当小的微优化。一般来说,我更喜欢以更具表现力的形式编写您的“非平凡布尔表达式”,以便更容易理解。对我来说,这比应用德摩根定理获得的任何非常小的优化都更有价值。
我相信这个问题的正确答案是编译器不会(通常)优化布尔评估,仅仅是由于逻辑短路,例如:
if (GetFlagA() || GetFlagB())
{
...do something
}
如果调用 GetFlagA 修改了 GetFlagB 所依赖的东西,那么如果评估的顺序真的很重要(当然这是非常糟糕的代码实践,但对于不同的线程来说这是一个不同的主题。)这里的问题是逻辑短路,如果 GetFlagA 运行并且返回 true,GetFlagB 将永远不会运行,如此处所示,GetFlagB 的结果与语句的评估无关。
一个 | 乙| =
F | F | F
F | T | 吨
T | F | 无论 B 的返回值如何,T 都为真。
T | T | 无论 B 的返回值如何,T 都为真。
因此,总而言之,询问您是否可以使用 Demoorgan 或其他任何东西进行优化,就像其他计算机科学和软件工程一样。“这取决于。” 如果您使用的是非功能评估,它可能会被优化。老实说,尽管您是在一个非常快速的平台上进行一些操作,但最好还是花时间编写文档。
我希望这有帮助。
我猜编译器已经这样做了。您可以通过 Reflector 进行测试并查看编译后的 IL。
优化可读性和可维护性。问问自己,你是否会在一年内理解你的巧妙优化,如果你认为,代码可以使用一些注释,让代码自我记录。
唯一需要重新排列、布尔代数或 demorgan 的时候是当逻辑太复杂而无法以另一种方式进行时。如果不是太复杂,请保持可读性。有一个简化逻辑的案例。
有时当逻辑很棘手时,我需要创建一个卡诺图来将逻辑简化为我什至可以写下来的东西。通常,使用 K-Maps 可以帮助您想出更简洁的方式来表达您的逻辑。结果可能有意义,也可能没有意义,但它是等价的。
而且我还要说,DeMorgan 本身确实不是会产生影响的优化,如果超过一半的项是负数(NOT),您最多只能获得删除一些 NOT 的性能,这是一个单一的每个 NOT 的 CPU 指令。在最坏的情况下,您可以添加尽可能多的 NOT,如果您不应该使用 DeMorgan,您将获得比一开始更多的 NOT。
如果您要优化逻辑,请使用一些布尔代数或我个人最喜欢的 K-Maps来减少术语的数量(如果可能)。不要只是移动布尔运算符,这很愚蠢。
由于布尔表达式评估使用快捷语义,您可以将计算成本较低的子表达式移到前面:
if (CountAllFilesOnDrive('C:\') > 7 && useFileScan) { ... }
无论何时评估表达式,都会运行昂贵的调用,即使它不需要。绕过该语句会跳过文件检查是否useFileScan
为假:
if (useFileScan && CountAllFilesOnDrive('C:\') > 7) { ... }
DeMorgan's 可能会帮助您将“早期退出”移到前面,从而获得更好的平均性能。
请注意,由于保证从左到右的评估,优化器没有太多的自由来修改表达式。
考虑到可读性和维护性。如果您有一组相当复杂且难以阅读的布尔表达式,那么 DeMorgan 定理可能是将表达式简化为更易于阅读/维护的好方法,但它仍然有效/与原始表达式一致。
另一方面,如果一个更冗长的表达式更容易阅读并且减少表达式,虽然在逻辑上是等价的,但使其更难以理解,请保持原样。
在我能想到的几乎所有实际案例中,布尔运算符的排列对整体性能没有明显影响。如果您的程序等待数据库、网络等,它将花费比那些微小操作更多的时间。如果您编写的程序真正发挥作用,最好跳过 C# 并改用 C++。
DeMorgan 本身可能与短路评估完全无关。
return !(exp1 || exp2);
return !exp1 && !exp2;
编译为
if( exp1 ) return !(true); else return !(exp2);
if(!(!exp1)) return false; else return !(exp2);
取消 s并not
折叠常量后,它们是相同的。
更重要的情况是评估顺序;把容易引起短路的东西放在表情前面。编译器无法为您优化这一点,因为它很难检测到语义问题,如副作用,或者如果后面的表达式基于早期的假设做出假设:
return validState() && checkAssumuingValidState();
我同意这些天在优化布尔表达式时可读性和可维护性是最重要的一般性陈述。因此,德摩根定理通常非常有用。
这条规则有一个例外。如果布尔表达式改变了德摩根定理优化的表达式,它可能更难维护。考虑一个具有多个输入的表达式,该表达式已优化为仅显示几个布尔条件。对所需布尔逻辑的一项更改可能会迫使某人再次列出所有可能的布尔组合,然后重新优化。如果表达式以未优化的格式保留,则更改将花费更少的步骤来完成。
更多地从轶事的角度来看,我想知道对团队进行有关德摩根定理和卡诺图等的教育是否会减少不必要的/低效的布尔表达式。也许如果有人对这些方法有很好的理解,他/她会倾向于产生更好的表达方式。例如,我最近在我维护的软件的代码中遇到了这个布尔表达式:
if ({boolean variable} != true && false)
考虑到逻辑表达式求值的短路规则,C# 优化器实际上不能做太多事情。因此,除非它允许您看到其他有用的重构(当然它可以帮助使您的代码更清晰),否则应用德摩根定律不会有太大的作用。
但是在某些情况下,您可以通过其他类型的表达式优化来显着提高性能。例如,这些条件应该交换
if ( costly_boolean_function() && cheap_often_false_boolean_function() )
SQL 查询优化器理所当然地这样做,因为 SQL 没有短路。查询优化器将积极地重新排列连接的 WHERE 子句谓词(形式c1 AND c2 AND ... cn
为 )以将最便宜的条件放在首位,因为它们可能会评估为假并避免评估更昂贵的条件的需要。
首先处理可维护性和高级优化。
然后处理低级优化。
对于我们所有非 CS 专业的学生:
德摩根定律是通过否定将逻辑运算符“和”和“或”相互关联的规则,即:
非(P 或 Q)=(非 P)与(非 Q)
非(P 与 Q)=(非 P)或(非 Q)
德摩根定律可用于将其简化为范式,例如析取范式 (DNF) 或合取范式 (CNF)。基本上这意味着它要么
DNF: (a and b and c) OR (e and f and g) ...
或者
CNF: (a or b or c) AND (e or f or g) ....
您可以将 NOT 放在最低级别。
我同意之前的海报,您应该针对可读性和理解进行优化。