我无法找到关于现实生活项目中ARC性能影响的客观研究。 The official doc说
编译器有效地消除了许多无关的保留/释放调用,并且已经投入了大量精力来加速Objective-C运行时。特别是,当方法的调用者是ARC代码时,常见的“返回一个保留/自动释放对象”模式要快得多,并且实际上并没有将对象放入自动释放池中。
被科技爱好者传播/变形为“ARC更快”。
我所知道的是我所测量的。我们最近将我们的iOS项目迁移到了ARC,我在代码的一些CPU密集区域之前/之后进行了一些性能测量(生产代码,当然是使用-Os
标志编译的)。
我观察到70%(是70%)的表现回归。使用 Instruments 来跟踪保留/释放事件,我意识到编译器在你不会这样做的区域引入了很多保留/释放对(在ARC之前的环境中)。基本上,任何临时变得强大。也就是说,我相信,性能回归的来源。
迁移之前的原始代码已经非常优化。几乎没有任何自动释放。因此,转换到ARC几乎没有改进的余地。
幸运的是, Instruments 能够向我展示ARC引入的最昂贵的保留/释放,并且我能够使用__unsafe_unretained来停用它们。这略微减轻了性能回归。
是否有人有关于此技术或其他技术的任何信息以避免性能损失? (除了停用ARC)
感谢。
编辑: 我并不是说ARC因为性能影响而很糟糕。使用ARC的优势在很大程度上优于性能回归(在我们的代码中,回归没有任何明显的效果,所以我放手了)。我认为ARC是一项非常好的技术。我永远不会回到MRC。我好奇地问这个问题。
我对这个主题的绝大多数博客(例如here and there)感到有些恼火,或多或少给人的印象是ARC代码会比MRC代码更快(在我把手放在它之前我相信的东西)。而且我确实感觉在某些微观基准测试之外并非如此。充其量你可以希望与MRC相提并论,而不是更快。我做了一些其他涉及对象操作的简单测试(比如计算文档中的单词)。每次ARC都比较慢(认为不像我最初谈论的70%的回归那么糟糕)
\开始{讽刺}
事实上,上述文件确实回答了这个问题:
ARC缓慢吗?
这取决于你测量的是什么,但通常是“不”......
显然应理解为
\开始{模仿}
嗯......哼......我们不能说它慢一点,因为这是一个新的酷技术,我们希望你采用它。所以我们用双引号回答“否”只是为了避免集体诉讼。并停止提出愚蠢的问题。
\ {端模仿}
\ {端讽刺}
答案 0 :(得分:6)
这是我的ARC vs MRC performance measurements。性能测试项目是available on github,因此您可以添加自己的测试。一定要在设备上运行它。模拟器中的结果是偏斜的,并且通常有利于MRC。
总结:
ARC和MRC通常速度相同。一般来说,在ARC下代码应该更快,但是紧密的循环可能会更慢而且非常明显。
在低级测试中,由于优化(自动释放返回,@ autoreleasepool),ARC在速度方面优于MRC。
只要应用程序是单线程的,有一些代码会在ARC中插入额外的保留/释放,这在MRC下并不是必需的。在ARC下这样的代码可能会更慢,尽管它只会在紧密循环中产生影响,并且在很大程度上取决于相关代码。
例如,接收对象的方法即使在MRC下也应该保留它,因为它可能在方法运行时在多线程应用程序中释放。您可以在MRC中省略该代码的事实使其更快,但本质上是不可取的(尽管您很少会遇到这样的问题,如果你希望你没有这么做的话,OTOH)。例如:
-(void) someMethod:(id)object
{
[object retain]; // inserted by ARC, good practice under MRC
[object doSomething];
[object doAnotherThing];
[object release]; // inserted by ARC, good practice under MRC
}
由于这个原因,我在测试项目中使用的遗传算法大约慢了40%,ARC 。这是一个糟糕的(极端)示例,因为对于这种算法,由于NSMutableArray上的大量插入/删除操作以及正在创建的NSNumber对象,您应该通过重写C中的关键代码段来看到更大的性能改进。
彻底解雇ARC完全是疏忽,因为在某些情况下 可能会慢。如果您发现这些情况对性能至关重要,那么-fno-objc-arc
会在C中编码或重写它。
出于性能考虑,不应考虑使用或支持ARC。 ARC is a tool that make's a programmer's job a lot easier。由你来决定是否喜欢浪费时间试图找到泄漏的物体和悬挂指针坠毁,以至于你宁愿坚持使用MRC。
答案 1 :(得分:2)
我认为,如果你得到类似的性能回归,唯一可能的解释是你的手动托管代码“不安全”,我的意思是存在潜在的内存泄漏和较少的保留/释放调用,这使得程序内存管理以某种方式不安全。
我不认为ARC代码比手动管理的代码慢,如果手动管理的代码写得很好且安全......
当然我也认为手写管理的代码编写得比ARC快一点,但成本是多少?手工做的工作要多得多...... 在大多数情况下,它比实际值更麻烦!
此外,我认为应该将ARC与垃圾收集器环境进行比较,而不是使用完美编写的MRC ,人类大脑将比程序更聪明(或者至少我希望如此...... :-)) ......
但是,如果你有一个写得很好的MRC代码库并且你确信这是安全和快速的,为什么要把它放在ARC下?保持手动内存管理,使用-fno-objc-arc
标志...使用ARC不是强制性的,特别是出于这些原因。
答案 2 :(得分:1)
您可能会获得明显的性能回归的情况是您发送消息或调用具有对象参数的C / C ++函数以及每个函数的指令数相对较少的情况。编译器将为每个参数插入(以后不再优化)保留/释放对。
考虑到上下文,编译器可能会认识到某些保留/释放对是不必要的。但是我注意到,即使调用一个声明为静态内联的函数并且它与调用者驻留在同一个转换单元中,编译器也无法为参数优化掉不必要的保留/释放调用对。
答案 3 :(得分:1)
我害怕谈论话题,也基于猜测...
我认为ARC的大多数性能提升都是通过内联保留/释放调用而不是忽略它们。
另外,据我所知,ARC通常会引入额外的保留/释放呼叫。因为ARC非常严格和保守,所以它大多不执行保留/释放省略。许多新插入的保留/释放调用在语义上是必需的,但在MRC下由程序员省略。 (例如,所有传入函数参数和临时变量)
所以,
保留/释放的调用次数实际上增加了很多以严格满足语义完整性。
其中一些将被非常保守的优化所取代。
保留/释放的实际调用将通过优化进行内联 - 通过动态Objective-C方法调用成为静态C函数调用 - 因此调用成本本身将大大减少。
因此,我们通常会降低性能。我意识到在使用ARC之前我省略了很多保留/释放调用。但正如你指出的那样,无论我们得到什么,它在语义上都是完整的,并且仍然可以手动 - 所以确定性 - 通过使用__unsafe_unretained
来消除。
答案 4 :(得分:0)
当然 临时变量很强是默认的。这是明确的,清楚地记录在案。而且,如果你仔细考虑,那就是人们通常想要的东西。
MyWidget *widget=[[MyWidget alloc]init]; // 1
[myWidget mill]; // 2
如果小部件不强,则新的MyWidget将在第1行创建,并且可以在第2行之前释放并归零!
现在,如果你使用了很多临时变量,那肯定是正确的 - 例如,如果你严格遵守得墨忒耳定律 - 在紧密循环的中间,如果你假设那些临时变量没有性能成本,因为世界上有很多寄存器,那么你会感到惊讶。
这可能就是你现在居住的角落。
但这是一个充满异国情调和特殊的地方!大多数代码都不在紧密循环的中间。大多数紧密循环不是性能瓶颈。大多数紧密循环不需要很多中间变量。
相反,ARC可以以您无法手动执行的方式执行自动释放优化(尽管优化器可以)。因此,如果有一个函数在紧密循环中返回一个自动释放的变量,那么使用ARC可能会更好。
过早优化是一个坏主意。你可能处在一个不可避免的表演角落,但大多数人都没有。我确实将大部分时间花在OS X上,但是我已经有多年了,因为我遇到了性能问题,答案不是更好的算法。
(最后,如果ARC对您的应用程序造成70%的性能损失,那么您在关键路径上进行了大量的内存管理!想一想:您花费了70%的时间来分配和释放对象。这听起来像是Flyweight,对象缓存或回收池之类的教科书案例!)