如果我将Objective-C用于低级代码,我的iPhone应用程序是否会对性能产生影响?


68

在iPhone或其他便携式硬件上对CPU密集型或GPU密集型应用程序进行编程时,必须做出明智的算法决策,以加快代码速度。

但是,如果您使用的语言比另一种语言的性能差,那么即使算法选择很好,也可能会很慢。

是否有任何硬数据将Objective-C与C ++进行比较,尤其是在iPhone上,但可能仅在Mac桌面上,以比较各种类似语言的性能?我对比较C和Objective-C的这篇文章非常熟悉,但这是将两种面向对象的语言相互比较的一个更大的问题。

例如,C ++ vtable查找真的比Obj-C消息快吗?快多少?线程化,多态性,排序等。在我寻求使用重复的对象模型和各种测试代码构建项目之前,我想知道是否有人已经这样做以及在何处得到结果。这种类型的测试和比较本身就是一个项目,可能会花费大量时间。也许这不是一个项目,但是可以比较两个,只有输出。

我在寻找硬数据,而不是传福音。像你们中的许多人一样,出于各种原因,我爱又恨这两种语言。此外,如果外面有人积极地追求同样的事情,那么我会投入一些代码来查看最终结果,这很有趣,而且我相信其他人也会提供帮助。我的猜测是它们都有长处和短处,我的目标是精确地找出它们是什么,以便在实际情况下可以避免/利用它们。


1
您的问题有什么意义?您是否在编写iPhone项目之前选择要学习的语言?您是否想学习将在其他项目中使用的语言?
罗格

8
我们的团队已经知道obj-​​c和c ++。我正在寻找两者之间类似操作的比较。关键是可以应用此信息。如果使用stl迭代器而不是objc快速枚举之类的方法对大于10000个项目的列表产生了很大的不同,那么请提前知道可能会使我们以不同的方式处理问题。
09年

您可以使用直接的可移植静态C函数调用进行密集操作。

Answers:


59

迈克·阿什(Mike Ash)在他的文章“常见操作的性能比较”中,对各种Objective-C方法调用与C和C ++的性能进行了比较的比较。同样, 在使用Objective-C ++调整iPhone应用程序的性能时,Savoy Software的这篇文章非常有趣。

我倾向于使用Objective-C的干净,描述性语法而不是Objective-C ++,并且还没有发现语言本身是性能瓶颈的根源。我什至倾向于做一些我知道会牺牲一些性能的事情,如果它们会使我的代码更易于维护。


优点。根据记录,我们目前没有任何性能瓶颈,或者我们将责任归咎于ObjC,这恰恰相反。我想最终这个问题仅仅是为了满足我对科学事实的渴望。
09年

您提到的第一个链接具有ObjC的性能编号,但是没有其他语言的任何比较编号(您建议将它们与C进行比较,但没有。),因此它缺乏控制这些编号的含义。
吉米·霍法

9
停止“性能比较”非常糟糕。一个“性能比较”应该有一些对Objective-C的比较:即普通的C和C ++。这不是一个比较。它列出了对于通用操作来说完全没有意义的运行时(因为它们在某些家伙的特定硬件设置上,因此完全没有意义)。
bobobobo 2012年

3
第二篇文章说:“当我使用数组时,通过切换到C数组,性能提高了4倍”。矛盾。文章说,要获得最高性能,您必须使用C,但是您在这里的回答是“ Eah just use Objective C”
bobobobo

5
@bobobobo-合作并收听。严重的是,我不了解您对Mike基准测试的问题。他运行了常见的低级操作,并将它们与C上的Objective-C层进行了比较,即消息发送,对象分配/释放等。它们都在单个硬件上运行,并且是相对的措施。他为您提供了可以在硬件上运行的基准,并且在iOS设备上执行了类似的基准,并获得了可比的结果。您想在自己的基准测试中添加函数调用,C ++对象分配等功能。
布拉德·拉尔森

57

是的,编写良好的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也是解决某些问题的很好的解决方案,并且有一些非常好的库可供使用。


31

为此,很难避免收集“硬数据”。

像您建议的那样进行功能之间的比较,最大的问题是两种语言鼓励非常不同的编码样式。Objective-C是带有鸭子类型的动态语言,其中典型的C ++使用是静态的。使用C ++或Objective-C,相同的面向对象的体系结构问题可能会有非常不同的理想解决方案。

我的感觉(因为我已经用两种语言进行了很多编程,主要是在大型项目中进行编程):为了最大化Objective-C的性能,必须将其写成非常接近C的语言。与C相比的任何性能损失。

哪一个更好?我不知道。为了获得纯粹的性能,C ++将始终具有优势。但是Objective-C的OOP风格肯定有其优点。我绝对认为,保持理智的体系结构比较容易。


12
在我关心性能的iPhone上,我发现我仅使用Objective-C来访问Objective-C中的API。我将C用于C(例如OpenGL ES)中的API以及自己的代码。我更喜欢将C用于我想移植的任何东西(例如,基于Java的电话或JavaScript)。从Objective-C移植到其他任何东西都会让我感到肚子疼。
Nosredna

1
我并不是真的在寻求“更好”,但是我理解你的意思。不过,当我在办公室里大喊“ c ++更快”时,有一些数字可以支持它,这使我听起来不像是一个传播者,而更像是一个实用主义者。
09年

2
我明白。我认为,对办公室产生影响的最好的主意是从您的应用程序中选择一些典型的代码,这可能会花费很多时间。编写一个C ++和一个Objective-C版本。测量,比较,讨论。
约翰·科特林斯基

7

