[Swift] 性能的一些测试

2018-08-04  本文已影响252人  无衔

1.Swift中数组性能的对比

编写性能要求高的算法时,发现Swift对使用Array还是ContiguousArray;使用Class或者Struct作为元素,都会对性能带来明显的影响。

对于"向数组增加元素"的操作,对性能提升起占主导作用的是要使用Struct。

图1

而对于"获取数组中的元素",使用ContiguousArray是最敏感的因素。

图2

如果算法跟数组操作十分相关,遇到性能不理想时,在条件允许的情况下首先应该用ContiguousArray替换Array。其次再考虑数据结构是Class改为用Struct。

附上性能测试代码

static let LogName = "swift"

public class MyPoint {
    public var X :Float = 0
    public var Y :Float = 0
}

public struct MyPointStruct {
    public var X :Float = 0
    public var Y :Float = 0
}

public static func testAll(){
    
    
    let startTime1 = CFAbsoluteTimeGetCurrent()
    //float_multi_test()
    let endTime1 = CFAbsoluteTimeGetCurrent()
    let diff1:Double = (endTime1 - startTime1) * 1000
    print(LogName + " 浮点乘法 " + String(diff1))

    //-------------------------------
    
    let startTime2 = CFAbsoluteTimeGetCurrent()
    element_add()
    let endTime2 = CFAbsoluteTimeGetCurrent()
    let diff2:Double = (endTime2 - startTime2) * 1000
    print(LogName + " 数组增加元素 " + String(diff2))
    
    let startTime2_a = CFAbsoluteTimeGetCurrent()
    element_add_contiguousArray()
    let endTime2_a = CFAbsoluteTimeGetCurrent()
    let diff2_a:Double = (endTime2_a - startTime2_a) * 1000
    print(LogName + " 数组增加元素contiguous " + String(diff2_a))

    let startTime2_b = CFAbsoluteTimeGetCurrent()
    element_add_struct()
    let endTime2_b = CFAbsoluteTimeGetCurrent()
    let diff2_b:Double = (endTime2_b - startTime2_b) * 1000
    print(LogName + " 数组增加元素struct " + String(diff2_b))
    
    let startTime2_c = CFAbsoluteTimeGetCurrent()
    element_add_contiguousArray_struct()
    let endTime2_c = CFAbsoluteTimeGetCurrent()
    let diff2_c:Double = (endTime2_c - startTime2_c) * 1000
    print(LogName + " 数组增加元素contiguous_struct " + String(diff2_c))
    //-------------------------------
    
    
    let startTime3 = CFAbsoluteTimeGetCurrent()
    element_get()
    let endTime3 = CFAbsoluteTimeGetCurrent()
    let diff3:Double = (endTime3 - startTime3) * 1000
    print(LogName + " 数组获取元素 " + String(diff3))
    
    let startTime3_a = CFAbsoluteTimeGetCurrent()
    element_get_contiguousArray()
    let endTime3_a = CFAbsoluteTimeGetCurrent()
    let diff3_a:Double = (endTime3_a - startTime3_a) * 1000
    print(LogName + " 数组获取元素contiguous " + String(diff3_a))
    
    let startTime3_b = CFAbsoluteTimeGetCurrent()
    element_get_struct()
    let endTime3_b = CFAbsoluteTimeGetCurrent()
    let diff3_b:Double = (endTime3_b - startTime3_b) * 1000
    print(LogName + " 数组获取元素struct " + String(diff3_b))
    
    let startTime3_c = CFAbsoluteTimeGetCurrent()
    element_get_contiguousArray_struct()
    let endTime3_c = CFAbsoluteTimeGetCurrent()
    let diff3_c:Double = (endTime3_c - startTime3_c) * 1000
    print(LogName + " 数组获取元素contiguous " + String(diff3_c))
}

//浮点乘法
public static func float_multi_test(){
    
    let times = 5999999
    var result:Float = 1

    for _ in 0 ..< times {
        for i in 2 ..< 999 {
            result = Float(1/Float(i)+0.5) * result + 0.5
        }
    }
    print(result)
}


//集合增加元素
public static func element_add(){
    let times = 5000
    var array = [MyPoint]()
    
    for _ in 0 ..< times {
        array = [MyPoint]()
        for i in 1 ..< 9999 {
            let p = MyPoint()
            p.X = (Float(i) - 1 )/Float(i)
            p.Y = p.X * 1/Float(i)
            array.append(p)
        }
    }
    print(array.count)
}

//集合增加元素struct
public static func element_add_struct(){
    let times = 5000
    var array = [MyPointStruct]()
    
    for _ in 0 ..< times {
        array = [MyPointStruct]()
        for i in 1 ..< 9999 {
            var p = MyPointStruct()
            p.X = (Float(i) - 1 )/Float(i)
            p.Y = p.X * 1/Float(i)
            array.append(p)
        }
    }
    print(array.count)
}

//集合增加元素contiguous
public static func element_add_contiguousArray(){
    
    let times = 5000
    var array = ContiguousArray<MyPoint>()
    
    for _ in 0 ..< times {
        array = ContiguousArray<MyPoint>()
        for i in 1 ..< 9999 {
            let p = MyPoint()
            p.X = (Float(i) - 1 )/Float(i)
            p.Y = p.X * 1/Float(i)
            array.append(p)
        }
    }
    print(array.count)
}

