整数与布尔数组Swift性能

时间:2016-12-11 17:44:27

标签: arrays swift int boolean

我尝试使用大型整数数组和大型Bool数组执行Sieve Of Eratosthenes算法。

整数版本似乎比布尔值更快地执行MUCH。可能的原因是什么?

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

2 个答案:

答案 0 :(得分:1)

(部分回答......)

正如@MartinR在他对这个问题的评论中提到的那样,如果你为发布模式构建(有优化),这两种情况之间没有这么大的区别; Bool案例因内存占用较小而略快(但同样快,例如UInt8具有相同的占用空间。)

运行仪器来分析(非优化的)调试版本,我们清楚地看到数组元素访问&amp;赋值是Bool案例的罪魁祸首(就我的简短测试所见;对于除整数之外的所有类型,IntUInt16等等。

enter image description here

enter image description here

我们可以进一步确定它不是特别是产生开销的写入部分,而是重复访问<{1}}元素的

enter image description here

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

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

答案 1 :(得分:1)

TL,DR:

  • 不要尝试在Debug版本中优化代码。始终通过Profiler运行它。在调试中IntBool更快,但在通过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 build中运行它:

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运行它,然后选择时间配置文件模板。 (不知何故,Profiler无法分离这些功能,可能是因为它们被内联,因此每次尝试只需要运行1个功能):

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

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

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

Bool Array int Array

哈!现在差异很大了。 Bool数组仅使用与Int数组一样多的内存。 Swift数组使用与NSArray相同的内部结构,因此它在堆上分配并且heap allocation很慢。

当你更多地思考它时:Bool值只占用1位,Int占用64位机器上的64位。 Swift可能已选择用单个字节表示Bool,而Int则需要8个字节,因此存储器比率。在Debug中,这种差异可能会造成所有差异,因为运行时必须进行各种检查以确保它实际处理Bool值,因此Bool数组方法需要更长的时间

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