使用C++/CX編寫應用程式和編寫正常的C++應用程式不一樣。純C++代碼和Windows運行時(WinRT)之間的互通性出奇的昂貴。基於Sridhar Madhugiri的視頻 C++/CX 最佳實戰中的內容,我們在本文中列舉了一些在Windows 8開發中避免效能問題的方式。
邊界
在應用程式的邊界上會產生多種效能障礙。
資料轉換就是其中的一個例子。考慮一下一個Web服務用戶端和應用程式剩餘部分之間的典型邊界。大多數Web服務是使用UTF-8編碼的,而大多數Windows應用程式的內部則是使用UTF-16編碼的。在Windows中UTF-16編碼是如此的流行以致於人們有時會將它錯誤地稱為“Unicode”編碼。資料轉換的成本可能是確定的,也可能廣泛變化,這依賴於它在資料本身中的特定值。
下一種效能消耗來自於類型轉換。例如,你可能需要一個wstring,但是卻有一個wchar_t *。儘管在記憶體中每種類型所包含的資料看起來是一樣的,但是將這些內容從一個資料結構複製到另一個資料結構依然是有效能成本的。
最後一種效能消耗來自於資料複製操作。有時候你必須為邊界處的資料複製付出代價,哪怕它們並不需要資料轉換和類型轉換。
我們為什麼要在現在討論這些內容呢?原因是WinRT本身就是應用程式和作業系統其餘部分之間的邊界。編寫高效能C++/CX應用程式的本質就是識別邊界並在可能的情況下避免跨越邊界。
如果跨越WinRT邊界的操作無法避免,那麼就尋找一些方式減少資料複製、類型轉換和資料轉換操作的數量。例如,如果資料來源和目標都使用UTF-8編碼,那麼就沒必要將資料轉換為UTF-16,因為你最終還是需要將其再轉換回來。
字串
在大多數應用程式中字串都是主要的資料類型。檔案系統、Web服務、UI、訊息、符文和契約等領域對字串的依賴性日益加深。不幸的是人們所使用的字串類型非常多。
在內部,大多數應用程式可能會使用std::wstring或者std::wchar_t*,你所依賴的大多數第三方類庫也是如此。但是在與WinRT類庫進行通訊的時候你需要切換到Platform::String^。每一次轉換都需要一次記憶體配置和一次資料複製操作。
String^和本地C++版本之間的一個關鍵區別是:String^是不可變的。WinRT運行時對不可變字串的這種強調可能來自於.NET和CLR。正如^符號所表示的,String^也是引用計數。
人們可能會對可變和不可變字串相關的優點爭論一整天,但是最終只有一個事實。因為C++標準類庫只理解可變字串,而WinRT僅理解不可變字串,所以對這兩者你都必須進行處理。正如前面所提到的,這意味著需要對字串進行複製。
類庫作者:如果你正在構建一個一般用途的類庫供他人使用,那麼你應該考慮提供多個不同版本的API,為每種字串類型提供一個API。這樣你就不需要猜測API的使用者在調用類庫的時候使用的是哪種字串類型了。
很多基於字串的操作實際上並不需要使用字串,但是開發人員寧可選擇使用字串迭代器。因為可變和不可變資料結構的迭代操作是一樣的,你可以在使用常規xxx_iterator( begin(string), end(string), …)文法的字串平台上直接建立STL樣式的迭代器。
另外,首先要尋找直接返回wchar_t*的API,而不是將它封裝成一個wstring。如果你找到了這樣的API,那麼你就能夠通過數組中第一個元素的地址以及數組的長度建立一個新的platform string。這樣就不需要建立一個在匹配的platform string被建立之後立即就會被廢棄的wstring。
調用帶有字串引用(StringReference )類型輸入參數的WinRT API時有一個小竅門。你可以向一個參數類型為platform string的WinRT函數傳遞一個wchar_t* 或者wstring參數,這種情況下將建立一個輕量級外觀。無論如何,這裡有一些需要注意的地方。
字串必須是空終止否則將會拋出一個錯誤。
如果字串在函數之外的任何地方發生了變化,那麼結果將無法確定。
如果函數之內有任何字串的引用,那麼無論如何都會產生一個完整副本。
上面的第1條內容很容易驗證,第2條則僅會在碰見安全執行緒問題的時候發生。在大多數環境下這應該是一個非常有用的技巧。
類庫作者:為了確保上面的方案是真實可能的,首先盡量避免讓它引用你以StringReference參數的方式擷取到的字串。因為隨後的引用並不會引入額外的複製,所以不要擔心使用第二個引用。