简体   繁体   English

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

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

I'm currently doing some graph calculations that involves adjacency matrices, and I'm in the process of optimizing every little bit of it. 我目前正在做一些涉及邻接矩阵的图形计算,而我正在优化它的每一点。

One of the instructions that I think can be optimized is the one in the title, in it's original form: 我认为可以优化的说明之一是标题中的一个,它的原始形式:

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

But for ease I'll stick to the form provided in the title: 但为了方便起见,我将坚持标题中提供的表格:

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

What I don't like is the > 0 part (being an adjacency matrix, in it's initial form it contains only 0 and 1, but as the program progresses, zeros are replaced with numbers from 2 onwards, until there are no more zeros. 我不喜欢的是> 0部分(是一个邻接矩阵,在它的初始形式中它只包含0和1,但随着程序的进展,零被从2开始的数字替换,直到没有更多的零。

I've done a test and removed the > 0 part for both a and b, and there was a significant improvement. 我做了一个测试并删除了a和b的> 0部分,并且有了显着的改进。 Over 60088 iterations there was a decrease of 792ms , from 3672ms to 2880ms, which is 78% of the original time, which to me is excellent. 超过60088次迭代减少了792ms ,从3672ms减少到2880ms,这是原始时间的78%,这对我来说非常好。

So my question is: can you think of some way of optimizing a statement like this and having the same result, in C#? 所以我的问题是:你能想到在C#中优化这样的语句并获得相同结果的某种方法吗? Maybe some bitwise operations or something similar, I'm not quite familiar with them. 也许是一些按位操作或类似的东西,我对它们并不熟悉。

Answer with every idea that crosses your mind, even if it's not suitable. 回答每一个想法,即使它不适合。 I'll do the speed testing myself and let you know of the results. 我会自己做速度测试,让你知道结果。

EDIT: This is for a compiler that I'm gonna run it myself on my computer. 编辑:这是一个编译器,我将在我的计算机上自己运行它。 What I just described it's not a problem / bottleneck that I'm complaining of. 我刚刚描述的不是我抱怨的问题/瓶颈。 The program in it's current form runs fine for my needs, but I just want to push it forward and make it as basic and optimized as possible. 它的当前形式的程序可以满足我的需求,但我只想推进它并使其尽可能基本和优化。 Hope this clarifies a little bit. 希望这能澄清一点。

EDIT I believe providing the full code it's a useful thing, so here it is, but keep in mind what I said in the bold below. 编辑我相信提供完整的代码它是一个有用的东西,所以在这里,但请记住我在下面的粗体说。 I want to concentrate strictly on the if statement . 我想严格关注if语句 The program essentially takes an adjacency matrix and stores all the route combinations that exists. 该程序基本上采用邻接矩阵并存储所有存在的路径组合。 Then there are sorted and trimmed according to some coefficients, but this I didn't included. 然后根据一些系数进行排序和修剪,但我没有包括在内。

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();
            }
        }
    }
}

Added comments with profiler results in optimized build . 添加了有关优化构建的分析器结果的注释

By the way, the timing results were obtained with exactly this piece of code (without the sorting and trimming which dramatically increases execution time). 顺便说一句,时间结果是通过这段代码获得的(没有排序和修剪,这大大增加了执行时间)。 There weren't another parts that were included in my measurement. 我的测量中没有其他部分。 There is a Stopwatch.StartNew() exactly before this code, and a Console.WriteLine(EllapsedMilliseconds) just after. 在此代码之前有一个Stopwatch.StartNew(),紧接着是一个Console.WriteLine(EllapsedMilliseconds)。

If you want to make an idea about the size, the adjacency matrix has 406 rows / columns. 如果您想了解大小,邻接矩阵有406行/列。 So basically there are only for-instructions combined which execute many many iterations, so I haven't got many options of optimizing. 所以基本上只有for-instructions组合执行许多迭代,所以我没有很多优化选项。 Speed is not currently a problem, but I want to make sure I'm ready when it'll become. 速度目前不是问题,但我想确保它已经准备就绪。

