Swift's Dictionary is slow even with -Ofast
我在SWIFT中使用一个EDCOX1的0个实现基本上是一个缓存。演出远没有我预料的那么好。我已经阅读了一些其他的问题,例如这个关于数组排序的问题,这似乎表明EDCOX1的1个答案是答案(如果你准备接受它带来的变化)。然而,即使在编译EDCOX1(1)时,性能与其他语言相比也很差。我使用Swift版本1(SWIFT-600 .0.34.4.8)。
下面是一个简单的例子,说明了这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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) } |
用
1 2 3 4 5 | xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) -O3 Test.swift && time ./Test real 0m2.295s user 0m2.176s sys 0m0.117s |
使用
1 2 3 4 5 | xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) -Ofast Test.swift && time ./Test real 0m0.602s user 0m0.484s sys 0m0.117s |
通过比较,这种Java实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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倍:
1 2 3 4 5 | javac Test.java && time java Test real 0m0.096s user 0m0.088s sys 0m0.021s |
这仅仅是复制
此代码:
1 2 3 4 5 6 7 8 9 | import Foundation var dictionary = Dictionary<Int, Int>() let items = 5000 for (var i: Int = 0; i < 5000; i++) { dictionary[i] = i } |
明显更快:
1 2 3 4 5 6 7 8 9 10 11 | $ 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是beta版的,工具是beta版的,还有很多优化工作要做。在obj-c中复制您的"holder"类示例表明,即使在相同的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | @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很快就出了大门。
1 2 3 4 5 | time ./loop real 0m0.013s user 0m0.006s sys 0m0.003s |
号
与您给出的noholder示例的时间相似性很好地说明了obj-c编译器正在做多少优化。
查看Swift中的
1 2 3 4 | 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类的结果。)
另一个有趣的问题是在类定义上使用
如您所见,它还规范化了以相同方式编译时的性能。
1 2 3 4 5 | 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 |
。
即使只使用
1 2 3 4 5 | 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 |
同样,我认为您在性能上看到的差异可能在编译时降到了当前的优化级别。
Swift 4.2的更新:我从@霍华德LoVATT使用HORD测试类获得130到180Ms的速度。
速度尖端α1:如果您正在使用游乐场进行测试,请将所有代码(包括循环测试方法)放置在单独的源文件中。
速度尖端α2:在这种情况下,通过将字典预分配到适当的大小,可以实现大约1.5到3x的速度增加:
型
不幸的是,我得到了更糟糕的结果。
我修改了Java代码以避免启动时间,增加循环次数以获得更多可重复的定时,并检查结果以防止JVM优化循环:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import java.util.Map; import java.util.HashMap; public class HolderTest { private static final int items = 1_000_000; public static void main(String[] args) { final long start = System.nanoTime(); final Holder holder = new Holder(); for (int i = 0; i < items; i++) { holder.store(i, i); } final long finish = System.nanoTime(); System.out.println("time =" + (finish - start) / 1_000_000.0 +" ms, holder 0 =" + holder.map.get(0) +", holder" + (items - 1) +" =" + holder.map.get(items - 1)); } } class Holder { final Map<Integer, Integer> map = new HashMap<>(); public void store(Integer key, Integer value) { map.put(key, value); } } |
。
同样,银行代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import Foundation class Holder { var dictionary = Dictionary<Int, Int>() func store(#key: Int, value: Int) { dictionary[key] = value } } let start = CFAbsoluteTimeGetCurrent() let holder = Holder() let items = 1_000_000 for i in 0 ..< items { holder.store(key: i, value: i) } let finish = CFAbsoluteTimeGetCurrent() println("time = \((finish - start) * 1000.0) ms, holder 0 = \(holder.dictionary[0]), holder \(items - 1) = \(holder.dictionary[items - 1])") |
我得到了300毫秒的Java和20秒(!)对于Swift:(
这是6点1分。
更新1:
改为nsmutabledictionary性能更好。
sunzero ln:holdertest lov080$swift-sdk$(xrun--show sdk path--sdk macosx)-ouncheck main.swift时间=647.060036659241 ms,支架0=可选(0),支架999999=可选(999999)
比Java慢2倍,但好多了!
更新2:
虽然我在xcode中要求取消选中-)但似乎我没有得到它(可能我还需要其他设置:()。从命令行,使用swift字典的swift版本给出:
sunzero ln:holdertest lov080$swift-sdk$(xrun--show sdk path--sdk macosx)-ouncheck main.swift时间=303.406000137329 ms,支架0=可选(0),支架999999=可选(999999)
IE与Java- Huray:
型
在Xcode6接近发布,Apple禁用调试代码并完成优化器之前,为什么不简单地将var声明为nsmutabledictionary呢?
这是经过现场验证的,而且速度相当快。
1 2 3 4 5 6 | class Holder { var dictionary = NSMutableDictionary() func store(#key: Int, value: Int) { dictionary[key] = value } } |
。
如果字典的性能相似或更好,您可以稍后再更改它。
更新:
我在TestPerformanceHolder()的单元测试中尝试了上述代码。
用-O3进行优化,平均完成013秒,比Java示例快7倍。