数组 – 为什么连续遍历一个Structs数组比一个Classes数组更快?
我正在开发一个使用
Swift的游戏,我有一个静态的位置数据数组,我用它来在游戏循环中进行处理.我最初使用一个Structs数组来保存这些数据,但我决定切换到类,所以我可以使用引用.但是在进行更改和分析之后,我注意到CPU在处理此数据的方法上花费的时间比在使用Structs时花费的时间多得多.
所以我决定创建一个简单的测试来看看发生了什么. final class SomeClass {} struct SomeStruct {} let classes = [ SomeClass(),SomeClass(),] let structs = [ SomeStruct(),SomeStruct(),] func test1() { for i in 0...10000000 { for s in classes {} } } func test2() { for i in 0...10000000 { for s in structs {} } } Test1需要15.4722579717636 s而Test2仅需0.276068031787872 s.通过结构阵列连续迭代的速度提高了56倍.所以我的问题是,这是为什么?我正在寻找一个详细的答案.如果我不得不猜测,我会说结构本身按顺序存储在内存中,而类只存储为地址.所以他们每次都需要被解除引用.但话说回来,每次都不需要复制结构吗? 旁注:两个数组都很小,但我不断迭代它们.如果我将代码更改为迭代一次但是使数组非常大,如下所示: for i in 0...10000000 { structs.append(SomeStruct()) classes.append(SomeClass()) } func test1() { for s in classes {} } func test2() { for s in structs {} } 然后我得到以下结果:Test1需要0.841085016727448 s而Test2需要0.00960797071456909 s.结构需要快88倍. 我正在使用OS X发布版本,优化级别设置为最快,最小[-Os] 编辑 根据要求,我编辑了这个问题,以包含一个测试,其中结构和类不再是空的.它们使用我在游戏中使用的相同属性.仍然没有什么区别.结构仍然快得多,我不知道为什么.希望有人能提供答案. import Foundation final class StructTest { let surfaceFrames = [ SurfaceFrame(a: SurfacePoint(x: 0,y: 410),b: SurfacePoint(x: 0,y: 400),c: SurfacePoint(x: 875,surfaceID: 0,dynamic:false),SurfaceFrame(a: SurfacePoint(x: 880,y: 304),b: SurfacePoint(x: 880,y: 294),c: SurfacePoint(x: 962,surfaceID: 1,SurfaceFrame(a: SurfacePoint(x: 787,y: 138),b: SurfacePoint(x: 791,y: 129),c: SurfacePoint(x: 1031,y: 248),surfaceID: 2,SurfaceFrame(a: SurfacePoint(x: 523,b: SurfacePoint(x: 523,y: 128),c: SurfacePoint(x: 806,y: 144),surfaceID: 3,SurfaceFrame(a: SurfacePoint(x: 1020,y: 243),b: SurfacePoint(x: 1020,y: 233),c: SurfacePoint(x: 1607,y: 241),surfaceID: 4,SurfaceFrame(a: SurfacePoint(x: 1649,b: SurfacePoint(x: 1649,c: SurfacePoint(x: 1731,y: 305),surfaceID: 5,SurfaceFrame(a: SurfacePoint(x: 1599,y: 240),b: SurfacePoint(x: 1595,y: 231),c: SurfacePoint(x: 1852,surfaceID: 6,SurfaceFrame(a: SurfacePoint(x: 1807,y: 141),b: SurfacePoint(x: 1807,y: 131),c: SurfacePoint(x: 2082,surfaceID: 7,SurfaceFrame(a: SurfacePoint(x: 976,y: 413),b: SurfacePoint(x: 976,y: 403),c: SurfacePoint(x: 1643,y: 411),surfaceID: 8,SurfaceFrame(a: SurfacePoint(x: 1732,b: SurfacePoint(x: 1732,c: SurfacePoint(x: 2557,surfaceID: 9,SurfaceFrame(a: SurfacePoint(x: 2130,y: 490),b: SurfacePoint(x: 2138,y: 498),c: SurfacePoint(x: 2109,y: 512),surfaceID: 10,SurfaceFrame(a: SurfacePoint(x: 1598,y: 828),b: SurfacePoint(x: 1597,y: 818),c: SurfacePoint(x: 1826,y: 823),surfaceID: 11,SurfaceFrame(a: SurfacePoint(x: 715,y: 826),b: SurfacePoint(x: 715,y: 816),c: SurfacePoint(x: 953,surfaceID: 12,SurfaceFrame(a: SurfacePoint(x: 840,y: 943),b: SurfacePoint(x: 840,y: 933),c: SurfacePoint(x: 920,surfaceID: 13,SurfaceFrame(a: SurfacePoint(x: 1005,y: 1011),b: SurfacePoint(x: 1005,y: 1001),c: SurfacePoint(x: 1558,surfaceID: 14,SurfaceFrame(a: SurfacePoint(x: 1639,b: SurfacePoint(x: 1639,c: SurfacePoint(x: 1722,y: 942),surfaceID: 15,SurfaceFrame(a: SurfacePoint(x: 1589,y: 825),b: SurfacePoint(x: 1589,y: 815),c: SurfacePoint(x: 1829,surfaceID: 16,SurfaceFrame(a: SurfacePoint(x: 0,y: 0),b: SurfacePoint(x: 1,y: 1),c: SurfacePoint(x: 2,y: 2),surfaceID: 17,dynamic:true) ] func run() { let startTime = CFAbsoluteTimeGetCurrent() for i in 0 ... 10000000 { for s in surfaceFrames { } } let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime println("Time elapsed (timeElapsed) s") } } struct SurfacePoint { var x,y: Int } struct SurfaceFrame { let a,b,c :SurfacePoint let surfaceID: Int let dynamic: Bool } import Foundation final class ClassTest { let surfaceFrames = [ SurfaceFrame(a: SurfacePoint(x: 0,y: Int } final class SurfaceFrame { let a,c :SurfacePoint let surfaceID: Int let dynamic: Bool init(a: SurfacePoint,b: SurfacePoint,c: SurfacePoint,surfaceID: Int,dynamic: Bool) { self.a = a self.b = b self.c = c self.surfaceID = surfaceID self.dynamic = dynamic } } 在此测试中,类使用了14.5261079668999 s,而使用结构的测试仅花费了0.310304999351501 s.结构快了47倍. 解决方法
正如Martin R推荐的那样,我描述了两个测试,实际上,保留/释放调用使得遍历类数组比迭代结构数组要慢得多.为了清楚起见,这是我跑的测试.
import Foundation final class SomeClass {} struct SomeStruct {} var classes = [ SomeClass(),] var structs = [ SomeStruct(),] let startTime = CFAbsoluteTimeGetCurrent() /* structTest() classTest() */ let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime println("Time elapsed (timeElapsed) s") func structTest() { for i in 0 ... 1000000 { for e in structs {} } } func classTest() { for i in 0 ... 1000000 { for e in classes {} } } 以下是使用仪器进行两种测试分析的图片.您可以通过将每次迭代期间Classes测试几乎所有时间花在保留/释放上的运行时间加起来来看.我有兴趣看看Swift 2.0如何处理这个问题. 结构 类 所以出于好奇,我想如果我可以通过直接在数组上执行指针算法来绕过retain / release调用会发生什么(旁注:我建议你永远不要在真正的应用程序中这样做).所以我创建了最后一个测试.但是在这个测试中,我不是多次迭代数组,而是创建一个大型数组并迭代一次,因为这是大多数开销发生的地方.我还决定在此测试中访问属性以减少优化中的模糊性. 所以这是最终测试的结果: >对大型Struct阵列进行一次迭代:1.00037097930908 s 以下是测试代码. final class SomeClass { var a: Int init(a: Int) { self.a = a } } struct SomeStruct { var a: Int init(a: Int) { self.a = a } } var classes: [SomeClass] = [] var structs: [SomeStruct] = [] var total: Int = 0 for i in 0 ... 100000000 { classes.append(SomeClass(a:i)) structs.append(SomeStruct(a:i)) } let startTime = CFAbsoluteTimeGetCurrent() /*structTest() classTest() structTestPointer() classTestPointer()*/ let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime println("Time elapsed (timeElapsed) s") func structTest() { for someStruct in structs { let a = someStruct.a total = total &+ a } } func structTestPointer() { var pointer = UnsafePointer<SomeStruct>(structs) for j in 0 ..< structs.count { let someStruct = pointer.memory let a = someStruct.a total = total &+ a pointer++ } } func classTest() { for someClass in classes { let a = someClass.a total = total &+ a } } func classTestPointer() { var pointer = UnsafePointer<SomeClass>(classes) for j in 0 ..< classes.count { let someClass = pointer.memory let a = someClass.a total = total &+ a pointer++ } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |