@csl是,你應該爲了個人資料,找出你的代碼的部分需要優化什麼權利。你應該在優化之後重新進行配置,以確保它有幫助。
這就是說,使用輪廓儀作爲Oracle是不優化的有效方式。一旦確定了導致性能問題的代碼,瞭解這些問題來自何處的更有可能導致您改進,而不是盲目地進行更改,並希望分析器能夠告訴您它變得更快。
一個優化的最根本的規則(配置文件後先)是,如果B方法做了所有同樣的工作方法的一個呢,它不會更快。如果CPU浪費時間等待不在高速緩存中的數據,但同時以相同的方式獲得相同的結果,則很有可能在同一時間內安裝額外的工作。
因此,我首先注意到您的示例,除了MessageBox API的功能遠遠超過設置函數調用的功能之外,因此您的儲蓄將微不足道,因爲必須完成某些操作才能調用消息框。
這導致瞭如下結論:
不可能使代碼比簡單的非託管的程序快:
#include <windows.h>
void func(void)
{
::MessageBoxA(NULL, "Hi all", "Win32 Message Box", MB_OK);
}
int main(void)
{
func();
}
經過優化內聯調用,它肯定本地優化器是相當不錯的,除了準備參數列表和調用函數外,這個程序不會做任何事情,對吧?
嗯,這是不正確的。 C++運行時庫將執行一些設置,這對於此示例不是必需的。您可以通過使用自定義入口點獲得巨大改進,但它看起來只會更快,實際上這些更改來自編譯器隱藏的代碼。
即使消除了C++運行時庫,您仍然留下少量開銷:OS加載程序解析導入,這導致了另一個細節:此處調用是使用函數指針創建的,而不是立即地址。然而,現代CPU分支預測消除了這種間接性的代價。
這真的會成爲主題,順便說一句,在後臺添加的代碼管理。
無論如何,下一個案例。該代碼將不會快,但我懷疑這將是更慢或者,至少後主開始運行:
// compile with /clr
#include <windows.h>
void func(void)
{
::MessageBoxA(NULL, "Hi all", "Win32 Message Box", MB_OK);
}
int main(void)
{
func();
}
這是使用C++互操作混合模式組件。 C++編譯器不能再調用func
,但優化現在是JIT的責任。在這種情況下,C++/CLI編譯器也會關閉代碼安全性,如果沒有權限運行非託管代碼,甚至無法加載混合模式程序集,因此在每次調用時都不會檢查它。
這個程序比第一個程序要慢很多,因爲運行時庫的初始化因.NET的引入而變得非常複雜。但是在JIT運行之後,函數調用將與第一個相同,每個調用的func
的開銷是相同的。
讓我們來看看從問題的另一個案例:
// compile with /clr
#include <windows.h>
#pragma managed(push, off)
void func(void)
{
::MessageBoxA(NULL, "Hi all", "Win32 Message Box", 0);
}
#pragma managed(pop)
int main(void)
{
func();
}
我們還是要加載.NET運行時,我們還是要JIT,我們還是要實際設置的參數並調用MessageBox
。所以這個不能比上面的例子更快。由於func
的實施不是託管代碼,所以JIT不能內聯它。但是現在C++編譯器可以這樣做,因爲func
不再是必須單獨放置的託管函數。如果優化器內嵌func
,我們獲得與前一種情況相同的性能,否則我們會有一個額外的函數調用。真的不是你應該擔心的事情。
下一頁情況:
// compile with /clr:pure
#include <windows.h>
void func(void)
{
::MessageBoxA(NULL, "Hi all", "Win32 Message Box", MB_OK);
}
int main(void)
{
func();
}
現在我們不再有一個混合模式組件和我們不再有C++互操作。相反,編譯器會生成一個匹配的p/invoke簽名(爲什麼你會用手寫p/invoke簽名?)。因此,它不是OS加載程序解析導入,而是.NET的運行時。而且現在JIT可以使用導入地址,理論上可以將固定地址直接編碼到調用指令中,並避免函數指針,儘管我們已經知道分支預測使這種差異沒有實際意義。但是p/invoke是爲了安全而設計的,而不是速度,所以你實際得到的是一些額外的堆棧檢查,以確保p/invoke簽名是正確的。底線:這個版本肯定比較慢,但不夠重要。
讓我們做一個很小的變化:
// compile with /clr:safe
#include <windows.h>
void func(void)
{
::MessageBoxA(NULL, "Hi all", "Win32 Message Box", MB_OK);
}
int main(void)
{
func();
}
好吧,請大會可覈查的。所有p/invoke內容與/clr:pure
相同,但是此代碼不再侷限於在受信任的環境中運行。因此,代碼訪問安全性檢查無法關閉。當環境完全可信時,安全性會檢查快捷方式,並且此示例應用程序未啓用部分信任,所以它沒有太大的區別。在將C++/CLI程序集加載到更大的.NET應用程序的實際場景中,安全檢查的影響可能會很大,因爲每個程序都需要堆棧散步。緩解這種情況的一種方法是,儘可能在調用堆棧中儘可能靠近CAS斷言,任何時候都會按順序執行許多p/invoke調用,但以部分信任方式運行會減慢對本機代碼的調用。
// compile with /clr:safe
[DllImport("user32.dll", CharSet=CharSet::Auto)]
int MessageBox(System::IntPtr, System::String^ text, System::String^ caption, unsigned type);
int main(void)
{
MessageBox(System::IntPtr::Zero, "Hi all", "Win32 Message Box", 0);
}
這是問題的示例#2。這是悲慘的比較慢,但仍可能不足以注意到MessageBox
本身的成本旁邊。原因是用戶提供的p/invoke簽名與編譯器可以使用頭文件自動生成的簽名相比是非常不理想的。支持缺少GetLastError
,但參數類型從SByte*
更改爲System::String^
。現在p/invoke有相當多的工作要做:分配垃圾收集器無法移動的空間並將字符串內容複製到那裏。更糟糕的是,CharSet.Auto
意味着ANSI。 System::String
是Unicode。因此,P/Invoke不僅需要複製字符串內容,還需要執行Unicode - > ANSI轉換。
同樣,對於MessageBox
,開銷與任務相比是微不足道的。但對於其他功能,如glVertex2i
和GetClassName
,複製參數,Unicode < - > ANSI轉換和CAS堆棧行程等額外工作可能會導致性能下降或中斷。
我會測試他們找出來的;) – Blender 2011-05-14 06:20:55
浩神,託管C++!我的眼睛在流血...... – 2011-05-14 06:23:57
這些是對Win32的調用,而不是對本機API的調用。如果它對性能產生真正的影響,我會很驚訝。如果我是你,我會擔心別的事情。 – 2011-05-14 06:40:05