【问题标题】:Will my iPhone app take a performance hit if I use Objective-C for low level code?如果我将 Objective-C 用于低级代码,我的 iPhone 应用程序会受到性能影响吗?
【发布时间】:2010-10-29 22:18:41
【问题描述】:

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

但是,如果您使用的语言比另一种语言表现更差,那么即使是出色的算法选择也会很慢。

是否有任何比较 Objective-C 和 C++ 的硬数据,特别是在 iPhone 上,但可能只是在 Mac 桌面上,用于各种类似语言方面的性能?我对this article comparing C and Objective-C 非常熟悉,但这是比较两种面向对象语言的更大问题。

例如,C++ vtable 查找真的比 Obj-C 消息快吗?快多少?线程、多态性、排序等。在我着手构建一个具有重复对象模型和各种测试代码的项目之前,我想知道是否有人已经这样做了,结果在哪里。这种类型的测试和比较本身就是一个项目,可能需要相当长的时间。也许这不是一个项目,而是两个,并且只有输出可以比较。

我在寻找硬数据,而不是传福音。像你们中的许多人一样,出于各种原因,我喜欢和讨厌这两种语言。此外,如果有人在那里积极追求同样的事情,我会很有趣地投入一些代码来查看最终结果,我相信其他人也会提供帮助。我的猜测是它们都有优点和缺点,我的目标是准确地找出它们是什么,以便在现实世界的场景中可以避免/利用它们。

【问题讨论】:

  • 您的问题背后的意义是什么?在编写 iPhone 项目之前,您是否在选择要学习的语言?您是否想学习一门将在其他项目中使用的语言?
  • 我们的团队已经知道 obj-c 和 c++。我正在寻找两者之间类似操作的比较。关键是可以应用这些信息。如果像使用 stl 迭代器而不是 objc 快速枚举之类的东西在超过 10000 个项目的列表上产生了很大的不同,那么提前知道可能会导致我们以不同的方式处理问题。
  • 您可以使用直接可移植的静态 C 函数调用进行密集操作。

标签: c++ iphone objective-c performance optimization


【解决方案1】:

很难为此收集“硬数据”,这不会产生误导。

按照您的建议进行功能与功能比较的最大问题是两种语言鼓励非常不同的编码风格。 Objective-C 是一种带有鸭子类型的动态语言,其中典型的 C++ 用法是静态的。使用 C++ 或 Objective-C 时,相同的面向对象架构问题可能会有非常不同的理想解决方案。

我的感觉(因为我用两种语言编写了很多程序,主要是在大型项目中):为了最大限度地提高 Objective-C 的性能,它必须写得非常接近 C。而使用 C++,可以更多地使用与 C 相比,该语言没有任何性能损失。

哪个更好?我不知道。对于纯粹的性能,C++ 将始终具有优势。但是 Objective-C 的 OOP 风格肯定有它的优点。我绝对认为用它来保持一个健全的架构更容易。

【讨论】:

  • 在我关心性能的 iPhone 上,我发现我只使用 Objective-C 来访问 Objective-C 中的 API。我将 C 用于 C 中的 API(如 OpenGL ES)和我自己的代码。我更喜欢将 C 用于我认为可以移植的任何东西(例如,移植到基于 Java 的电话或 JavaScript)。从 Objective-C 移植到其他任何东西让我感到胃痛。
  • 我并不是真的在寻找“更好”,但我理解你的意思。尽管如此,当我在办公室里走来走去大喊“c++ 更快”时,有一些数据来支持它让我听起来不像是一个传道者,更像是一个实用主义者。
  • 我明白了。我认为在办公室产生影响的最佳想法是从您的应用程序中挑选一些典型的代码,这可能不会花费很多时间。编写一个 C++ 和一个 Objective-C 版本。衡量、比较、讨论。
【解决方案2】:

Mike Ash 在他的帖子"Performance Comparisons of Common Operations" 中有一些关于各种 Objective-C 方法调用与 C 和 C++ 的性能的硬性数据。另外,this post 在谈到使用 Objective-C++ 调整 iPhone 应用程序的性能时,Savoy Software 是一本有趣的读物。

相比 Objective-C++,我更喜欢 Objective-C 的简洁、描述性语法,并且没有发现语言本身是我性能瓶颈的根源。我什至倾向于做一些我知道会牺牲一点性能的事情,如果它们使我的代码更易于维护的话。

