关于性能:Swift在处理数字方面真的很慢吗?

Is Swift really slow at dealing with numbers?

当我在玩一个快速教程时,我开始编写一个自定义的isPrime方法来检查给定的Int是否是主要的。

写完后,我意识到它工作正常,但发现在一些相当大的数字上执行isPrime有点慢(仍然比Int.max低很多)。

所以我在objc中编写了相同的代码,代码执行速度快得多(66x的系数)。

这里是SWIFT代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Swift {
    class func isPrime(n:Int) -> Bool {
        let sqr : Int = Int(sqrt(Double(n))) + 1
        for i in 2...sqr {
            if n % i == 0 {
                return false
            }
        }
        return true;
    }
    class func primesInRange(start:Int, end:Int) -> Int[] {
        var primes:Int[] = Int[]()
        for n in start...end {
            if self.isPrime(n) {
                primes.append(n)
            }
        }
        return primes;
    }
}

以及OBJC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@implementation Utils

+ (BOOL)isPrime:(NSUInteger)n {
    NSInteger sqr = (NSUInteger)(sqrt(n))+1;
    for (NSUInteger i = 2; i < sqr; ++i) {
        if (n % i == 0) {
            return false;
        }
    }
    return YES;
}

+ (NSArray*)primesInRange:(NSUInteger)start end:(NSUInteger)end {
    NSMutableArray* primes = [NSMutableArray array];
    for (NSUInteger i = start; i <= end; ++i) {
        if ([self isPrime:i])
            [primes addObject:@(i)];
    }

    return primes.copy;
}

@end

main.swift中:

1
2
3
4
5
6
7
8
9
10
let startDateSwift = NSDate.date()
let swiftPrimes = Swift.primesInRange(1_040_101_022_000, end: 1_040_101_022_200)
let elapsedSwift = NSDate.date().timeIntervalSinceDate(startDateSwift)*1000

let startDateObjc = NSDate.date()
let objcPrimes = Utils.primesInRange(1_040_101_022_000, end: 1_040_101_022_200)
let elapsedObjc = NSDate.date().timeIntervalSinceDate(startDateObjc)*1000

println("\(swiftPrimes) took: \(elapsedSwift)ms");
println("\(objcPrimes) took: \(elapsedObjc)ms");

这产生:

1
2
[1040101022027, 1040101022039, 1040101022057, 1040101022099, 1040101022153] took: 3953.82004976273ms
[1040101022027, 1040101022039, 1040101022057, 1040101022099, 1040101022153] took: 66.4250254631042ms

我知道我可以在这里用Int上的extension来检查一个数字是否是质数,但我希望这两个代码非常相似。

有人能告诉我为什么这个快速代码慢得多吗?66X系数是相当可怕的,只会随着我增加范围而变得更糟。


以下是Swift编译器代码生成的优化级别(您可以在构建设置中找到它们):

1
2
3
[-Onone] no optimizations, the default for debug.
[-O]     perform optimizations, the default for release.
[-Ofast] perform optimizations and disable runtime overflow checks and runtime type checks.

使用您的代码,我可以在不同的优化级别获得这些时间:

[奥诺]

1
2
Swift: 6110.98903417587ms
Objc:  134.006023406982ms

[-O]

1
2
Swift: 89.8249745368958ms
Objc:  85.5680108070374ms

[ Ofast ]

1
2
Swift: 77.1470069885254ms
Objc:  76.3399600982666ms

请记住,Ofast是有风险的。例如,它会默默地忽略整数和数组溢出,产生无意义的结果,所以如果你选择使用它,你必须保证你的程序中不可能出现溢出。


感谢@sjeohp的评论,这基本上是问题的答案。

我尝试用Release中最激进的方式对代码进行优化,以同时进行llvm和swift优化:

enter image description here

enter image description here

Release中编译项目,得到:

1
2
[1040101022027, 1040101022039, 1040101022057, 1040101022099, 1040101022153] took: 63.211977481842ms
[1040101022027, 1040101022039, 1040101022057, 1040101022099, 1040101022153] took: 60.0320100784302ms

再次感谢@sjeohp抓到这个!