【问题标题】:What is the fastest way to check 2 fixed size arrays for equality in Swift?在 Swift 中检查 2 个固定大小的数组是否相等的最快方法是什么?
【发布时间】:2023-03-24 19:50:01
【问题描述】:

为什么?

给定以下 Swift 代码:

struct DemoStruct {
    let buf: [UInt8]

    init?(fromArray buf: [UInt8]) {
        guard buf.count == 6 else {
            return nil
        }
        self.buf = buf
    }

    var containsJustZeros: Bool {
        // test code
    }
}

let data = DemoStruct(fromArray: Array(repeating: 0, count: 6))!
if data.containsJustZeros {
    print("zeros")
}

对于test code 部分,我已经实现并测量了以下实现:

  1. self.buf == Array(repeating: 0, count: 6)
  2. for dig in self.buf where dig != 0 { return false } return true
  3. self.buf.elementsEqual(Array(repeating: 0, count: 6))

第一个代码最快(~13 秒/10 000 000 次测试),最后一个代码最慢(~43 秒/10 000 000 次测试)。还有其他可能的实现吗?我想优化我的代码以提高速度并更好地理解 Swift。

【问题讨论】:

  • 您的问题是关于如何检查两个数组是否相等,或者如何检查一个数组是否只包含零?这是两个不同的问题,后者简单地用buf.allSatisfy( { $0 == 0 }) 完成。
  • 请修正标题,因为它与实际问题有关。
  • 问题是关于平等以及如何优化我的代码以实现快速执行。我选择“零”示例只是为了让一切尽可能简单。我猜你的答案是2. for dig in self.eui where dig != 0 { return false } return true 的另一种变体
  • 你是如何测试的?你还记得只使用发布版本吗?你在设备上测试过吗?你是怎么测量的?只是检查一下,但我了解到,当人们声称他们测试了某些东西的性能时,他们不会自动相信,因为他们有时不知道如何。
  • 我写了一个 XCTest,同时我循环了提到的属性/函数 10,000,000 次。测试是在我的本地开发机器(Intel 64bit i5)上执行的。测试框架在最后给你一个关于执行时间的总结。

标签: arrays swift optimization


【解决方案1】:

首先:这个Data 结构是一个真的 坏主意,这肯定会导致混乱。 Foundation 已经有一个名为 Data 的结构,而且它无处不在,因为它是用于在无类型字节的杂项数据中穿梭的“通用货币”类型。

此外,您并没有真正从使用[UInt8] 中得到任何信息。只需使用Foundation.Data

至于你的主要问题。

  1. 第一种技术分配一个数组,并使用== 来比较它。绝对没有理由分配 6 个零。如果buf 有十亿个元素,你会分配十亿个元素吗?浪费。
  2. 第二种技术更好,因为您没有分配不必要的元素,只是为了比较。但是,它手动滚动了标准库中已经存在的功能(allSatisfy(_:),我稍后会介绍。)
  3. elementsEqual== 的更通用版本,它可以将一个序列与任何其他序列进行比较。您选择将其与 6 个零的数组进行比较,但这很糟糕(原因与 0 相同)。相反,您可以使用repeatElement(0, count: 6) 来生成实际上不需要存储n 副本的元素。它只存储一个,并以符合 Collection 协议的方式包装它。

最好的方法是使用allSatisfy。它速度很快,不会分配任何不必要的东西,并且最重要的是它准确地描述了您想要表达的内容:

var containsJustZeros: Bool {
    self.buf.allSatisfy { byte in byte == 0 }
}

但是,我不会在计算属性中实现这一点。那些具有快速的传统期望,而这是对整个缓冲区进行线性扫描。除非您想将结果缓存在存储的布尔属性中,否则我会将其更改为 func

【讨论】:

  • 感谢您的回答,我已经更新了我的问题,很抱歉提问时不够准确。
  • @EvelKnievel 所以你的缓冲区总是 6 个字节?
  • 其实是的,它是一个固定大小的 6 字节缓冲区,我想优化与另一个相同大小的数组的比较
  • 您对性能的渴望如何?如果答案是“非常”(并且要小心并确保您有数据来备份它,因为它是以花费时间、维护开销和整个系统复杂性为代价的),那么您可以只存储一个 6 @ 的元组987654336@,并使用内置的== 运算符将其与(0,0,0,0,0,0) 文字进行比较。如果字节在您的域中具有特定含义,您甚至可以实现自己的比较函数,该函数首先比较那些最有可能不同的字节(更早更频繁地短路)。
  • 这意味着self.buf == [0, 0, 0, 0, 0, 0] 会比self.buf == Array(repeating: 0, count: 6) 更快,因为我们不必分配数组(因为数组已经编译成二进制文件)?
猜你喜欢
  • 1970-01-01
  • 2011-01-25
  • 2011-05-08
  • 2010-11-24
  • 2010-10-07
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2010-12-18
相关资源
最近更新 更多