对于三种情况的排列,最快的算法是什么?

时间:2013-06-20 14:10:24

标签: c optimization boolean atmega

有人可以帮我解决最短步骤评估三种情况的最快方法吗? 我有三个条件,如果这两个条件中的任何一个都是真的,则整个表达式变为true其他false

我尝试了两种方法:

if ((condition1 && condition2) || 
    (condition1 && condition3) || 
    (condition2 && condition3))

另一种方法是引入变量i

i = 0;
if (condition1) i++;
if (condition2) i++;
if (condition3) i++;
if (i >= 2)
    //do something

我想要比上面两种方法更好的其他有效方法。

我在内存受限的环境中工作(Atmeta8有8 KB的闪存)并且需要一个在C中工作的解决方案。

8 个答案:

答案 0 :(得分:9)

这可以简化为:

if((condition1 && (condition2 || condition3)) || (condition2 && condition3))
    //do something

根据每种情况的可能性,您可以优化排序以获得更快的短路(尽管这可能是过早的优化......)

答案 1 :(得分:4)

总是很难给出一个更好的"更好的"解决方案(在哪方面更好 - 代码行,可读性,执行速度,机器代码指令的字节数,......?)但是由于你在这种情况下询问执行速度,我们可以专注于此。

您可以引入您建议的变量,并在知道答案后使用它将条件简化为简单的小于条件。在大多数体系结构中,小于条件可以简单地转换为两个机器代码指令(例如,CMP(比较),然后是JL(如果小于,则跳转)或JNL(如果不是更少则跳转)比英特尔IA-32)。运气好的话,编译器会注意到(或者你可以自己动手,但我更喜欢在任何地方都有相同模式的清晰度)trues < 2 总是在前两个if()语句,并对其进行优化。

int trues = 0;
if (trues < 2 && condition1) trues++;
if (trues < 2 && condition2) trues++;
if (trues < 2 && condition3) trues++;
// ...
if (trues >= 2)
{
    // do something
}

这一点,一旦得知答案,就会将conditionN的可能复杂评估简化为简单的比较,因为大多数语言的布尔短路行为。

另一种可能的变体,如果您的语言允许您将布尔条件转换为整数,则可以利用它来减少源代码行的数量。但是,您仍将评估每个条件。

if( (int)(condition1)
  + (int)(condition2)
  + (int)(condition3)
  >= 2)
{
    // do something
}

这是基于这样的假设,即将一个布尔值的FALSE值转换为整数,结果为0,并且转换为TRUE会导致1.您也可以使用条件运算符获得相同的效果,但要注意它可能会引入额外的分支

if( ((condition1) ? 1 : 0)
  + ((condition2) ? 1 : 0)
  + ((condition3) ? 1 : 0)
  >= 2)
{
    // do something
}

根据编译器的优化器的智能程度,可能能够确定一旦任何两个条件评估为真,整个条件将始终评估为真,并基于优化在那。

  • 请注意除非您实际上已经对您的代码进行了分析并确定这是罪魁祸首,否则这可能是过早优化的情况。 始终努力使代码首先被人类程序员读取,然后快速由计算机执行,除非您能够显示您正在寻找的特定代码片段的明确证据at是实际性能瓶颈。 了解该分析器的工作原理并充分利用它。请记住,在大多数情况下,程序员时间比CPU时间要贵很多,而且需要更长时间才能让维护程序员解析。
  • 此外,编译器是非常聪明的软件;有时它们实际上会检测所编写代码的 intent ,并且能够使用特定的构造来使这些操作更快,但这依赖于它能够确定您要执行的操作。一个完美的例子是使用中间变量交换两个变量,在IA-32上可以使用XCHG消除中间变量来完成,但编译器必须能够确定你实际上是在做这个而不是something clever which may give another result in some cases
  • 由于绝大多数非明确一次性软件所花费的大部分时间都花费在维护模式上(而且许多一次性软件都是有效的,并且很长时间超过了预期的最佳日期之前),所以优化可维护性,除非在其他方面成本不可接受。当然,如果你在紧密循环中评估这些条件数万亿次,那么目标优化可能是有意义的。但是,从性能的角度来看,探查器会准确地告诉您需要仔细检查代码的哪些部分,这意味着您可以避免不必要地使代码复杂化。

