繁体   English   中英

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

[英]Optimize if-statement (a > 0 && b > 0 && a + b == c) in C#

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

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

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语句和比较,这是我的目标。

对于带符号变量ab,可以用(a-1)|(b-1) >= 0替换a>0 && b>0

同样,条件x == w可以表示为(x - w)|(w - x) >= 0 ,因为当x != w左边或表达式的右边部分将切换符号位,这是按位保留。 放在一起的所有东西都是(a-1)|(b-1)|(a+bw)|(wab) >= 0表示为单个比较。

或者,将概率按升序排列可能会带来轻微的速度优势:

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

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

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

很高兴看到更多的代码来建议更具体的东西。

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

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

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

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

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

  1. 零检查:检查a != 0 && b != 0可能比a >= 0 && b >= 0快一些。 由于您的邻接矩阵是非负的,您可以安全地执行此操作。

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

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

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

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

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

你考虑过扭转逻辑吗?

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

可以改写为:

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM