即使使用 -Ofast Swift 的字典也很慢 [英] Swift's Dictionary is slow even with -Ofast

查看:46
本文介绍了即使使用 -Ofast Swift 的字典也很慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Swift 中的 Dictionary 实现本质上的缓存.性能远低于我的预期.我已经阅读了一些其他问题,例如 这个关于数组排序的问题 似乎建议 -Ofast 是答案(如果您准备接受它带来的更改).然而,即使在编译 -Ofast 时,性能与其他语言相比也很差.我使用的是 Swift 1.0 版 (swift-600.0.34.4.8).

I'm implementing what is essentially a cache using a Dictionary in Swift. The performance is well short of what I would expect. I've read some of the other questions, for example this one about array sorting that seem to suggest that -Ofast is the answer (if you're prepared to accept the changes it brings with it). However, even when compiled -Ofast, performance compares poorly to other languages. I'm using Swift version 1.0 (swift-600.0.34.4.8).

以下是说明问题的简化示例:

The following is a boiled-down example which illustrates the problem:

import Foundation

class Holder {
    var dictionary = Dictionary<Int, Int>()

    func store(#key: Int, value: Int) {
        dictionary[key] = value
    }
}

let holder = Holder()

let items = 5000

for (var i: Int = 0; i < 5000; i++) {
    holder.store(key: i, value: i)
}

-O3编译,运行时间超过两秒:

Compiled with -O3 it takes more than two seconds to run:

xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) -O3 Test.swift && time ./Test

real    0m2.295s
user    0m2.176s
sys     0m0.117s

使用 -Ofast 编译产生 3-4 倍的改进:

Compiling with -Ofast yields a 3-4x improvement:

xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) -Ofast Test.swift && time ./Test

real    0m0.602s
user    0m0.484s
sys     0m0.117s

相比之下,这个 Java 实现:

By comparison, this Java implementation:

import java.util.Map;
import java.util.HashMap;

public class Test {
    public static void main(String[] args) {
        Holder holder = new Holder();
        int items = 5000;
        for (int i = 0; i < items; i++) {
            holder.store(i, i);
        }
    }
}

class Holder {
    private final Map<Integer, Integer> map = new HashMap<Integer, Integer>();

    public void store(Integer key, Integer value) {
        map.put(key, value);
    }
}

又快了约 6 倍:

javac Test.java && time java Test

real    0m0.096s
user    0m0.088s
sys     0m0.021s

是否仅仅是复制 Dictionary 的成本,因为它发生了变异并存储在 Holder 实例中,导致 Swift 的表现如此糟糕?删除 Holder 并直接访问 Dictionary 将表明它是.

Is it simply the cost of copying the Dictionary as it's mutated and stored in the Holder instance that's causing Swift to fare so badly? Removing Holder and accessing the Dictionary directly would suggest that it is.

此代码:

import Foundation

var dictionary = Dictionary<Int, Int>()

let items = 5000

for (var i: Int = 0; i < 5000; i++) {
    dictionary[i] = i
}

明显更快:

$ xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) -O3 NoHolder.swift && time ./NoHolder

real    0m0.011s
user    0m0.009s
sys     0m0.002s

$ xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) -Ofast NoHolder.swift && time ./NoHolder

real    0m0.011s
user    0m0.007s
sys     0m0.003s

虽然它提供了一个(希望如此)有趣的数据点,但在我的情况下直接访问字典是不可能的.我还能做些什么来让 Swift 在当前形式下更接近这种性能水平?

While it provides a (hopefully) interesting data point, accessing the Dictionary directly isn't possible in my situation. Is there anything else I can do to get closer to this level of performance with Swift in its current form?

推荐答案

TL;DR It's Beta.

TL;DR It's Beta.

我认为现在的答案只是 Swift 处于测试阶段,工具处于测试阶段,还有很多优化尚未完成.在 Obj-C 中复制您的Holder"类示例表明,即使它在相同的 -Ofast 级别上也快了很多.

I would think that the answer right now is just that Swift is in beta, the tools are in beta, and a lot of optimizations are yet to be done. Replicating your "Holder" class example in Obj-C shows that even it is quite a bit faster at the same -Ofast level.

@import Foundation;

@interface Holder : NSObject

@property NSMutableDictionary *dictionary;
- (void)storeValue:(NSInteger)value forKey:(NSString *)key;

@end

@implementation Holder

- (instancetype)init {
   self = [self initWithDict];
    return self;
}


- (instancetype)initWithDict {
    if (!self) {
        self = [super init];
        _dictionary = [NSMutableDictionary dictionary];
    }

    return self;
}

- (void)storeValue:(NSInteger)value forKey:(NSString *)key {
    [self.dictionary setValue:@(value) forKey:key];
}

@end

int main(int argc, const char * argv[]) {

    Holder *holder = [Holder new];

    for (NSInteger i = 0; i < 5000; i++) {
        [holder storeValue:i forKey:[NSString stringWithFormat:@"%ld", i]];
    }

}

Obj-C 很快就出来了.

The Obj-C is fast out of the gate.

time ./loop 

    real    0m0.013s
    user    0m0.006s
    sys     0m0.003s

与您提供的 NoHolder 示例在时间上的相似性很好地表明了 Obj-C 编译器正在执行多少优化.

The similarities in time to the NoHolder example you give is a good indication at how much optimization the Obj-C compiler is doing.

查看 Swift 中 -O3-Ofast 级别的程序集,可以看出在完成的安全检查量上存在很大差异.查看 Obj-C 程序集表明,要执行的程序要少得多.既然让一个程序快速运行的关键是让它不需要做太多......

Taking a look at the assembly for the -O3 and -Ofast levels in Swift show there is a big difference in the amount of safety checking done. Looking at the Obj-C assembly shows that, well, there is just a lot less of it to be performed. Since the key to making a program fast is to make it not need to do much…

OS-X-Dos-Equis:~ joshwisenbaker$ wc -l objc.txt 
     159 objc.txt
OS-X-Dos-Equis:~ joshwisenbaker$ wc -l oFast.txt 
    3749 oFast.txt

(更新 Holder 类的最终结果.)

另一个有趣的问题是在类定义上使用了 @final 装饰.如果你知道你的类永远不会被子类化,那么尝试添加这样的关键字:@final class Holder

So another interesting wrinkle is the use of the @final decoration on the class definition. If you know that your class is never going to be subclassed then try adding the keyword like this: @final class Holder

正如你所看到的,当以同样的方式编译时,它也使性能正常化.

As you can see it also normalizes the performance when compiled the same way.

OS-X-Dos-Equis:~ joshwisenbaker$ swift -sdk $(xcrun --show-sdk-path --sdk macosx) -Ofast bench.swift && time ./bench

real    0m0.013s
user    0m0.007s
sys     0m0.003s

即使只使用 -O3@final 也很神奇.

Even using just -O3 the @final works magic.

OS-X-Dos-Equis:~ joshwisenbaker$ swift -sdk $(xcrun --show-sdk-path --sdk macosx) -O3  bench.swift && time ./bench

real    0m0.015s
user    0m0.009s
sys 0m0.003s

同样,我认为您在性能方面看到的差异可能归结为编译时的当前优化级别.

Again, I think the differences you are seeing in performance is probably down to the current optimization levels when compiled.

这篇关于即使使用 -Ofast Swift 的字典也很慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