And to rule out the 'optimize another parts' problem, there is room for talk in this subject also, but for this specific matter, I just want to find solution for this as an abstract problem / concept. 为了排除“优化其他部分”的问题,本主题也有讨论的余地,但对于这个具体问题,我只想找到解决方案作为一个抽象的问题/概念。 It may help me and others understand how the C# compiler works and treats if-statements and comparisons, that's my goal here. 它可以帮助我和其他人理解C#编译器如何工作并处理if语句和比较,这是我的目标。

You can replace a>0 && b>0 with (a-1)|(b-1) >= 0 for signed variables a and b . 对于带符号变量ab,可以用(a-1)|(b-1) >= 0替换a>0 && b>0

Likewise, the condition x == w can be expressed as (x - w)|(w - x) >= 0 , since when x != w either left or the right part of the expression will toggle the sign bit, which is preserved by bit-wise or . 同样,条件x == w可以表示为(x - w)|(w - x) >= 0 ,因为当x != w左边或表达式的右边部分将切换符号位,这是按位保留。 Everything put together would be (a-1)|(b-1)|(a+bw)|(wab) >= 0 expressed as a single comparison. 放在一起的所有东西都是(a-1)|(b-1)|(a+bw)|(wab) >= 0表示为单个比较。

Alternatively a slight speed advantage may come from putting the probabilities in increasing order: 或者,将概率按升序排列可能会带来轻微的速度优势:

Which is more likely (a|b)>=0 or (a+b)==w ? 哪个更有可能(a|b)>=0(a+b)==w

I don't know how well C# optimizes things like this, but it's not so difficult to try to store adjMatrix[i][k] and adjMatrix[k][j] in temporary variables not to read memory twice. 我不知道C#如何优化这样的东西,但是尝试将adjMatrix[i][k]adjMatrix[k][j]在临时变量中并不难以读取内存两次。 See if that changes things in any way. 看看这是否以任何方式改变了事情。

It's hard to believe that arithmetic and comparison operations are the bottleneck here. 很难相信算术和比较操作是这里的瓶颈。 Most likely it's memory access or branching. 最有可能的是内存访问或分支。 Ideally memory should be accessed in a linear fashion. 理想情况下,应以线性方式访问内存。 Can you do something to make it more linear? 你能做点什么让它变得更线性吗?

It would be good to see more code to suggest something more concrete. 很高兴看到更多的代码来建议更具体的东西。

Update: You could try to use two-dimensional array ( int[,] ) instead of a jagged one ( int[][] ). 更新:您可以尝试使用二维数组( int[,] )而不是锯齿状数组( int[][] )。 This might improve memory locality and element access speed. 这可能会改善内存局部性和元素访问速度。

The order of the logical tests could be important (as noted in other answers). 逻辑测试的顺序可能很重要(如其他答案中所述)。 Since you are using the short circuit logical test (&& instead of &), then the conditions are evaluated from left to right, and the first one it finds that is false, will cause the program to stop evaluating the conditional and continue executing (without executing the if block). 由于您使用的是短路逻辑测试(&&而不是&),因此从左到右评估条件,并且发现第一个条件为假,将导致程序停止评估条件并继续执行(不使用执行if块)。 So if there is one condition is the far more likely to be false than the rest, that one should go first, and the next should be the next most likely one to be false , etc. 因此,如果有一个条件是比其他条件更可能是false的,那么应该先行,而下一个应该是下一个最可能是false ,等等。

Another good optimization (which I suspect is really what gave you your performance increase --rather than simply dropping out some of the conditions) is to assign the values you are pulling from the arrays to local variables. 另一个很好的优化(我怀疑它实际上是什么让你的性能提升 - 而不是简单地删除一些条件)是将你从数组中提取的值分配给局部变量。