【讨论】:

  • 好点。根据记录,我们目前没有任何性能瓶颈,或者我们将事情归咎于 ObjC,恰恰相反。我想这个问题归根结底只是为了满足我对科学事实的渴望。
  • 你提到的第一个链接有 ObjC 的性能数据,但没有任何其他语言的比较数据(你建议将它们与 C 进行比较,但它没有。),所以它缺乏控制赋予数字含义。
  • 停止"Performance Comparisons" 非常糟糕。 '性能比较' 应该有一些东西可以比较 Objective-C to:即普通 C 和 C++。这不是比较。这是对常见操作完全没有意义的运行时列表(完全没有意义,因为它是在某些家伙的特定硬件设置上)。
  • 第二篇文章说“当我使用数组时,通过切换到 C 数组,我获得了 4 倍的性能增强”。矛盾。文章说要获得最大性能,您必须使用 C,但您在这里的回答是“嗯,只需使用 Objective C”
  • @bobobobo - 协作和倾听。说真的,我不明白你对迈克基准的问题。他运行了常见的低级操作,并将它们与 C 上的 Objective-C 层进行了比较,即消息发送、对象分配/释放等。它们都在单个硬件上运行,并且是相对的措施。他让你可以在你的硬件上运行基准测试,并在 iOS 设备上执行了类似的基准测试,结果相当。您想在自己的基准测试中添加函数调用、C++ 对象分配等,请随意。
【解决方案3】:

我没有关于 Objective C 的硬数据,但我确实有一个寻找 C++ 的好地方。

