【问题标题】:Integer vs Boolean array Swift Performance整数与布尔数组 Swift 性能
【发布时间】:2017-04-26 15:01:09
【问题描述】:

我尝试使用大整数数组和大布尔数组执行埃拉托色尼筛算法。

整数版本的执行速度似乎比布尔版本快得多。这可能是什么原因?

import Foundation

var n : Int = 100000000;

var prime = [Bool](repeating: true, count: n+1)
var p = 2
let start = DispatchTime.now()
while((p*p)<=n)
{
    if(prime[p] == true)
    {
        var i = p*2
        while (i<=n)
        {
            prime[i] = false
            i = i + p
        }
    }
    p = p+1
}
let stop = DispatchTime.now()    
let time = (Double)(stop.uptimeNanoseconds - start.uptimeNanoseconds) / 1000000.0

print("Time = \(time) ms")

布尔数组执行时间:78223.342295 ms

import Foundation

var n : Int = 100000000;

var prime = [Int](repeating: 1, count: n+1)
var p = 2
let start = DispatchTime.now()
while((p*p)<=n)
{
    if(prime[p] == 1)
    {
        var i = p*2
        while (i<=n)
        {
            prime[i] = 0
            i = i + p
        }
    }
    p = p+1
}
let stop = DispatchTime.now()

let time = (Double)(stop.uptimeNanoseconds - start.uptimeNanoseconds) / 1000000.0

print("Time = \(time) ms")

整数数组执行时间:8535.54546 ms

【问题讨论】:

  • 这也许不相关,但你永远不应该说if prime[p] == true。如果prime[p] 是一个布尔值,它一个条件:if prime[p]
  • "这可能是什么原因?"为什么不让 Instruments 告诉你时间都花在了哪里呢?无需猜测(或要求我们为您做)。
  • 请发布两个版本。
  • @shallowThought 我已经按照你的要求做了。
  • 您是否在发布模式下编译了两个版本(带有优化)?在我的 iMac 上,它大约是 950 毫秒(布尔版本)与 1600 毫秒(整数版本)

标签: arrays swift int boolean


【解决方案1】:

TL、DR:

  • 不要尝试在调试版本中优化您的代码。始终通过 Profiler 运行它。 Int 在 Debug 中比 Bool 快,但在通过 Profiler 运行时相反。
  • 堆分配很昂贵。明智地使用你的记忆。 (This question 讨论了 C 中的复杂性,但也适用于 Swift)

长答案

首先,让我们重构您的代码以便于执行:

func useBoolArray(n: Int) {
    var prime = [Bool](repeating: true, count: n+1)
    var p = 2

    while((p*p)<=n)
    {
        if(prime[p] == true)
        {
            var i = p*2
            while (i<=n)
            {
                prime[i] = false
                i = i + p
            }
        }
        p = p+1
    }
}

func useIntArray(n: Int) {
    var prime = [Int](repeating: 1, count: n+1)
    var p = 2

    while((p*p)<=n)
    {
        if(prime[p] == 1)
        {
            var i = p*2
            while (i<=n)
            {
                prime[i] = 0
                i = i + p
            }
        }
        p = p+1
    }
}

现在,在 Debug 构建中运行它:

let count = 100_000_000
let start = DispatchTime.now()

useBoolArray(n: count)
let boolStop = DispatchTime.now()

useIntArray(n: count)
let intStop = DispatchTime.now()

print("Bool array:", Double(boolStop.uptimeNanoseconds - start.uptimeNanoseconds) / Double(NSEC_PER_SEC))
print("Int array:", Double(intStop.uptimeNanoseconds - boolStop.uptimeNanoseconds) / Double(NSEC_PER_SEC))

// Bool array: 70.097249517
// Int array: 8.439799614

所以BoolInt很多,对吧?让我们通过按Cmd + I 来运行Profiler,然后选择Time Profile 模板。 (不知何故,Profiler 无法分离这些函数,可能是因为它们是内联的,所以我每次尝试只能运行 1 个函数):

let count = 100_000_000
useBoolArray(n: count)
// useIntArray(n: count)

// Bool: 1.15ms
// Int: 2.36ms

它们不仅比 Debug 快一个数量级,而且结果反转为:Bool 现在比Int!!! Profiler 并没有告诉我们为什么我们必须进行一次政治迫害。让我们通过添加一个分配工具来检查内存分配:

哈!现在差异暴露无遗。 Bool 数组使用的内存仅为Int 数组的八分之一。 Swift 数组使用与 NSArray 相同的内部结构,因此它分配在堆上,heap allocation 很慢。

如果您想得更多:Bool 值仅占用 1 位,而 Int 在 64 位机器上占用 64 位。 Swift 可能选择用单个字节表示 Bool,而 Int 需要 8 个字节,因此内存比率。在 Debug 中,这种差异可能导致了所有差异,因为运行时必须执行各种检查以确保它实际处理的是 Bool 值,因此 Bool 数组方法需要更长的时间。

本课的寓意:不要在调试模式下优化您的代码。这可能会产生误导!

【讨论】:

    【解决方案2】:

    (部分答案...)

    正如@MartinR 在他的 cmets 中提到的那样,如果您为发布模式构建(带有优化),这两种情况之间没有那么大的区别; Bool 的情况稍微快一些,因为它的内存占用更小(但与 UInt8 一样快,它具有相同的占用空间)。

    运行工具来分析(非优化)调试构建,我们清楚地看到数组元素访问和分配是Bool 案例的罪魁祸首(据我的简短测试所见;对于所有类型,除了整数,IntUInt16 等)。

    我们可以进一步确定,产生开销的不是特别是写入部分,而是i:th 元素的重复访问

    对整数元素数组进行相同的显式读取访问测试没有显示出如此大的开销。

    在使用调试构建配置进行编译时,由于某种原因,似乎随机元素访问无法正常工作(对于非整数类型)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-08
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      • 2015-11-08
      • 2011-11-05
      • 2011-05-20
      相关资源
      最近更新 更多