以上警告说,我最近一直在研究代码,乍一看几乎肯定会被认为是过早的细节优化。如果您需要高性能并使用分析器来确定代码的哪些部分是瓶颈,那么优化就不成熟。 (但是,根据具体情况,他们可能仍然是不明智的。)

答案 2 :(得分:3)

取决于您的语言,我可能会采用以下方式:

$cond = array(true, true, false);

if (count(array_filter($cond)) >= 2)

if (array_reduce($cond, function ($i, $k) { return $i + (int)$k; }) >= 2)

答案 3 :(得分:1)

对此没有绝对的答案。这在很大程度上取决于底层架构。例如。如果你用VHDL或Verilog编程一些硬件电路,那么肯定第一个会给你最快的结果。我假设您的目标是某种CPU,但即使在这里,它也将取决于目标CPU,它支持的指令以及它们将采用的时间。此外,您没有指定目标语言(例如,您的第一种方法可能会被短路,这会严重影响速度)。

如果我什么都不知道,我会推荐第二种解决方案 - 只是因为你的意图(至少有2个条件应该是真的)会更好地反映在代码中。

两种解决方案的速度差异不是很高 - 如果这只是一些逻辑而不是一些内部循环执行很多次的部分,我甚至会猜测过早优化并尝试优化某处其他

答案 4 :(得分:1)

由于我们没有采用深度流水线架构,因此分支机构避免可能没有价值,这通常会引导桌面开发人员提供的优化。在这里,捷径是金色的。

如果你选择:

if ((condition1 && (condition2 || condition3)) || (condition2 && condition3))

那么你可能有最好的机会,不依赖于任何进一步的信息,从编译器中获取最好的机器代码。在汇编时,可以将condition2分支的第二次评估返回到condition3的第一次评估以减少代码大小,但是没有可靠的方法在C中表达这一点。

如果你知道你通常会通过考试,并且你知道通常会导致哪两个条件,那么你可能更愿意写:

if ((rare1 || rare2) && (common3 || (rare1 && rare2)))

但是编译器仍然很有可能完全重新排列并使用自己的快捷方式。

您可能希望使用__builtin_expect()_Rarely()或编译器提供的任何内容来注释事物,以指示条件的可能结果。

然而,更有可能有意义地提高性能的是识别条件之间的任何常见因素或以简化整体测试的方式测试条件的任何方式。

例如,如果测试很简单,那么在装配中你几乎可以肯定做一些基本的诡计,带来快速积累条件。将其移植回C 有时可行。

答案 5 :(得分:0)

您可以考虑简单地添加它们。如果您使用标准stdbool.h中的主题,那么true为1,(condition1 + condition2 + condition3) >= 2就是您想要的。

但它仍然仅仅是微观优化,通常你不会通过这种技巧获得大量的生产力。

答案 6 :(得分:0)

您似乎想要评估所有条件,因为您在自己的问题中提出了这样的解决方案。如果条件是非常复杂的公式,需要花费很多CPU周期来计算(比如大约几百毫秒),那么您可以考虑使用线程同时评估所有三个条件以获得加速。类似的东西:

pthread_create(&t1, detached, eval_condition1, &status);
pthread_create(&t2, detached, eval_condition2, &status);
pthread_create(&t3, detached, eval_condition3, &status);
pthread_mutex_lock(&status.lock);
while (status.trues < 2 && status.falses < 2) {
    pthread_cond_wait(&status.cond, &status.lock);
}
pthread_mutex_unlock(&status.lock);
if (status.trues > 1) {
    /* do something */
}

这是否能让您加快速度取决于计算条件的成本。计算时间必须主导线程创建和同步开销。

答案 7 :(得分:0)

试试这个:

unsigned char i;

i = condition1;
i += condition2;
i += condition3;
if (i & (unsigned char)0x02)
{
    /*
    At least 2 conditions are True
    0b00 - 0 conditions are true
    0b01 - 1 conditions are true
    0b11 - 3 conditions are true 
    0b10 - 2 conditions are true
    So, Checking 2nd LS bit is good enough.
    */
}