根据 Bjarne Stroustroup 在对 C++ 早期发展的反思 (http://www2.research.att.com/~bs/hopl2.pdf) 中,C++ 以 C with Classes 开始,因此可以认为 C++(与 Objective C 一样)将 C 推向面向对象的极限。

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

正如我所提到的,C++ 是静态类型的,而 Objective C 是鸭子类型的。执行(但不是代码行)的性能差异可能是这种差异的结果。

【讨论】:

  • Objective-C 不使用鸭子类型。对象要么具有特定类型,要么具有 id 类型。使用鸭子类型,运行时根据上下文分配类型。 Objective-C 运行时不分配类型。
  • 在 C# (.net) 中,并非所有函数都是虚拟的。这是 java 和 .net 之间的区别之一。您必须使用 virtual 或 abstract 关键字明确地将您想要拥有的每个方法标记为虚拟。
  • @TechZen:这与至少在 Ruby 社区中对“鸭子类型”一词的理解相矛盾。 Ruby 被普遍视为鸭子类型,但每个对象实际上都有一个特定的类型(类;它也可以具有进一步专门化它的单例方法)。鸭子类型被理解为您可以向对象发送消息的方式,假设某种行为作为响应,但不假设某种类型。
【解决方案4】:

这确实不是一般可以回答的问题,因为它实际上取决于您如何使用语言功能。两种语言都会有他们擅长的事情,他们擅长的事情,以及有时快有时慢的事情。这实际上取决于您使用什么以及如何使用它。唯一可以确定的方法是分析您的代码。

在 Objective C 中你也可以编写 c++ 代码,所以在大多数情况下使用 Objective C 编写代码可能更容易,如果你发现在其中表现不佳的地方,那么你可以尝试编写它的 c++ 版本并查看是否有帮助(C++ 倾向于在编译时更好地优化)。如果你正在与之交互的 API 也写在 Objective C 中,它会更容易使用,而且你可能会发现它的 OOP 风格更容易或更灵活。

最后,您应该使用您知道可以编写安全、健壮代码的内容,如果您发现需要其他语言特别关注的领域,那么您可以换成那个领域。 X-Code 确实允许您在同一个项目中编译两者。

【讨论】:

    【解决方案5】:

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

    这不是一个非常广泛的测试,我主要对 NSArray 与 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 倍多一点。

    【讨论】:

    • 嗯,访问一个 C 数组实际上就是将偏移量添加到指针,然后取消引用结果指针。很难比这更快。
    • @memo 链接已失效,但 here's part i, and part ii
    • 实际上,如果您可以看到链接,这是一个很好的答案。总而言之,他发现 C 数组至少比 NSArray 快 100 倍
    【解决方案6】:

    是的,写得好的 C++ 速度要快得多。如果您正在编写性能关键的程序并且您的 C++ 不如 C 快(或在几个百分点之内),那么就有问题了。如果您的 ObjC 实现与 C 一样快,那么通常出了点问题——即该程序可能是 ObjC OOD 的一个坏例子,因为它可能使用一些“肮脏”的技巧来低于它的抽象层在内部进行操作,例如直接 ivar 访问。

    Mike Ash 的“比较”非常具有误导性——我绝不会推荐这种方法来比较您编写的程序的执行时间,或者推荐它来比较 C、C++ 和 ObjC。呈现的结果来自编译器优化禁用的测试。当您测量执行时间时,在禁用优化的情况下编译的程序很少相关。将其视为将 C++ 与 Objective-C 进行比较的基准是有缺陷的。该测试还比较了单个功能,而不是整个、现实世界的优化实现——单个功能以非常不同的方式与两种语言结合。这远非优化实现的现实性能基准。示例:启用优化后,IMP 缓存与虚拟函数调用一样慢。静态分派(与动态分派相反,例如使用virtual)和对已知 C++ 类型的调用(可以绕过动态分派)可能会被积极优化。这个过程称为去虚拟化,使用时,声明为virtual的成员函数甚至可能是inlined。在 Mike Ash 测试的情况下,对已声明为 virtual 并且具有空主体的成员函数进行了许多调用:当类型已知时,这些调用被优化掉 完全,因为编译器看到实现并且能够确定动态调度是不必要的。编译器还可以消除在优化构建中对malloc 的调用(有利于堆栈存储)。因此,在任何 C、C++ 或 Objective-C 中启用编译器优化都会在执行时间上产生巨大差异。

    这并不是说呈现的结果完全没有用。如果您想确定他们在一个平台或架构上使用pthread_create+[NSObject alloc] 与另一个平台或架构所花费的时间之间是否存在可测量的差异,您可以获得有关外部 API 的一些有用信息。当然,这两个示例将在您的测试中使用优化的实现(除非您碰巧正在开发它们)。但是对于在您编译的程序中将一种语言与另一种语言进行比较……显示的结果在禁用优化的情况下毫无用处。

    对象创建

    还要考虑在 ObjC 中创建对象 - 每个对象都是动态分配的(例如在堆上)。使用 C++,对象可以在堆栈上创建(例如,在许多情况下,与创建 C 结构和调用简单函数的速度差不多)、在堆上或作为抽象数据类型的元素。每次你分配和释放(例如通过 malloc/free),你可能会引入一个锁。当您在堆栈上创建 C 结构或 C++ 对象时,不需要锁(尽管内部成员可能使用堆分配),并且通常只需要几条指令或几条指令加上一个函数调用。

    同样,ObjC 对象是引用计数的实例。在性能关键的 C++ 中,对象成为std::shared_ptr 的实际需求非常罕见。在 C++ 中,使每个实例都成为共享的、引用计数的实例是不必要或不可取的。使用 C++,您可以更好地控制所有权和生命周期。

    数组和集合

    C 和 C++ 中的数组和许多集合也使用强类型容器和连续内存。由于下一个元素的成员的地址通常是已知的,优化器可以做更多的事情,并且你有很好的缓存和内存局部性。使用 ObjC,这对于标准对象(例如 NSObject)来说远非现实。

    派送

    关于方法,许多 C++ 实现很少使用虚拟/动态调用,特别是在高度优化的程序中。这些是优化器的静态方法调用和素材。

    使用 ObjC 方法,每个方法调用(objc 消息发送)都是动态的,因此是优化器的防火墙。最终,这会导致许多限制或不便,即在编写性能关键的 ObjC 时,您可以做什么和不可以做什么以将性能保持在最低水平。这可能会导致更大的方法、IMP 缓存、频繁使用 C。

    某些实时应用程序无法在其渲染路径中使用任何 ObjC 消息传递。无——音频渲染就是一个很好的例子。 ObjC 调度根本不是为实时目的而设计的;消息传递对象时可能会在幕后发生分配和锁定,从而使 objc 消息传递的复杂性/时间难以预测,以至于音频渲染可能会错过其截止日期。

    其他功能

    C++ 还为其许多库提供泛型/模板实现。这些优化得很好。它们是类型安全的,并且可以使用模板进行许多内联和优化(考虑它在编译时发生的多态性、优化和特化)。 C++ 增加了一些在严格的 ObjC 中不可用或可比的特性。试图直接比较非常不同的语言、对象和库并不是那么有用——它只是实际实现的一小部分。考虑到设计和实现的许多方面,最好将问题扩展到库/框架或实际程序。

    其他要点

    在构建的各个阶段(剥离、死代码消除、内联和早期内联,以及链接时间优化),可以更轻松地删除和优化 C 和 C++ 符号。这样做的好处包括减少二进制文件大小、减少启动/加载时间、减少内存消耗等。对于单个应用程序来说,这可能没什么大不了的;但是如果你重用了很多代码,而且你应该重用,那么如果实现了 ObjC,那么你的共享库可能会给程序增加很多不必要的重量——除非你准备好跳过一些燃烧的箍。因此,可扩展性和重用也是大中型项目和重用率高的群体的因素。

    包含的库

    ObjC 库实现者也针对环境进行了优化,因此其库实现者可以利用一些语言和环境特性来提供优化的实现。尽管在纯 ObjC 中编写优化程序时存在一些相当大的限制,但 Cocoa 中存在一些高度优化的实现。这是 Cocoa 的强项之一,尽管 C++ 标准库(有些人称之为 STL)也没有懈怠。 Cocoa 在比 C++ 高得多的抽象级别上运行——如果您不太清楚自己在做什么(或应该做什么),更接近金属的操作真的会花费您。如果你不是某个领域的专家,回到一个好的库实现是一件好事,除非你真的准备好学习。同样,Cocoa 的环境也是有限的。您可以找到更好地利用操作系统的实现/优化。

    如果您正在编写优化程序并且在 C++ 和 ObjC 方面都有这样做的经验,clean C++ 实现通常会比 clean ObjC 快两倍或更快(是的,您可以与 Cocoa 进行比较)。如果您知道如何优化,您通常可以比更高级别的通用抽象做得更好。虽然,一些优化的 C++ 实现将与 Cocoa 的一样快或慢(例如,我在文件 I/O 上的初始尝试比 Cocoa 的慢——主要是因为 C++ 实现初始化了它的内存)。

    很大程度上取决于您熟悉的语言功能。我使用两种语言,它们都有不同的优势和模型/模式。它们很好地互补,并且两者都有很好的库。如果您正在实现一个复杂的、性能关键的程序,正确使用 C++ 的特性和库将为您提供更多的控制权并为优化提供显着优势,因此在正确的手中,“快几倍”是一个很好的默认期望(但是,不要期望每次都赢,或者不做一些工作)。请记住,要充分理解 C++ 才能真正达到这一点需要数年时间。

    我将大部分性能关键路径保留为 C++,但也认识到 ObjC 对于某些问题也是一个非常好的解决方案,并且有一些非常好的库可用。

    【讨论】:

      【解决方案7】:

      This study 说要在 CPU 密集型游戏中真正获得性能,你必须使用 C。链接的文章包含一个可以运行的 XCode 项目。

      我相信底线是:在必须与 iPhone 的功能交互的地方使用 Objective-C(毕竟,putting trampolines everywhere can't be good for anyone),但在循环方面,诸如矢量对象类之类的东西,或密集的数组访问,坚持使用 C++ STL 或 C 数组以获得良好的性能。

      我的意思是看到position = [[Vector3 alloc] init] ; 是完全愚蠢的。如果您对基本对象(如位置向量)使用引用计数,您只是要求性能下降。

      【讨论】:

        【解决方案8】:

        是的。 c++ 在性能/表现力/资源权衡方面占据主导地位。

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

        1. obj-c nsstring 被苹果工程师用 c++ 交换以获得性能。在资源受限的设备中,只有 c++ 将其切割为 MAINSTREAM oop 语言。 NSString stringWithFormat is slow

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

        【讨论】:

        • 对我来说听起来像是传福音
        猜你喜欢
        • 2015-11-11
        • 1970-01-01
        • 1970-01-01
        • 2012-10-09
        • 1970-01-01
        • 2019-06-19
        • 1970-01-01
        • 2022-08-04
        相关资源
        最近更新 更多