Re: [心得] 記憶體效能瓶頸

看板C_Sharp (C#)作者 (靖)時間9年前 (2016/01/02 15:07), 9年前編輯推噓1(104)
留言5則, 3人參與, 最新討論串2/2 (看更多)
※ 引述《erspicu (.)》之銘言: : 全code.... : http://paste.ofcode.org/3bw8ufpUwuPqT4xNfwUScZH : 你覺得在你預設的猜測中 : a. : color = (uint)((0xff << 24) | ((rgb555 & 0x1f) << 19) | ((rgb555 & 0x3e0) : << 6) | ((rgb555 & 0x7c00) >> 7)); : b. : color = rgb555_Table[rgb555 ]; : 你會覺得哪個速度比較快???? : 一個是好幾次的bitwise計算算出結果.. : 一個是用index撈取array一個步驟 : 答案是 a 比較快... 不管是debug模式下或是一般模式 : (現在一般電腦的情況...cpu效能超高,一般記憶體沒追上cpu速度) : 只是一般模式a快得更多 : 而pointer的寫法 debug模式下,快一般array的操作方式一點,一般模式, : 用指標跟ARRAY速度差不多(應該是一樣).... : pointer在c#好像不只一種方式操作 測試環境為 VS2015 .net 4.6.1 Release build 64位元執行模式 直接觀察反組譯碼 在原測試code中第51行 color = (uint)((0xff << 24) | ((rgb555 & 0x1f) << 19) | ((rgb555 & 0x3e0) << 6) | ((rgb555 & 0x7c00) >> 7)); 這句只會執行一次(最後一次) 而不是10000*65535次 (反而第50行 rgb555 = (ushort)(i & 0xffff); 老老實實的執行了10000*65535次 優化的邏輯真奇怪) 而array只會檢查i有沒有超過陣列界限65535次 不會取值 pointer 則是CLR的bug https://github.com/dotnet/coreclr/issues/2480 每次存取fixed的pointer時 都重新從local variables載入到CPU register (暫時的解法是把pointer複製出來一份) 所以結果為 37X 19X 55X ms ==== 測試code http://paste.ofcode.org/d8tZ28wfK3iA5Mi3kLuvAR 不確定在原測試code 63行有沒有寫錯 color = rgb555_Table_pointer[i]; 測試code通通改成用 color = rgb555_Table_pointer[rgb555]; 註: GC.KeepAlive() 是拿來模擬一個method call 避免取值賦值被優化掉 其方法內容是空白 如果不呼叫GC.KeepAlive() 而自己寫空方法的話 一樣會被優化掉 http://referencesource.microsoft.com/#mscorlib/system/gc.cs,18b31b2edcc0f711 在加上GC.KeepAlive(color); 的狀況下 Array 32XX ms bitwise 34XX ms pointer 30XX ms 而這個測試也順便測試JIT warm up 的影響 以及傳說 "int的操作有特別優化" 這兩件事 看起來是沒什麼影響 ==== : 參考 : http://nbsoftsolutions.com/blog/high-performance-unsafe-c-code-is-a-lie 這篇文章剛好挑到一個在.net中極度優化的類別 string 所以會得到使用指標沒有什麼加速效果的結論 : stringBuilder.Append((char)currentByte); 內部為char array pointer操作 http://referencesource.microsoft.com/#mscorlib/system/text/ stringbuilder.cs.html,a2e7c78d85807da5 : stringBuilder.ToString() 內部為pointer操作 http://referencesource.microsoft.com/#mscorlib/system/text/ stringbuilder.cs,5a97da49a158a3c9 : charBuffer[index++] = (char)currentByte; : new string(charBuffer, 0, index); : *bytePtr++ = currentByte; : new string((sbyte*)byteArray); : charPtr++ = (char)currentByte; : new string(charPtr); string的建構子內部均為為C++實作 以String(char [] value)為例 http://referencesource.microsoft.com/#mscorlib/system/ string.cs,ec408439366007e6 https://github.com/dotnet/coreclr/blob/master/src/vm/ecalllist.h#L220 https://github.com/dotnet/coreclr/blob/master/src/ classlibnative/bcltype/stringnative.cpp#L99 https://github.com/dotnet/coreclr/blob/master/src/vm/object.cpp#L2086 測試出來的結果相當是正常的 如果有比較慢 有可能是做了多餘的轉型導致 網路上也有使用unsafe大幅加速的例子 可參考 http://blog.darkthread.net/post-2010-06-19-use-unsafe.aspx -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 61.230.204.189 ※ 文章網址: https://www.ptt.cc/bbs/C_Sharp/M.1451718428.A.A19.html

01/02 15:28, , 1F
64位元環境的確會有這個短迴圈只跑一次的問題,我也不知道
01/02 15:28, 1F

01/02 15:30, , 2F
是神優化還是BUG,用32BIT或把color提到class field才能
01/02 15:30, 2F

01/02 15:38, , 3F
剛測試 32位元一樣只跑一次 class field就不會只有一次了
01/02 15:38, 3F

01/02 18:24, , 4F
一直覺得優化原則.JIT 若沒摸都反組譯那層 被包著
01/02 18:24, 4F

01/02 18:24, , 5F
沒辦法深入看進去的話 一切都很謎樣... 可能真的要來學了
01/02 18:24, 5F
※ 編輯: fo40225 (61.230.213.119), 01/09/2016 21:39:47
文章代碼(AID): #1MXtSSeP (C_Sharp)
文章代碼(AID): #1MXtSSeP (C_Sharp)