//集合增加元素contiguous_struct
public static func element_add_contiguousArray_struct(){
    
    let times = 5000
    var array = ContiguousArray<MyPointStruct>()
    
    for _ in 0 ..< times {
        array = ContiguousArray<MyPointStruct>()
        for i in 1 ..< 9999 {
            var p = MyPointStruct()
            p.X = (Float(i) - 1 )/Float(i)
            p.Y = p.X * 1/Float(i)
            array.append(p)
        }
    }
    print(array.count)
}

//集合获取元素
public static func element_get(){
    
    let times = 5000
    var array = [MyPoint]()
    for i in 1 ..< 9999 {
        let p = MyPoint()
        p.X = (Float(i) - 1 )/Float(i)
        p.Y = p.X * 1/Float(i)
        array.append(p)
    }
    
    var result:Float = 0
    for _ in 0 ..< times {
        for i in 2 ..< 9999 {
            let p = array[i-2]
            let m = array[i-1]
            result = result + p.X + p.Y + m.X + m.Y
        }
    }
    print(result)
}

//集合获取元素contiguous
public static func element_get_contiguousArray(){
    
    let times = 5000
    var array = ContiguousArray<MyPoint>()
    for i in 1 ..< 9999 {
        let p = MyPoint()
        p.X = (Float(i) - 1 )/Float(i)
        p.Y = p.X * 1/Float(i)
        array.append(p)
    }
    
    var result:Float = 0
    for _ in 0 ..< times {
        for i in 2 ..< 9999 {
            let p = array[i-2]
            let m = array[i-1]
            result = result + p.X + p.Y + m.X + m.Y
        }
    }
    print(result)
}

//集合获取元素struct
public static func element_get_struct(){
    
    let times = 5000
    var array = [MyPointStruct]()
    for i in 1 ..< 9999 {
        var p = MyPointStruct()
        p.X = (Float(i) - 1 )/Float(i)
        p.Y = p.X * 1/Float(i)
        array.append(p)
    }
    
    var result:Float = 0
    for _ in 0 ..< times {
        for i in 2 ..< 9999 {
            let p = array[i-2]
            let m = array[i-1]
            result = result + p.X + p.Y + m.X + m.Y
        }
    }
    print(result)
}

//集合获取元素contiguous_struct
public static func element_get_contiguousArray_struct(){
    
    let times = 5000
    var array = ContiguousArray<MyPointStruct>()
    for i in 1 ..< 9999 {
        var p = MyPointStruct()
        p.X = (Float(i) - 1 )/Float(i)
        p.Y = p.X * 1/Float(i)
        array.append(p)
    }
    
    var result:Float = 0
    for _ in 0 ..< times {
        for i in 2 ..< 9999 {
            let p = array[i-2]
            let m = array[i-1]
            result = result + p.X + p.Y + m.X + m.Y
        }
    }
    print(result)
}
记录一次实战效果大概是这样的

1)没有改善前,算法运行耗时【124s】

2)改用ContiguousArray后,算法运行耗时【60s】

3)在ContiguousArray的基础上再把关键数据类型改用Struct,运行耗时变成【50s】

我想这样的结果应该和大部分算法的调优过程会很接近,毕竟通常情况下读取数组比写数组的操作频率要高,那么ContiguousArray的使用会显得更要紧一些。

2.Swift和ObjectC在乘法和数组操作上的对比

在安卓开发时,高性能要求的部分可能就不会再用java实现了。所以对于性能优先的情况下,ios开发平台选择哪种开发语言也需要做个决断。
性能测试代码还是跟上面贴的那段一样,改用ObjectC实现。
简单测试得出的结论是,Swift用来写算法问题不大。

图3
Swift的乘法性能比Object稍差,但是集合操作有优势。不过这也取决于ObjectC的集合操作用法。鉴于题目是高性能要求的算法是否要ObjectC改写Swift,这里已经基本得到大致结论,ObjectC应该不会提升相对于Swift几何倍数的性能。所以不是非常极限的情况下,用Swift写算法是可以的。

3.Swift和Java在乘法和数组操作上的对比

最后再把鄙人经常干的事情,就是各个平台移植算法需要考虑的一个测试也做了。自从Swift产生,看到的都是好评一片。私以为,那是以ObjectC为参照的罢了。测试逻辑还是一样,最前面贴出的代码Swift翻版Java实现。结论大概就是下图这样的。


图4
图5

这里Swift的添加和获取元素的操作,用的是它最快的组合ContiguousArray+Struct了。
在这样简单的测试用例下, Swift还是能看到Java的车尾灯。性能有差距,对于这样的新语言来说请继续等待它的发展。

最近一次Swift发展的后果就是,鄙人在不改算法的情况下,升级了系统版本和XCode,把Swift3升级到Swift3.3,结果就是性能下降10%。不管是OS有升级还是是Xcode有升级,反正都是苹果的锅。我只能说庆幸不是Swift当主打语言的开发者,对于是的人,你们辛苦了。

上一篇 下一篇

猜你喜欢

热点阅读