C++/CX的机能陷阱
副标题#e#
利用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参数的方法获取到的字符串。因为随后的引用并不会引入特另外复制,所以不要担忧利用第二个引用。
#p#副标题#e#
荟萃
与C++中常见的荟萃对比,WinRT中的荟萃长短常昂贵的。和.NET中可调查的荟萃一样,对WinRT荟萃的每一次修改城市发生一个通知。该通知主要用于XAML数据绑定以便于更新UI。
#p#分页标题#e#
在初始化期间制止这种损失的一种方法是,首先在仓库上建设并填充一个尺度的vector,然后利用move函数初始化一个platform vector。你可以或许这样做,因为尺度的vector将会被销毁,同时它的动态内存无论如何城市被释放。
在更新许多元素的时候,思量利用ReplaceAll要领。这仅会触发一个通知而不是每一笔记录一个通知。在WPF和Silverlight中没有与之相对应的要领,因为这些UI仓库自己不支持一次性插入可能移除多个条目。
WinRT荟萃中的另一种机能耗损来自于元素的读取。WinRT荟萃是以接口的形式袒露的,因此它们是虚的,这就意味着它们并不能像普通的函数那样被内联。另外,每一次读取都需要举办范畴查抄。所以假如你需要多次读取同一个值,思量将它复制到一个局部变量中,不要每次都从荟萃中读取。实际上复制的缺点是,你必需复制值可能增加工具上的引用数,这是一个连锁操纵。
完全制止这种耗损的一种方法是在迭代荟萃之前复制它。分派一个正确巨细的局部vector,然后在ArrayReference上利用GetMany函数。然后团结利用ReplaceAll要领,你就可以或许对荟萃举办屡次迭代,仅需要超过WinRT界线三次就可以或许做一系列巨大的修改。
WinRT接口
和传统的COM一样,在WinRT中一个工具的成员仅会通过接口袒露。你永远都不行能直接会见工具。C++/CX通过做须要的隐式转换对你埋没了这些细节。这样做之所以须要的一个常见原因是,可以满意挪用非默认接口上的要领时的需要。
WinRT中的转换不是便宜的。它需要挪用QueryInterface这个虚要领,同时有一个增加引用数的连锁操纵。一旦完成了对非默认接口的挪用,还需要另一个淘汰引用数的连锁操纵。
类库作者:
确保类中所有的常用要领在默认接口上都是可用的,这样就不需要转换成另一个接口了。
假如要对同一个非默认接口举办多次挪用,那么建设一个该接口范例的局部变量。这样仅需要执行一次转换,而不是每次要领挪用时都做一次转换。
类
在任何大概的时候你都应该利用仓库分派可能unique_ptr类。因为这样你将得到所有选项的最好机能。
在你确实需要一个巨大生命周期的时候,你的下一个选择是通过一个shared_ptr会见的普通C++类。这种方法和上面选项之间的主要区别在于引用数开销。
你选择的最后一种手段应该是ref类。一个ref类拥有和shared_ptr相似的引用数语义,可是可以或许带来其他基于WinRT的开销。所以仅在需要将类通报到一个WinRT函数可能在XAML的数据绑定中利用ref类。
在利用ref类的时候,只管保持较浅的担任条理。WinRT担任和C++担任纷歧样,它有特另外开销。
XAML数据绑定
在WinRT+XAML数据绑定中你应该制止实现INotifyPropertyChanged,除非你确实但愿在属性被填充之后产生改变。同样的,不要袒露民众set函数,除非UI确实需要修改数据。
XAML所挪用的get函数应该是便宜的。不只仅是因为它们是在UI线程上被挪用的,还因为我们大概会挪用它们多次。所以不要在get函数中分派内存可能执行昂贵的计较。
对付所有基于XAML的UI(WPF、Silverlight、WinRT+XAML)而言,一点很是重要的发起是保持较浅的数据条理。绑定表达式中的每一个点都代表了一次属性改变事件,而为了保持屏幕实时更新数据绑定引擎必需监听这些事件。