Mik*_*ers 9 performance multithreading memory-management reference-counting objective-c
所以我正在阅读这篇文章,试图从Python解释器中删除全局解释器锁(GIL)以提高多线程性能并看到一些有趣的东西.
事实证明,删除GIL实际上使事情变得更糟的地方之一是内存管理:
使用自由线程,引用计数操作会失去线程安全性.因此,补丁引入了全局引用计数互斥锁以及用于更新计数的原子操作.在Unix上,使用标准的pthread_mutex_t锁(包含在PyMutex结构中)和以下函数实现锁定......
...在Unix上,必须强调的是,简单的引用计数操作已经被不少于三个函数调用替换,加上实际锁定的开销.它的价格要贵得多......
...显然细粒度的引用计数锁定是性能不佳的主要原因,但即使你取消锁定,引用计数性能仍然对任何类型的额外开销(例如,函数调用等)非常敏感. ).在这种情况下,性能仍然是使用GIL的Python的两倍.
然后:
引用计数是一种用于自由线程的非常糟糕的内存管理技术.这已经广为人知,但性能数据更加具体.对于任何尝试使用GIL删除补丁的人来说,这肯定是最具挑战性的问题.
所以问题是,如果引用计数对于线程来说太糟糕了,Objective-C如何做呢?我编写了多线程的Objective-C应用程序,并没有注意到内存管理的大量开销.他们在做别的吗?像某种每个对象锁而不是全局锁?Objective-C的引用计数在线程上实际上在技术上是不安全的吗?我不是一个真正推测的并发专家,但我有兴趣知道.
存在开销,并且在极少数情况下(例如,微基准测试;),无论有哪些优化(其中有很多优化),它都可能很重要.但是,正常情况是针对对象的引用计数的无争议操作进行了优化.
所以问题是,如果引用计数对于线程来说太糟糕了,Objective-C如何做呢?
游戏中有多个锁定,实际上,任何给定对象的保留/释放都会为该对象选择随机锁定(但始终是相同的锁定).因此,减少锁争用,同时不需要每个对象一次锁定.
(以及Catfish_man所说的;一些类将实现自己的引用计数方案,以使用特定于类的锁定原语来避免争用和/或针对其特定需求进行优化.)
实现细节更复杂.
Objectice-C的引用计数在线程上实际上在技术上是不安全的吗?
不 - 它对线程是安全的.
实际上,与其他操作相比,典型代码会调用retain并且release很少调用.因此,即使这些代码路径上存在显着的开销,它也会在应用程序中的所有其他操作中进行摊销(相比之下,通过比较将像素推向屏幕非常昂贵).
如果一个对象是跨线程共享的(通常是坏主意),那么保护数据访问和操作的锁定开销通常会远远大于保留/释放开销,因为保留/释放的频率很低.
就Python的GIL开销而言,我敢打赌它更多地与引用计数作为正常解释器操作的一部分递增和递减的频率有关.
除了bbum所说的,Cocoa中最常被抛出的对象会覆盖正常的引用计数机制,并在对象中内联存储引用计数,它们使用原子加法和减法指令而不是锁定来操作.
(从未来编辑:Objective-C现在通过将refcount与'isa'指针混合在一起,自动在现代Apple平台上进行优化)