gpt4 book ai didi

arrays - 为什么连续迭代结构数组比类数组快得多?

转载 作者:搜寻专家 更新时间:2023-10-31 08:05:13 27 4
gpt4 key购买 nike

我正在使用 Swift 开发一款游戏,我有一个静态位置数据数组,用于在游戏循环中进行处理。我最初是使用一个结构数组来保存这些数据,但我决定切换到类,这样我就可以使用引用了。然而,在进行更改和分析之后,我注意到 CPU 在处理这些数据的方法上花费的时间比我在使用 Structs 时花费的时间多得多。

所以我决定创建一个简单的测试来查看发生了什么。

final class SomeClass {}
struct SomeStruct {}

let classes = [
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
]

let structs = [
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
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 秒,而 Test2 仅耗时 0.276068031787872 秒。连续迭代结构数组的速度提高了 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 秒,而 Test2 需要 0.00960797071456909 秒。这些结构的速度提高了 88 倍。

我使用的是 OS X 发布版本,优化级别设置为 Fastest,Smallest [-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, y: 410), surfaceID: 0, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 880, y: 304), b: SurfacePoint(x: 880, y: 294), c: SurfacePoint(x: 962, y: 304), surfaceID: 1, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 787, y: 138), b: SurfacePoint(x: 791, y: 129), c: SurfacePoint(x: 1031, y: 248), surfaceID: 2, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 523, y: 138), b: SurfacePoint(x: 523, y: 128), c: SurfacePoint(x: 806, y: 144), surfaceID: 3, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1020, y: 243), b: SurfacePoint(x: 1020, y: 233), c: SurfacePoint(x: 1607, y: 241), surfaceID: 4, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1649, y: 304), b: SurfacePoint(x: 1649, y: 294), c: SurfacePoint(x: 1731, y: 305), surfaceID: 5, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1599, y: 240), b: SurfacePoint(x: 1595, y: 231), c: SurfacePoint(x: 1852, y: 128), surfaceID: 6, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1807, y: 141), b: SurfacePoint(x: 1807, y: 131), c: SurfacePoint(x: 2082, y: 138), surfaceID: 7, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 976, y: 413), b: SurfacePoint(x: 976, y: 403), c: SurfacePoint(x: 1643, y: 411), surfaceID: 8, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1732, y: 410), b: SurfacePoint(x: 1732, y: 400), c: SurfacePoint(x: 2557, y: 410), surfaceID: 9, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 2130, y: 490), b: SurfacePoint(x: 2138, y: 498), c: SurfacePoint(x: 2109, y: 512), surfaceID: 10, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1598, y: 828), b: SurfacePoint(x: 1597, y: 818), c: SurfacePoint(x: 1826, y: 823), surfaceID: 11, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 715, y: 826), b: SurfacePoint(x: 715, y: 816), c: SurfacePoint(x: 953, y: 826), surfaceID: 12, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 840, y: 943), b: SurfacePoint(x: 840, y: 933), c: SurfacePoint(x: 920, y: 943), surfaceID: 13, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1005, y: 1011), b: SurfacePoint(x: 1005, y: 1001), c: SurfacePoint(x: 1558, y: 1011), surfaceID: 14, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1639, y: 943), b: SurfacePoint(x: 1639, y: 933), c: SurfacePoint(x: 1722, y: 942), surfaceID: 15, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1589, y: 825), b: SurfacePoint(x: 1589, y: 815), c: SurfacePoint(x: 1829, y: 825), surfaceID: 16, dynamic:false),
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: 410), b: SurfacePoint(x: 0, y: 400), c: SurfacePoint(x: 875, y: 410), surfaceID: 0, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 880, y: 304), b: SurfacePoint(x: 880, y: 294), c: SurfacePoint(x: 962, y: 304), surfaceID: 1, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 787, y: 138), b: SurfacePoint(x: 791, y: 129), c: SurfacePoint(x: 1031, y: 248), surfaceID: 2, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 523, y: 138), b: SurfacePoint(x: 523, y: 128), c: SurfacePoint(x: 806, y: 144), surfaceID: 3, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1020, y: 243), b: SurfacePoint(x: 1020, y: 233), c: SurfacePoint(x: 1607, y: 241), surfaceID: 4, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1649, y: 304), b: SurfacePoint(x: 1649, y: 294), c: SurfacePoint(x: 1731, y: 305), surfaceID: 5, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1599, y: 240), b: SurfacePoint(x: 1595, y: 231), c: SurfacePoint(x: 1852, y: 128), surfaceID: 6, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1807, y: 141), b: SurfacePoint(x: 1807, y: 131), c: SurfacePoint(x: 2082, y: 138), surfaceID: 7, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 976, y: 413), b: SurfacePoint(x: 976, y: 403), c: SurfacePoint(x: 1643, y: 411), surfaceID: 8, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1732, y: 410), b: SurfacePoint(x: 1732, y: 400), c: SurfacePoint(x: 2557, y: 410), surfaceID: 9, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 2130, y: 490), b: SurfacePoint(x: 2138, y: 498), c: SurfacePoint(x: 2109, y: 512), surfaceID: 10, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1598, y: 828), b: SurfacePoint(x: 1597, y: 818), c: SurfacePoint(x: 1826, y: 823), surfaceID: 11, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 715, y: 826), b: SurfacePoint(x: 715, y: 816), c: SurfacePoint(x: 953, y: 826), surfaceID: 12, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 840, y: 943), b: SurfacePoint(x: 840, y: 933), c: SurfacePoint(x: 920, y: 943), surfaceID: 13, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1005, y: 1011), b: SurfacePoint(x: 1005, y: 1001), c: SurfacePoint(x: 1558, y: 1011), surfaceID: 14, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1639, y: 943), b: SurfacePoint(x: 1639, y: 933), c: SurfacePoint(x: 1722, y: 942), surfaceID: 15, dynamic:false),
SurfaceFrame(a: SurfacePoint(x: 1589, y: 825), b: SurfacePoint(x: 1589, y: 815), c: SurfacePoint(x: 1829, y: 825), surfaceID: 16, dynamic:false),
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
}
final class SurfaceFrame {
let a,b,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 秒,而结构测试只用了 0.310304999351501 秒。结构速度提高了 47 倍。

最佳答案

正如 Martin R 所建议的,我对这两个测试进行了概要分析,实际上,保留/释放调用使得类数组的迭代比结构数组的迭代慢得多。需要说明的是,这是我运行的测试。

import Foundation

final class SomeClass {}

struct SomeStruct {}

var classes = [
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
SomeClass(),
]
var structs = [
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
SomeStruct(),
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 {}
}
}

以下是使用仪器对这两个测试进行分析的图片。只需将运行时间相加,您就可以看到类测试在每次迭代期间几乎将所有时间都花在了保留/释放上。我很想知道 Swift 2.0 如何处理这个问题。

结构 enter image description here

enter image description here

所以出于好奇,我想如果我可以通过直接在数组上进行指针运算来绕过保留/释放调用会发生什么(旁注:我建议你永远不要这样做在真实的应用程序中)。所以我创建了最后一个测试。但是在这个测试中,我不会多次迭代数组,而是创建一个大数组并迭代一次,因为无论如何这是大部分开销发生的地方。我还决定在此测试中访问属性以减少优化中的歧义。

下面是最终测试的结果:

  • 对大型结构数组进行一次迭代:1.00037097930908 秒
  • 大型类数组的一次迭代:11.3165299892426 秒
  • 使用指针对大型结构数组进行一次迭代算术:0.773443996906281 s
  • 使用指针对大型类数组进行一次迭代算术: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++
}
}

关于arrays - 为什么连续迭代结构数组比类数组快得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31032772/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com