Swift函数执行时间测量问题

时间:2018-11-29 16:13:19

标签: ios arrays swift sorting set

我有两个功能:

extension Array where Element: Hashable {
    func uniqueOrderly() -> [Element] {
        let startTime = CFAbsoluteTimeGetCurrent()
        var set = Set<Element>()
        var array = [Element]()
        for element in self {
            if set.contains(element) {
                continue
            }
            set.insert(element)
            array.append(element)
        }
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        print("Time for uniqueOrderly: \(timeElapsed)")
        return array
    }
}

第二个:

public extension Sequence where Element: Equatable {
    func unique() -> [Element] {
        let startTime = CFAbsoluteTimeGetCurrent()
        var unique: [Element] {
            return reduce(into: []) {
                unique, x in
                if !unique.contains(x) {
                    unique.append(x)
                }
            }
        }
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        print("Time for unique: \(timeElapsed)")
        return unique
    }
}

我正在对数组进行函数执行时间测量。

那是:

let arrayToFilter = [1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1]

实际的函数调用和结果按以下顺序排列:

arrayToFilter.unique() //Time for unique: 0.00012195110321044922
arrayToFilter.uniqueOrderly() Time for uniqueOrderly: 0.02329099178314209

但是当我更改函数顺序时,我的unique()函数显示了可怕的时间测量结果。

arrayToFilter.uniqueOrderly() //Time for uniqueOrderly: 0.0013059377670288086
arrayToFilter.unique() //Time for unique: 8.940696716308594e-06

所以我的问题是为什么在不同的函数调用顺序下会出现这种情况?另外,当我在for循环中运行这些测试时,测量结果会大不相同。 (大约+-1秒)

所有测量者都在带有Release build设置(在模拟器上)的操场和真实的iOS应用程序中完成。

测试规范:

Xcode版本10.1

Swift 4.2

1 个答案:

答案 0 :(得分:2)

您的unique()实现在计算unique之前先计算经过的时间。 unique是一个计算得出的变量,在访问时会进行评估。

对于像这样的相当短的列表,您的uniqueOrderly()实现仍然比unique()慢,因为与几乎总是在前几个元素中命中的线性搜索相比,插入集合中的开销很大。 / p>

关于订购问题,几乎可以肯定这是测试方式的人工产物。微轮廓分析非常具有挑战性。我通过将每个文件放入自己的.swift文件并以swift -O <file>运行的方式进行了测试。但是,如果我按照自己的方式将它们放到同一个文件中,并修复unique()以实际计时其活动,那么在以swift -O <file>运行时,时间是相当一致的:

Time for uniqueOrderly: 1.800060272216797e-05
[1, 2, 4, 6, 5, 7, 9, 3]
Time for unique: 2.0265579223632812e-06
[1, 2, 4, 6, 5, 7, 9, 3]

Time for unique: 2.002716064453125e-05
[1, 2, 4, 6, 5, 7, 9, 3]
Time for uniqueOrderly: 2.9802322387695312e-06
[1, 2, 4, 6, 5, 7, 9, 3]

经过更多测试之后,我怀疑这些差异部分是内存分配(内存是从操作系统中按块获取的,因此第一个分配器要比第二个分配器付出更大的代价)和L1缓存。如果对两个不同的数组进行排序(指向内存缓存),几乎所有差异都会消失。但是,由于该算法有很多重复项,因此它对数组的组成也非常敏感。

无论如何,这种微观优化是没有意义的。您将整日追逐鬼魂。很难在微小的数据块上对微小的代码段进行性能测试,并以适用于实际使用的方式对其进行优化。至少,您需要更多,更大的数组,并需要对许多不同种类的发行版进行测试(很多重复项或很少重复项)。您必须在Playgrounds之外进行测试,并且必须使用优化器。