簡體   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