xy 阵列在 Swift 性能

xy array in Swift performance

我有两个带有浮点数的大型(几千个值)数组,我想将它们组合成一个 xy 点数组以进行进一步处理,例如绘图。

所以现在在 Xcode 游乐场我正在这样做:

let xArray = // read from datafile, fast
let yArray = // read from another datafile, fast

struct xyPoint {
   let x: Float
   let y: Float
}

var spectrum: [xyPoint] = []

for i in 0..<xArray.count {
    let xy = xyPoint(x: xArray[i], y: yArray[i])
    spectrum.append(xy)
}

现在我运行游乐场的时候,这真的需要很长时间才能做到。

有什么办法可以加快速度吗?

一旦你有 2 个独立的数组,将它们组合起来就有点笨拙,并且没有一种巧妙的 "Swifty" 方法来做到这一点。如果你有一个结构数组,其中每个结构包含一个 x 和 y 值,你可以使用 map 语句将该数组转换为 CGPoint 对象数组(实际上是另一种 Struct 类型)。

您首先告诉我们:

let xArray = // read from datafile, fast
let yArray = // read from another datafile, fast

重新编写您未显示的代码可能会更好,这样您就可以:

而不是读取所有 x 点数据文件,然后读取所有 y 点数据文件
  • 读取 x 点
  • 读一个y点
  • 为 X/Y 对创建一个 CGPoint
  • 将新的 CGPoint 添加到 CGPoint 个值的输出数组中

或者甚至重组创建数据文件的代码,使其写入一个包含 X/Y 对数组的文件,而不是 2 个单独的文件。

如果您有 2 个单独的数组,您可以使用 for... 的变体,它为每个数组条目提供一个索引和一个值:

let xArray: [CGFloat] = [0.1, 0.2, 0.3, 0.4]
let yArray: [CGFloat] = [0.4, 0.3, 0.2, 0.1]

var output = [CGPoint]()
output.reserveCapacity(xArray.count)
for (index, value) in xArray.enumerated() {
    let yValue = yArray[index]
    let aPoint = CGPoint (x: value, y: yValue)
    output.append(aPoint)
}

如果 yArray 的值小于 xArray,上面的代码将会崩溃,如果 yArray 包含 更多[=41],将会错过最后的值=] 值比 xArray。一个完整的实现应该首先进行错误检查并处理数组具有不同数量的值的情况。

创建点数组的最简单方法是

let spectrum = zip(xArray, yArray).map(XYPoint.init)

(我冒昧地将结构 XYPoint 称为 Swift 类型 应该以大写字母开头。)这也允许定义 结果数组作为 常数。

但是,就执行时间而言,它并不是最快的。 原因可能是

  • zip()对一般序列进行操作,不利用 输入是数组。
  • zip() returns 一个 Sequence 因此 map() 不知道要创建的元素的数量。 因此,目标数组将被重新分配几个 次。

因此,如果保留所需的循环,则显式循环会更快 提前容量:

var spectrum: [XYPoint] = []
spectrum.reserveCapacity(xArray.count)
for i in 0..<xArray.count {
    let xy = XYPoint(x: xArray[i], y: yArray[i])
    spectrum.append(xy)
}

在我的测试中(在 1.2 GHz Intel Core m5 MacBook 上,在 Release 中编译 mode) 有两个 10,000 个元素的数组,第一个方法采用 大约 0.65 毫秒,第二种方法大约 0.42 毫秒。 对于 1,000,000 个元素,我测量了 12 毫秒对 6 毫秒。

我检查了针对您的问题的各种解决方案的性能。您可以从 this link to github

下载我的测试

A) 你的代码

var spectrum: [XYPoint] = []
for i in 0..<xArray.count {
    let xy = XYPoint(x: xArray[i], y: yArray[i])
    spectrum.append(xy)
}

B) 邮编 + 地图 ()

let spectrumB = zip(xArray, yArray).map(XYPoint.init)

C)范围+地图(我的解决方案)

let spectrum = (0 ..< xArray.count).map { i in
    return XYPoint(x: xArray[i], y: yArray[i])
}

D) ReserveCapacity + 追加 ()

var spectrum: [XYPoint] = []
spectrum.reserveCapacity(xArray.count)
for (index, value) in xArray.enumerated() {
    spectrum.append(XYPoint(x: xArray[index], y: yArray[index]))
}

我的结果(以秒为单位)

            ╭──────────────┬──────────────┬──────────────┬──────────────╮
            │       A      │       B      │       C      │       D      │
╭───────────╬══════════════╪══════════════╪══════════════╪══════════════╡
│       100 ║  0.000009426 │  0.000002401 │  0.000000571 │  0.000000550 │
│       200 ║  0.000003356 │  0.000002629 │  0.000000911 │  0.000000866 │
│       500 ║  0.000005610 │  0.000007288 │  0.000002236 │  0.000002012 │
│      1000 ║  0.000010638 │  0.000009181 │  0.000003905 │  0.000005030 │
│      2000 ║  0.000019377 │  0.000013316 │  0.000007116 │  0.000008732 │
│      5000 ║  0.000023430 │  0.000019304 │  0.000019809 │  0.000019092 │
│     10000 ║  0.000050463 │  0.000031669 │  0.000035121 │  0.000035420 │
│     20000 ║  0.000087040 │  0.000058664 │  0.000069300 │  0.000069456 │
│     50000 ║  0.000272357 │  0.000204213 │  0.000176962 │  0.000192996 │
│    100000 ║  0.000721436 │  0.000459551 │  0.000415024 │  0.000437604 │
│    200000 ║  0.001114534 │  0.000924621 │  0.000816374 │  0.000896202 │
│    500000 ║  0.002576687 │  0.002094998 │  0.001860833 │  0.002060462 │
│   1000000 ║  0.007063596 │  0.005924892 │  0.004319181 │  0.004869024 │
│   2000000 ║  0.014474969 │  0.013594134 │  0.008568550 │  0.009388957 │
│   5000000 ║  0.038348767 │  0.035136008 │  0.021276415 │  0.023855382 │
│  10000000 ║  0.081750925 │  0.078742713 │  0.043578664 │  0.047700495 │
│  20000000 ║  0.202616669 │  0.199960563 │  0.148141266 │  0.145360923 │
│  50000000 ║  0.567078563 │  0.552158644 │  0.370327555 │  0.397115294 │
│ 100000000 ║  1.136993625 │  1.101725386 │  0.713406642 │  0.740150322 │
└───────────╨──────────────┴──────────────┴──────────────┴──────────────┘

当您 运行 在主 playground 文件中编写代码时,您可能会启用日志记录。这给代码带​​来了巨大的性能损失。

我在问题中将您的代码作为函数进行了测试。将函数放入大小为 10000 的数组的 swift 主文件中花费了 10 多分钟!

我将函数移动到具有相同大小数组的 playground 源文件夹中的单独 swift 文件中,它立即完成。

我使用的代码来自你的问题(在一个函数中)而不是优化版本。