是的,编写良好的C ++速度要快得多。如果您正在编写对性能有严格要求的程序,而您的C ++却不如C快(或百分之几以内),那是有问题的。如果您的ObjC实现与C一样快,那通常是错误的-即该程序可能是ObjC OOD的一个不好的例子,因为它可能会使用一些“肮脏”的技巧进入其所运行的抽象层以下,例如直接ivar访问。
Mike Ash的“比较”极具误导性-我永远不会推荐这种方法来比较您编写的程序的执行时间,也不会推荐它用来比较C,C ++和ObjC。给出的结果是通过禁用编译器优化的测试提供的。在测量执行时间时,禁用优化功能编译的程序几乎不相关。将其视为将C ++与Objective-C进行比较的基准是有缺陷的。该测试还比较了单个功能,而不是整个现实世界中优化的实现-两种语言以不同的方式组合了单个功能。这与优化实施的现实性能基准相去甚远。示例:启用优化功能后,IMP
缓存与虚拟函数调用一样慢。静态分配(与动态分配(例如使用virtual
)相反)和对已知C ++类型的调用(可以绕过动态分配)可以进行积极优化。此过程称为去虚拟化,使用该过程时,virtual
甚至可以声明已声明的成员函数inline
。在Mike Ash测试的情况下,对已声明且具virtual
有空主体的成员函数进行了许多调用:在知道类型的情况下,这些调用将完全优化,因为编译器可以看到实现并能够确定动态分派。不必要。编译器还可以消除对malloc
在优化的版本中(有利于堆栈存储)。因此,在任何C,C ++或Objective-C中启用编译器优化都会在执行时间上产生巨大差异。
这并不是说呈现的结果完全没有用。如果要确定外部API在一个平台或体系结构上pthread_create
或+[NSObject alloc]
在另一个平台或体系结构上花费的时间之间是否存在可衡量的差异,则可以获得有关外部API的一些有用信息。当然,这两个示例将在测试中使用优化的实现(除非您碰巧正在开发它们)。但是,如果要在您编译的程序中将一种语言与另一种语言进行比较,则在禁用优化的情况下,呈现的结果将毫无用处。
对象创建
还考虑在ObjC中创建对象-每个对象都是动态分配的(例如,在堆上)。使用C ++,可以在堆栈上(例如,与创建C结构并在许多情况下调用简单函数一样快),在堆上或作为抽象数据类型的元素创建对象。每次您分配和释放(例如通过malloc / free)时,您都可以引入一个锁。当在堆栈上创建C结构或C ++对象时,不需要锁(尽管内部成员可能使用堆分配),并且它通常仅花费几条指令或几条指令以及一个函数调用。
同样,ObjC对象是引用计数的实例。一个对象成为std::shared_ptr
性能至关重要的C ++的实际需求很少。在C ++中,没有必要或不需要使每个实例成为共享的,引用计数的实例。使用C ++,您可以更好地控制所有权和生存期。
数组和集合
C和C ++中的数组和许多集合也使用强类型容器和连续内存。由于下一个元素成员的地址通常是已知的,因此优化器可以做更多的事情,并且您在缓存和内存中的位置也很好。使用ObjC,对于标准对象(例如NSObject
)而言,这是不现实的。
调度
关于方法,许多C ++实现都很少使用虚拟/动态调用,尤其是在高度优化的程序中。这些是优化器的静态方法调用和饲料。
使用ObjC方法,每个方法调用(objc消息发送)都是动态的,因此是优化程序的防火墙。最终,这会导致在编写对性能有严格要求的ObjC时,您可以做什么和不能做什么使性能保持最低的许多限制或不便之处。这可能导致更大的方法,IMP缓存,频繁使用C。
某些实时应用程序无法在其渲染路径中使用任何ObjC消息传递。没有-音频渲染就是一个很好的例子。ObjC调度根本不是为实时目的而设计的。消息传递对象时,分配和锁定可能会在后台发生,这使得objc消息传递的复杂性/时间变得不可预测,以至于音频渲染可能会错过其截止日期。
其它功能
C ++还为其许多库提供了泛型/模板实现。这些优化得很好。它们是类型安全的,并且可以使用模板进行很多内联和优化(考虑它在编译时发生的多态性,优化和专门化)。C ++添加了一些功能,这些功能在严格的ObjC中是不可用或不可比较的。尝试直接比较非常不同的lang,对象和库并不是那么有用-它只是实际实现的一小部分。考虑到设计和实现的许多方面,最好将问题扩展到库/框架或实际程序。
其他要点
在构建的各个阶段(剥离,消除死代码,内联和早期内联以及链接时间优化),可以更轻松地删除和优化C和C ++符号。这样做的好处包括减少二进制文件大小,减少启动/加载时间,减少内存消耗等。对于单个应用程序,这可能没什么大不了的。但是,如果您确实应该重用大量代码,那么,如果实施了ObjC,则共享库可能会给程序增加很多不必要的负担-除非您准备跳过一些麻烦的难题。因此,可伸缩性和重用也是中/大型项目以及重用率较高的组的因素。
包含的图书馆
ObjC库实现者还针对环境进行了优化,因此其库实现者可以利用某些语言和环境功能来提供优化的实现。尽管在纯ObjC中编写优化程序时存在一些相当大的限制,但Cocoa中仍存在一些高度优化的实现。这是Cocoa的强项之一,尽管C ++标准库(有些人称为STL)也不乏味。Cocoa的抽象级别比C ++高得多-如果您不知道自己在做什么(或应该做的事情),那么在金属表面附近操作确实会花费您。如果您不是某个领域的专家,那么依靠良好的库实现是一件好事,除非您真的准备好学习。同样,可可的环境是有限的。您可以找到可以更好地利用操作系统的实现/优化。
如果您正在编写优化程序,并且在C ++和ObjC上都有这样做的经验,那么干净的C ++实现通常比干净的ObjC快一倍或更快(是的,您可以与Cocoa进行比较)。如果您知道如何进行优化,则通常可以比更高级别的通用抽象做得更好。虽然,某些优化的C ++实现将与Cocoa一样快或慢一些(例如,我最初对文件I / O的尝试比Cocoa慢-主要是因为C ++实现初始化了其内存)。
其中很多取决于您熟悉的语言功能。我使用两种语言,它们都有不同的优势和模型/模式。它们相辅相成,并且都有很好的库。如果您要实现一个复杂的,对性能至关重要的程序,则正确使用C ++的功能和库将为您提供更多控制权,并为优化提供明显优势,例如,在正确的情况下,“好几倍”是一个很好的默认期望(不要指望每次都能获胜,或者没有付出任何努力)。请记住,要充分理解C ++才能真正达到这一点需要花费数年的时间。
我将大多数性能关键路径都保留为C ++,但也认识到ObjC也是解决某些问题的很好的解决方案,并且有一些非常好的库可供使用。