【问题标题】:Is Swift really slow at dealing with numbers?Swift 处理数字真的很慢吗?
【发布时间】:2014-08-01 11:37:10
【问题描述】:

在玩 swift 教程时,我开始编写自定义 isPrime 方法来检查给定的 Int 是否为素数。

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

所以我在 objc 中编写了相同的代码,并且代码执行得更快(66 倍)。

这里是快速代码:

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 代码:

@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:

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");

这会产生:

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

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

谁能告诉我为什么这个 swift 代码这么慢? 66 倍系数非常可怕,而且随着范围的增加只会变得更糟。

【问题讨论】:

  • 你看过生成的汇编代码了吗?我认为,这会很有启发性。
  • 首先,把测试的顺序颠倒一下,看看结果是否一样。我不明白为什么在这种情况下它很重要,但在其他情况下你肯定会得到缓存效果等。
  • @cmaster xcode 6 (6A215l) 似乎还不支持 Assembly View 的 swif。 @“Tom Zych”是的,我也尝试过,但结果相同。 objc 以任何顺序快速运行。
  • 未优化的 Swift 很慢。看这里:stackoverflow.com/questions/24101718/…
  • 我认为for i in 2...sqr 应该是for i in 2..sqr。您在 Swift 中包含 i=sqr 而不是 Obj-C。 ... vs .. swift 中这样的错误。

标签: performance swift primes


【解决方案1】:

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

[-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.

使用您的代码,我得到了不同级别的优化:

[-Onone]

Swift: 6110.98903417587ms
Objc:  134.006023406982ms

[-O]

Swift: 89.8249745368958ms
Objc:  85.5680108070374ms

[-Ofast]

Swift: 77.1470069885254ms
Objc:  76.3399600982666ms

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

【讨论】:

  • 所以对于默认的生产级别优化,处理数字时的 swift 不是苹果在 wwdc 声称的 2 倍!?
  • @ryan 我敢肯定他们非常仔细地选择了一个测试用例。
  • @ryan 它在现实扭曲场中跑得更快。
  • 别忘了 Swift 还不是 v1,等 Swift 发布后再次运行这段代码会是一个很好的测试。
【解决方案2】:

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

我尝试将代码优化为Release 中最激进的方式,用于 LLVM 和 Swift 优化:

Release编译项目,得到:

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

再次感谢@sjeohp 捕捉到这个!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    相关资源
    最近更新 更多