在C#中优化if语句(a>&0;& b> 0&& a + b == c)

时间:2012-10-15 17:10:32

标签: c# optimization if-statement

我目前正在做一些涉及邻接矩阵的图形计算,而我正在优化它的每一点。

我认为可以优化的说明之一是标题中的一个,原始形式:

if ((adjMatrix[i][k] > 0) && (adjMatrix[k][j] > 0) && (adjMatrix[i][k] + adjMatrix[k][j] == w))

但为了方便起见,我会坚持使用标题中提供的表格:

if (a > 0 && b > 0 && a + b == c)

我不喜欢的是> 0部分(作为邻接矩阵,在它的初始形式中它只包含0和1,但随着程序的进展,零从2开始被替换为数字,直到没有更多的零。

我做了一个测试并删除了> a和b均为0,并且有显着改善。在60088次迭代中,有一个减少792ms ,从3672ms减少到2880ms,这是原始时间的78%,这对我来说是非常好的。

所以我的问题是:你能想到在C#中优化这样的语句并获得相同结果的某种方法吗?也许是一些按位操作或类似的东西,我对它们并不十分熟悉。

回答每一个想法,即使它不适合。我会自己做速度测试,让你知道结果。

编辑:这是一个编译器,我将在我的计算机上自己运行它。我刚刚描述的不是我抱怨的问题/瓶颈。它的当前形式的程序可以满足我的需求,但我只想推进它并使其尽可能基本和优化。希望这能澄清一点。

编辑我相信提供完整的代码是有用的,所以在这里,但请记住我在下面的粗体中所说的内容。 我想严格关注if语句。该程序基本上采用邻接矩阵并存储所有存在的路径组合。然后根据一些系数进行排序和修剪,但是我没有包括在内。

int w, i, j, li, k;
int[][] adjMatrix = Data.AdjacencyMatrix;
List<List<List<int[]>>> output = new List<List<List<int[]>>>(c);

for (w = 2; w <= 5; w++)
{
    int[] plan;

    for (i = 0; i < c; i++)
    {
        for (j = 0; j < c; j++)
        {
            if (j == i) continue;
            if (adjMatrix[i][j] == 0)
            {
                for (k = 0; k < c; k++) // 11.7%
                {
                    if (
                        adjMatrix[i][k] > 0 && 
                        adjMatrix[k][j] > 0 && 
                        adjMatrix[i][k] + adjMatrix[k][j] == w) // 26.4%
                    {
                        adjMatrix[i][j] = w;

                        foreach (int[] first in output[i][k])
                            foreach (int[] second in output[k][j]) // 33.9%
                            {
                                plan = new int[w - 1];
                                li = 0;

                                foreach (int l in first) plan[li++] = l;
                                plan[li++] = k;
                                foreach (int l in second) plan[li++] = l;

                                output[i][j].Add(plan);
                            }
                    }
                }

                // Here the sorting and trimming occurs, but for the sake of
                // discussion, this is only a simple IEnumerable<T>.Take()
                if (adjMatrix[i][j] == w)
                    output[i][j] = output[i][j].Take(10).ToList();
            }
        }
    }
}

使用优化构建的分析器结果添加了评论

顺便说一句,时间结果是通过这段代码获得的(没有排序和修剪,这大大增加了执行时间)。我的测量中没有其他部分。在此代码之前有一个Stopwatch.StartNew(),紧接着是一个Console.WriteLine(EllapsedMilliseconds)。

如果您想了解大小,邻接矩阵有406行/列。所以基本上只有for-instructions组合执行许多迭代,所以我没有很多优化选项。速度目前不是问题,但我想确保它已经准备就绪。

为了排除“优化其他部分”的问题,本主题也有讨论的余地,但对于这个具体问题,我只想找到解决方案作为一个抽象的问题/概念。它可以帮助我和其他人理解C#编译器如何工作并处理if语句和比较,这是我的目标。

6 个答案:

答案 0 :(得分:6)

对于签名变量 a b ,您可以将a>0 && b>0替换为(a-1)|(b-1) >= 0

同样,条件x == w可以表示为(x - w)|(w - x) >= 0,因为当x != w表达式的左侧或右侧部分将切换符号位时,该位由位保留明智的。放在一起的所有内容都将(a-1)|(b-1)|(a+b-w)|(w-a-b) >= 0表示为单一比较。

或者,轻微的速度优势可能来自推杆 增加顺序的可能性:

哪个更有可能(a|b)>=0(a+b)==w

答案 1 :(得分:4)

我不知道C#如何优化这样的事情,但尝试将adjMatrix[i][k]adjMatrix[k][j]存储在临时变量中并不是很难读取内存两次。看看这是否以任何方式改变了事情。

很难相信算术和比较操作是这里的瓶颈。最有可能的是内存访问或分支。理想情况下,应以线性方式访问内存。你能做些什么来使它变得更线性吗?

很高兴看到更多代码可以提出更具体的建议。

更新:您可以尝试使用二维数组(int[,])而不是锯齿状数组(int[][])。这可能会提高内存局部性和元素访问速度。

答案 2 :(得分:3)

逻辑测试的顺序可能很重要(如其他答案中所述)。由于您使用的是短路逻辑测试(&amp;&amp;&amp;&amp;),因此从左到右评估条件,并且发现第一个条件为假,将导致程序停止评估条件和继续执行(不执行if块)。因此,如果有一个条件比其余条件更可能是false,那么应该首先进行,下一个应该是下一个最有可能成为false等的条件。

另一个很好的优化(我怀疑它实际上是什么让你的性能提升 - 而不是简单地删除一些条件)是将你从数组中提取的值分配给局部变量。

您正在使用adjMatrix[i][k]两次(以及adjMatrix[k][j]),这迫使计算机挖掘数组以获取值。相反,在if语句之前,每次都将它们设置为局部变量,然后对这些变量进行逻辑测试。

答案 3 :(得分:1)

我同意其他人的观点,他们认为这个简单的陈述不太可能是您的瓶颈,并在您决定优化此特定行之前建议进行分析。但是,作为一个理论实验,你可以做几件事:

  1. 零检查:检查a != 0 && b != 0可能比a >= 0 && b >= 0稍快一些。由于你的邻接矩阵是非负的,你可以安全地做到这一点。

  2. 重新排序:如果仅a + b == c测试更快,请先尝试使用此测试,然后再单独测试ab。我怀疑这会更快,因为添加和相等检查比零检查更昂贵,但它可能适用于您的特定情况。

  3. 避免双重索引:使用ILDASM或等效项查看生成的IL,以确保数组索引仅被解除引用一次,而不是两次。如果不是,请在检查前将它们放入局部变量中。

答案 4 :(得分:1)

除非您正在调用函数,否则不会优化条件。这是毫无意义。但是,如果你真的想要记住一些简单的事情

检查条件是否为零(或不是),如果设置了最高位(或不是),并且比较(==或!=)基本上是a-b并检查其是否为零(== 0 )或不(!= 0)。所以a是无符号的,则&gt; 0与!= 0相同。如果a是有符号的,则&lt; 0非常好(这使用最高位检查)并且优于&lt; = 0。但无论如何只知道这些规则可能有所帮助。

同时启动一个分析器,你会看到条件在001%的时间。如果有的话你应该问如何写一些不需要条件的东西。

答案 5 :(得分:0)

您是否考虑过颠倒逻辑?

if (a > 0 && b > 0 && a + b == c)

可以改写为:

if (a == 0 || b == 0 || a + b != c) continue;

因为如果任何语句都是假的,你不想在循环中做任何事情,那么尽量尝试中止(如果运行时那么聪明,我假设)。

最重的操作应该是最后一次,因为如果第一个语句为真,则不需要检查其他语句。我认为增加是最重要的部分,但是分析它可能会讲述一个不同的故事。

但是,我没有将这些情景描述为我自己,并且在这些微不足道的条件下,它甚至可能是一个缺点。看到你的发现会很有意思。