实际上,这实际上是无法解决的,因为它实际上取决于您使用语言功能的方式。两种语言都会有他们快的东西,他们慢的东西,有时快又有时慢的东西。这实际上取决于您使用什么以及如何使用它。唯一可以确定的方法是分析您的代码。

在Objective C中,您也可以编写c ++代码,因此在大多数情况下,在Objective C中编写代码可能会更容易,并且如果发现其中的某些功能执行不佳,则可以尝试编写c ++版本并查看是否有帮助(C ++倾向于在编译时进行优化)。如果还编写了与您连接的API,则目标C会更易于使用,并且您可能会发现它的OOP样式更容易或更灵活。

最后,您应该继续使用所知道的知识,可以在其中编写安全,可靠的代码,如果发现需要其他语言特别关注的领域,则可以切换到该领域。X-Code确实允许您在同一项目中进行编译。


3

大约2年前,我在iPhone 3G上进行了两次测试,当时没有任何文档或记录。不确定它们是否仍然有效,但是源代码已发布并附加。

这不是一个非常广泛的测试,我主要对NSArray vs C Array迭代大量对象感兴趣。

http://memo.tv/nsarray_vs_c_array_performance_comparison

http://memo.tv/nsarray_vs_c_array_performance_comparison_part_ii_makeobjectsperformselector

您可以看到C数组在高迭代时要快得多。从那时起,我意识到瓶颈可能不是NSArray的迭代,而是消息的发送。我想尝试methodForSelector并直接调用方法,以查看差异有多大,但始终无法解决。根据Mike Ash的基准测试,速度仅快5倍。


2
嗯,访问C数组实际上就是将偏移量添加到指针,然后取消对结果指针的引用。很难比这更快。
Arafangion

@memo链接是死的,但是这是我的一部分而第二部分
bobobobo

1
实际上,如果您可以看到链接,这是一个很好的答案。总而言之,他发现C数组的速度至少NSArray
bobobobo 2012年

3

我没有用于目标C的硬数据,但是我确实有寻找C ++的好地方。

在Bjarne Stroustroup回顾C ++的早期阶段(http://www2.research.att.com/~bs/hopl2.pdf)时,C ++从Classes的C开始。),将C推至面向对象的极限。

这些限制是什么?在1994-1997年的时间范围内,许多研究人员发现面向对象是以动态绑定为代价的,例如,当C ++函数被标记为虚拟时,可能/可能不存在覆盖这些函数的子类。(在Java和C#中,所有功能都希望ctor本质上是虚拟的,对此您无能为力。)在IBM Research Tokyo研究人员的“ Java即时编译器的去虚拟化技术研究”中,他们对比了用于解决此问题的技术,其中包括UrzHölzle和Gerald Aigner的技术。UrzHölzle在与Karel Driesen的另一篇论文中表明,在C ++程序中平均有5.7%的时间(最多约50%)用于调用虚拟函数(例如vtables和thunk)。后来,他与一些Smalltalk研究人员合作,最终使Java HotSpot VM解决了OO中的这些问题。其中一些功能正被反向移植到C ++(例如“受保护”和异常处理)。

如前所述,C ++是静态类型的,而目标C是鸭子类型的。执行中的性能差异(但不是代码行)可能是这种差异的结果。


5
Objective-C不使用鸭子类型。对象要么具有特定类型,要么具有ID类型。使用鸭子类型时,运行时将根据上下文分配类型。Objective-C运行时不分配类型。
TechZen

2
在C#(.net)中,并非所有功能都是虚拟的。那是java和.net之间的区别之一。您必须使用virtual或abstract关键字将要使用的每种方法显式标记为virtual。
Rui Craveiro

1
@TechZen:至少在Ruby社区中,这与对“鸭子类型”一词的理解相矛盾。Ruby通常被认为是鸭子类型的,但是每个对象实际上都有一个特定的类型(类;它也可以具有单例方法来进一步对其进行专门化)。鸭子类型被理解为是一种将消息发送到对象的方式,该行为假设某种行为是响应的,而不是某种类型。
echristopherson 2012年

1

这项研究表明,要真正在CPU密集型游戏中获得性能,您必须使用C。链接的文章包含可运行的XCode项目。

我认为最重要的是:使用Objective-C,您必须在其中与iPhone的功能进行交互(毕竟,将蹦床放在任何地方对任何人都不利),但是当涉及到循环时,矢量对象类之类的东西或密集型数组访问,请坚持使用C ++ STL或C数组以获得良好的性能。

我的意思是完全愚蠢的看到position = [[Vector3 alloc] init] ;。如果您对诸如位置矢量之类的基本对象使用引用计数,您只是在要求提高性能。


-1

是。c ++在性能/扩展性/资源权衡方面占据统治地位。

“我在寻找硬数据,而不是传福音”。谷歌是你最好的朋友。

  1. 苹果工程师将obj-c nsstring与c ++交换来提高性能。在资源受限的设备中,只有c ++会将其剪切为MAINSTREAM oop语言。 NSString stringWithFormat很慢

  2. obj-c oop抽象被分解为基于过程的c结构以提高性能,否则MAGNITUDE的顺序比Java慢!作者还知道消息缓存-仍然可以。因此,使用c ++在oop中完成对许多小型播放器/敌人对象的建模,否则,使用obj-c在其周围进行简单的OOP包装的许多过程结构。可以存在一种等同于过程+面向对象编程= obj-c的范例。 http://ejourneyman.wordpress.com/2008/04/23/writing-a-ray-tracer-for-cocoa-objective-c/


听起来像是对我传福音
克里斯·德弗雷克斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.