You are using adjMatrix[i][k] twice (as well as adjMatrix[k][j] ) which is forcing the computer to dig through the array to get the value. 您正在使用adjMatrix[i][k]两次(以及adjMatrix[k][j] ),它正在强制计算机挖掘数组以获取值。 Instead, before the if statement, set each of those to a local variable each time, then do your logic test against those variables. 相反,在if语句之前,每次都将它们设置为局部变量,然后对这些变量进行逻辑测试。

I agree with others who say it's unlikely that this simple statement is your bottleneck and suggest profiling before you decide on optimizing this specific line. 我同意其他人的观点,他们认为这个简单的陈述不太可能是你的瓶颈,并建议在你决定优化这条特定的产品线之前进行分析。 But, as a theoretical experiment, you can do a couple of things: 但是,作为一个理论实验,你可以做一些事情:

  1. Zero-checks: checking for a != 0 && b != 0 will probably be somewhat faster than a >= 0 && b >= 0 . 零检查:检查a != 0 && b != 0可能比a >= 0 && b >= 0快一些。 Since your adjacency matrix is non-negative, you can safely do this. 由于您的邻接矩阵是非负的,您可以安全地执行此操作。

  2. Reordering: if testing just a + b == c is faster, try using this test first and only then test for a and b individually. 重新排序:如果仅测试a + b == c更快,请先尝试使用此测试,然后再单独测试ab I doubt this will be faster because addition and equality check is more expensive than zero checks, but it might work for your particular case. 我怀疑这会更快,因为添加和相等检查比零检查更昂贵,但它可能适用于您的特定情况。

  3. Avoid double indexing: look at the resulting IL with ILDASM or an equivalent to ensure that the array indexes are only dereferenced once, not twice. 避免使用双索引:使用ILDASM或等效项查看生成的IL,以确保数组索引仅被解除引用一次,而不是两次。 If they aren't, try putting them in local variables before the check. 如果不是,请在检查之前尝试将它们放在局部变量中。

Unless you're calling a function you don't optimize conditionals. 除非你正在调用函数,否则你不会优化条件。 Its pointless. 没有用。 However if you really want to theres a few easy things to keep in mind 但是,如果你真的想要记住一些简单的事情

Conditions are checked if something is a zero (or not), if the highest bit is set (or not) and a compare (== or !=) is essentially a - b and checking if its zero (==0) or not (!=0). 检查条件是否为零(或不是),如果设置了最高位(或不是),并且比较(==或!=)基本上是a-b并检查其是否为零(== 0) (!= 0)。 So a is unsigned then a>0 is the same as a!=0. 所以a是无符号的,然后> 0与!= 0相同。 If a is signed then a<0 is pretty good (this uses the check on highest bit) and is better then a<=0. 如果a是有符号的,则<0非常好(这使用最高位检查)并且优于<= 0。 But anyways just knowing those rules may help. 但无论如何只知道这些规则可能有所帮助。

Also fire up a profiler, you'll see conditionals take 001% of the time. 同时启动一个分析器,你会看到条件在001%的时间。 If anything you should ask how to write something that doesnt require conditionals. 如果有的话你应该问如何写一些不需要条件的东西。

Have you considered reversing the logic? 你考虑过扭转逻辑吗?

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

could be rewritten to: 可以改写为:

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

Since you don't want to do anything in the loop if any of the statements are false, then try to abort as soon as possible (if the runtime is that smart, which I assume). 因为如果任何语句都是假的,你不想在循环中做任何事情,那么尽量尝试中止(如果运行时是那么聪明,我假设)。

The operation which is the heaviest should be last, because if first statement is true, the others doesn't need to be checked. 最重的操作应该是最后的,因为如果第一个语句为真,则不需要检查其他语句。 I assumed that the addition is the heaviest part, but profiling it might tell a different story. 我认为增加是最重要的部分,但分析它可能会讲述一个不同的故事。

However, I haven't profiled these scenarios my self, and with such trivial conditionals, it might even be a drawback. 但是,我没有将这些场景描述为我自己,并且在这些微不足道的条件下,它甚至可能是一个缺点。 Would be interesting to see your findings. 看到你的发现会很有趣。

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

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