如何最大限度地降低分配和初始化NSDateFormatter的成本?

时间:2010-12-14 17:21:14

标签: multithreading cocoa memory-management nsdateformatter microbenchmark

我注意到使用NSDateFormatter可能会非常昂贵。我发现分配和初始化对象已经消耗了很多时间 此外,似乎在多个线程中使用NSDateFormatter会增加成本。线程必须等待彼此的阻塞吗?

我创建了一个小型测试应用程序来说明问题。请检查一下。

此类费用的原因是什么?如何改善使用?


17.12。 - 更新我的观察结果:我不理解为什么线程处理时线程运行时间比串行运行时更长。只有在使用NSDateFormatter时才会出现时差。

6 个答案:

答案 0 :(得分:17)

注意:您的示例程序非常微观基准,并且非常有效地最大限度地放大了日期格式化程序的成本。您正在比较绝对没有做某事。因此,无论某事是什么,它似乎某些时间 nothing 慢。

此类测试极具价值且极具误导性。微基准测试通常仅在您拥有Teh Slow的真实案例时才有用。如果您要将此基准测试速度提高10倍(事实上,您可能会按照我的建议进行操作)但实际情况仅为应用程序中使用的总CPU时间的1%,最终结果不会是显着的速度提升 - 几乎不会引人注意。

  

这种费用的原因是什么?

NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyyMMdd HH:mm:ss.SSS"];

最有可能的是,成本与必须解析/验证日期格式字符串以及必须执行NSDateFormatter所做的任何类型的特定于语言环境的goop相关联。 Cocoa对本地化提供了非常全面的支持,但这种支持是以复杂性为代价的。

看到你如何编写一个相当棒的示例程序,你可以启动你的应用程序在仪器中尝试各种CPU采样仪器,以了解什么是消耗CPU周期和仪器如何工作(如果你发现任何有趣的,更新您的问题!)。

  

线程必须互相等待才能阻塞吗?

我很惊讶当你从多个线程使用单个格式化程序时,它不会简单地崩溃。 NSDateFormatter没有具体提到它是线程安全的。因此,您必须假设它不是线程安全的。

  

如何改善用法?

不要创建这么多日期格式化程序!

要么保留一批操作然后将其删除,或者如果你一直使用它们,请在应用程序运行开始时创建一个并保持不变,直到格式发生变化。

对于线程,每个线程保持一个,如果你真的必须(我敢打赌这是过度的 - 你的应用程序的架构是这样的,每批操作创建一个将更加明智)。 / p>

答案 1 :(得分:5)

我喜欢使用GCD顺序队列来确保线程安全,方便,有效且高效。类似的东西:

dispatch_queue_t formatterQueue = dispatch_queue_create("formatter queue", NULL);
NSDateFormatter *dateFormatter;
// ...
- (NSDate *)dateFromString:(NSString *)string
{
    __block NSDate *date = nil;
    dispatch_sync(formatterQueue, ^{
        date = [dateFormatter dateFromString:string];
    });
    return date;
}

答案 2 :(得分:3)

使用-initWithDateFormat:allowNaturalLanguage:代替-init后跟-setDateFormat:应该快得多(可能是2倍)。

一般来说,bbum说:缓存日期格式化程序以获取热门代码。

(编辑:在iOS 6 / OSX 10.8中不再适用,现在它们应该同样快速)

答案 3 :(得分:3)

使用GDC dispath_once,你很好。这将确保多个线程之间的同步,并确保只创建一次日期格式化程序。

+ (NSDateFormatter *)ISO8601DateFormatter {
    static NSDateFormatter *formatter;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
    });
    return formatter;
}

答案 4 :(得分:2)

由于NSDateFormatter的创建/初始化以及格式和区域设置更改成本很高。我创建了一个“工厂”类来处理NSDateFormatters的重用。

我有一个NSCache实例,在我创建的那一刻,我根据格式和区域设置信息存储多达15个NSDateFormatter个实例。所以,稍后当我再次需要它们时,我会使用locale“pt-BR”以NSDateFormatter格式“dd / MM / yyyy”向我的班级询问,而我的班级已经加载了已经加载的通讯员NSDateFormatter实例。

你应该同意在大多数标准应用程序中每个运行时有超过15种日期格式是一个边缘情况,所以我认为这是缓存它们的一个很大的限制。如果您只使用1或2种不同的日期格式,则只有这个加载的NSDateFormatter个实例数。听起来不错。

如果您想尝试一下,I made it public on GitHub

答案 5 :(得分:0)

我认为最佳实施方式如下:

NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
NSDateFormatter *dateFormatter = threadDictionary[@”mydateformatter”];
if(!dateFormatter){
    @synchronized(self){
        if(!dateFormatter){
            dateFormatter = [[NSDateFormatter alloc] init];
           [dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];
           [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@”Asia/Shanghai”]];
          threadDictionary[@”mydateformatter”] = dateFormatter;
         }
    }
}