加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

数组 – 为什么连续遍历一个Structs数组比一个Classes数组更快?

发布时间:2020-12-14 04:56:45 所属栏目:百科 来源:网络整理
导读:我正在开发一个使用 Swift的游戏,我有一个静态的位置数据数组,我用它来在游戏循环中进行处理.我最初使用一个Structs数组来保存这些数据,但我决定切换到类,所以我可以使用引用.但是在进行更改和分析之后,我注意到CPU在处理此数据的方法上花费的时间比在使用St
我正在开发一个使用 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
>对大型类阵列的一次迭代:11.3165299892426 s
>使用指针对大型Struct数组进行一次迭代
算术:0.773443996906281 s
>使用指针对大型Class数组进行一次迭代
算术:2.81995397806168 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++
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读