在 Swift 中检查 2 个固定大小的数组是不是相等的最快方法是啥?

Posted

技术标签:

【中文标题】在 Swift 中检查 2 个固定大小的数组是不是相等的最快方法是啥?【英文标题】:What is the fastest way to check 2 fixed size arrays for equality in Swift?在 Swift 中检查 2 个固定大小的数组是否相等的最快方法是什么? 【发布时间】:2020-02-28 18:07:42 【问题描述】:

为什么?

给定以下 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 部分,我已经实现并测量了以下实现:

    self.buf == Array(repeating: 0, count: 6) for dig in self.buf where dig != 0 return false return true 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)上执行的。测试框架在最后给你一个关于执行时间的总结。 【参考方案1】:

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

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

至于你的主要问题。

    第一种技术分配一个数组,并使用== 来比较它。绝对没有理由分配 6 个零。如果buf 有十亿个元素,你会分配十亿个元素吗?浪费。 第二种技术更好,因为您没有分配不必要的元素,只是为了比较。但是,它手动滚动了标准库中已经存在的功能(allSatisfy(_:),我稍后会介绍。) 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) 更快,因为我们不必分配数组(因为数组已经编译成二进制文件)?

以上是关于在 Swift 中检查 2 个固定大小的数组是不是相等的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建固定大小的对象数组

TypeScript:如何在编译时声明固定大小的数组以进行类型检查

如何在swift中创建固定大小的2d数组

Swift 中 MemoryLayout.size 中数组的大小

Swift - 检查一个值是不是在数组中

Swift - 循环数组数组[[Int]]的智能方法