【发布时间】:2015-07-15 20:40:16
【问题描述】:
以下代码将简单的值持有者映射到对象,在 Java 中的运行速度比使用 XCode 7 beta3 的 Objective-C 快 15 倍以上,“Fastest, Aggressive Optimizations [-Ofast]”。我可以在 Java 中获得超过 280M 的查找/秒,但在 objc 示例中只有大约 19M。 (我在这里发布了相应的 Java 代码,因为这是作为 Swift 比较开始的:Swift Dictionary slow even with optimizations: doing uncessary retain/release?)。
这是我真实代码的简化版本,它肯定受哈希查找时间的限制,并且也表现出这种整体性能差异。在下面的测试中,我正在测试 null 的值,只是为了确保编译器不会优化查找,但在实际应用中,我会在大多数情况下使用该值。
当我查看工具时,我发现很多时间都花在了保留/释放、msgSend 以及一些我不理解的锁定调用上。
任何关于什么可以解释这比 Java 慢 10-15 倍的想法或任何解决方法都将不胜感激。我实际上可以实现一个完美的哈希,如下所示,所以如果我能找到一个,我可以为 iOS 使用一个快速的 int-object 字典。
@interface MyKey : NSObject <NSCopying>
@property int xi;
@end
@implementation MyKey
- (NSUInteger)hash { return self.xi; }
- (BOOL)isEqual:(id)object { return ((MyKey *)object).xi == self.xi; }
- (id)copyWithZone:(NSZone *)zone { return self; }
@end
NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:2501];
NSObject *obj = [[NSObject alloc] init];
int range = 2500;
for (int x=0; x<range; x++) {
MyKey *key = [[MyKey alloc] init];
key.xi=x;
[map setObject:obj forKey:key];
}
MyKey *key = [[MyKey alloc] init];
int runs = 50;
for (int run=0; run<runs; run++)
{
NSDate *start = [NSDate date];
int reps = 10000;
for(int rep=0; rep<reps; rep++)
{
for (int x=0; x<range; x++) {
key.xi=x;
if ( [map objectForKey:key] == nil ) { NSLog(@"missing key"); }
}
}
NSLog(@"rate = %f", reps*range/[[NSDate date] timeIntervalSinceDate:start]);
}
【问题讨论】:
-
使用
NSNumber代替MyKey使性能翻倍,这表明MyKey负责大约1/2 的性能。同意NSNumber可能不是最好的性能测试。但是哈希和相等方法以及为测试创建的关键对象是时间的一部分存在时间问题。 -
我并不是要指出 Objective-C NSMutableDictionary 甚至接近 Java 实现的速度。但我对这种差异感到惊讶。
-
你说得对,使用 NSNumber 更快,我不知道为什么。仅供参考,我使用可变键来避免读取循环期间的任何内存分配。
-
有趣的是 mutable 本质上是一个标志。 NSNumber 被特殊处理,如果整数落在某个范围内,则不创建对象,只是将数字移位并在最低有效位之一中设置一个位。可以这样做是因为地址落在边界上,否则这些位会保留为 0。
-
请添加你的Java代码,否则真的很难判断。但是请注意,第一次运行 Java 代码时,它会被编译为高度优化的本机代码(通过 JIT),因此它应该非常快。