[英]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語句和比較,這是我的目標。
對於帶符號變量a和b,可以用(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語句之前,每次都將它們設置為局部變量,然后對這些變量進行邏輯測試。
我同意其他人的觀點,他們認為這個簡單的陳述不太可能是你的瓶頸,並建議在你決定優化這條特定的產品線之前進行分析。 但是,作為一個理論實驗,你可以做一些事情:
零檢查:檢查a != 0 && b != 0
可能比a >= 0 && b >= 0
快一些。 由於您的鄰接矩陣是非負的,您可以安全地執行此操作。
重新排序:如果僅測試a + b == c
更快,請先嘗試使用此測試,然后再單獨測試a
和b
。 我懷疑這會更快,因為添加和相等檢查比零檢查更昂貴,但它可能適用於您的特定情況。
避免使用雙索引:使用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.