使用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參數的方式獲取到的字符串。因為隨後的引用並不會引入額外的復制,所以不要擔心使用第二個引用。
與C++中常見的集合相比,WinRT中的集合是非常昂貴的。和.NET中可觀察的集合一樣,對WinRT集合的每一次修改都會產生一個通知。該通知主要用於XAML數據綁定以便於更新UI。