衡量强/弱ARC引用的影响 – 对执行时间的影响

我想知道是否(和多less)强/弱引用pipe理对代码执行有影响,尤其是当释放许多类可能具有弱引用的对象时。 起初我把它误认为ARC,但事实并非如此。

关于同一个话题也有类似的问题 ,但是他们没有研究性能影响或者试图从中得到一些数据。

让我清楚一点:我不是说ARC或者强/弱可能会对性能造成不好的影响,或者说“不要使用这个”。 我喜欢这个。 我只是好奇它有多高效,以及如何确定它的大小。

我把这段代码放在一起,以便了解强/弱引用在执行时的性能影响。

import Foundation class Experiment1Class { weak var aClass: Experiment1Class? } class Experiment2Class { var aClass: Experiment2Class? } var persistentClass: Experiment1Class? = Experiment1Class() var nonWeakPersistentClass: Experiment2Class? = Experiment2Class() var classHolder = [Experiment1Class]() var nonWeakClassholder = [Experiment2Class]() for _ in 1...1000 { let aNewClass = Experiment1Class() aNewClass.aClass = persistentClass classHolder.append(aNewClass) let someNewClass = Experiment2Class() someNewClass.aClass = nonWeakPersistentClass nonWeakClassholder.append(someNewClass) } let date = Date() persistentClass = nil let date2 = Date() let someDate = Date() nonWeakPersistentClass = nil let someDate2 = Date() let timeExperiment1 = date2.timeIntervalSince(date) let timeExperiment2 = someDate2.timeIntervalSince(someDate) print("Time: \(timeExperiment1)") print("Time: \(timeExperiment2)") 

这段代码只测量释放一个对象所需的时间,并设置为零。

如果您在Playground(Xcode 8.3.1)中执行它,您将看到10:1的比率,但是Playground的执行速度比实际执行慢很多,所以我也build议使用“Release”构buildconfiguration来执行上面的代码。

如果你在Release中执行,我build议你至less将迭代计数设置为“1000000”,就像我这样做:

  • 将上面的代码插入到test.swift文件中
  • 从terminal,运行swiftc test.swift
  • 执行./test

作为这种testing,我相信绝对的结果毫无意义,我认为这对99%的常用应用程序没有影响。

到目前为止,我的结果显示,在Mac上执行的Releaseconfiguration上:

 Time: 3.99351119995117e-06 Time: 0.0 

然而,在我的iPhone 7Plus中,在Release中执行相同的执行:

 Time: 1.4960765838623e-05 Time: 1.01327896118164e-06 

显然,这个testing显示,对于典型应用程序的真实影响应该没有多大关系。

这是我的问题:

  • 你能想出任何其他方式来衡量强/弱参考影响执行时间吗? (我想知道系统采取了哪些策略来改善这一点)
  • 还有哪些其他指标可能很重要? (如multithreading优化或线程locking)
  • 我怎样才能改善这个?

编辑1

我发现这个LinkedListtesting是非常有趣的几个原因,请考虑下面的代码:

 //: Playground - noun: a place where people can play import Foundation var n = 0 class LinkedList: CustomStringConvertible { var count = n weak var previous: LinkedList? var next: LinkedList? deinit { // print("Muorte \(count)") } init() { // print("Crea \(count)") n += 1 } var description: String { get { return "Node \(count)" } } func recurseDesc() -> String { return(description + " > " + (next?.recurseDesc() ?? "FIN")) } } func test() { var controlArray = [LinkedList]() var measureArray = [LinkedList]() var currentNode: LinkedList? = LinkedList() controlArray.append(currentNode!) measureArray.append(currentNode!) var startingNode = currentNode for _ in 1...31000 { let newNode = LinkedList() currentNode?.next = newNode newNode.previous = currentNode! currentNode = newNode controlArray.append(newNode) measureArray.append(newNode) } controlArray.removeAll() measureArray.removeAll() print("test!") let date = Date() currentNode = nil let date2 = Date() let someDate = Date() startingNode = nil let someDate2 = Date() let timeExperiment1 = date2.timeIntervalSince(date) let timeExperiment2 = someDate2.timeIntervalSince(someDate) print("Time: \(timeExperiment1)") print("Time: \(timeExperiment2)") } test() 

我发现以下(运行在发布configuration):

  • 我不能在我的手机上运行超过32000次迭代,在deinit期间EXC_BAD_ACCESS崩溃(是的,在DEINIT期间…并不奇怪)
  • 时间约为32000次迭代是0和0.06秒,这是巨大的!

我认为这个testing是非常耗费CPU的,因为节点是一个接一个地释放的(如果你看到代码,只有下一个是强大的,以前是弱的)…所以一旦第一个被释放,其他的下降一个之后,而不是完全。

实际上没有自动内存pipe理这样的事情。 无论您使用ARC,内存都由retainreleasepipe理。 不同的是编写代码的人,你或编译器。 有写的手动内存pipe理代码,还有ARC写的手动内存pipe理代码。 从理论上讲,如果你正确地做了这些工作,ARC会在你的代码中插入完全相同retainrelease命令。 因此,性能的差异应该是非常小的。