9

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

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

什么是这样的成本,我怎么能提高使用的原因是什么?


17.12。 - 为了更新我的观察结果:我不明白为什么线程在并行处理时运行的时间要长于按串行顺序运行的时间。时差仅在使用NSDateFormatter时发生。

+0

,我愿意给你三个点为基准的应用程序,如果我能。那么2,因为iVars都带有'm_'前缀,但是......仍然是一个很好的起点,可以深入深入w /仪器,采样,线程等...... – bbum 2010-12-14 18:13:17

回答

17

注意:您的示例程序是一个微型基准测试程序,非常有效地最大限度地放大了日期格式化程序的成本。您正在比较做什么都不做做点事。因此,无论是什么东西是,它将显示为东西时间慢于没有

这样的测试非常有价值且极具误导性。微型基准通常只在您有真实世界的Teh Slow情况下才有用。如果您要将此基准测试速度提高10倍(实际上,您可能可以使用以下建议),但真实世界的情况只占应用程序总体CPU时间的1%,最终结果不会是戏剧性的速度提升 - 它几乎不会引人注目。

这样的成本是什么原因?

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

最有可能的,成本既不必解析/验证日期格式字符串和做任何一种特定的语言环境是粘性物质从中确实NSDateFormatter相关。可可对本地化提供了非常全面的支持,但这种支持的代价是复杂性。

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

有没有线程必须等待对方的阻塞?

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

如何提高使用率?

不要创建如此多的日期格式化程序!

要么保留一个操作的批处理,然后摆脱它,或者,如果您始终使用'em,请在应用程序运行的开始时创建一个并保留,直到格式更改。

对于线程,每个线程保持一个,如果你确实需要(我敢打赌这太过分了 - 你的应用程序的体系结构是这样的,每批操作创建一个会更明智)。

+0

谢谢您的回复。我试图保持测试应用程序的简单。这就是为什么它“无所事事”。当然有一个真实的世界背景。它是一个检索时间戳和更多的文件解析器。尽管如此,NSDateFormatter的简单分配使得例程非常缓慢。 - 我知道将格式化程序作为一个成员变量处理以在一个线程中重用它可以加快速度。但是,我仍然无法解释为什么格式化程序在多线程使用它的时候是那么昂贵的。当我不使用该成员时,时间差异更加明显。 – JJD 2010-12-15 11:01:23

+0

线程情况下的行为未定义,因为'NSDateFormatter'没有明确声明线程安全。因此,毫不奇怪,它在线程情况下是很慢的(很令人惊讶,它可以工作)。您是否尝试使用Instruments中的CPU采样器在运行时对基准进行采样?那会告诉你是循环去的。 – bbum 2010-12-15 16:29:28

+0

我不确定我是否理解。如果CPU采样器能够显示哪个CPU处理了哪个线程,我无法在Instruments中找到它。 – JJD 2010-12-17 11:34:35

3

使用-initWithDateFormat:allowNaturalLanguage:而不是-init然后-setDateFormat:应该快得多(可能~2倍)。

一般来说,什么bbum说:缓存你的日期格式化器热码。

(编辑:这不再是真实的的iOS 6/OSX 10.8,他们都应该是现在一样快)

+0

谢谢。函数调用'setDateFormat'不会产生成本。实际上,您可以将其从测试应用程序中排除。我只是为了防止编译尝试优化并删除dateFormatter而导致它不被使用。 – JJD 2010-12-15 11:06:28

+0

确实速度更快。你怎么知道?不过,我不确定使用这个初始化器是否聪明,因为它不清楚它是否被弃用。看到这里:http://stackoverflow.com/questions/3182314/nsdateformatters-init-method-is-deprecated/ – JJD 2010-12-17 11:32:30

+0

我激发了鲨鱼(或仪器,我不记得现在),并看看成本是在哪里。 -init和-set调用最终都会调用ICU格式化程序设置代码,这是很昂贵的一点。 (编辑)哦,嗯。我看到了这个问题。这会创建一个10.0格式的格式器。这很奇怪......使用格式化程序的预期方式不应该做重复的工作。我会调查。 – 2010-12-19 19:46:40

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; 
} 
1

由于NSDateFormatter创建/ init和格式和语言环境的改变花费了很多。我创建了一个“工厂”类来处理我的NSDateFormatters的重用。

我有一个NSCache实例,其中我存储多达15个NSDateFormatter实例,基于格式和区域设置信息,在我创建的那一刻。所以,在某些时候,当我再次需要它们时,我通过使用区域设置“pt-BR”的一些NSDateFormatter格式“dd/MM/yyyy”询问我的班级,并且我的班级给了对应的已加载NSDateFormatter实例。

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

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

+0

是否有一个特殊的原因你在[实现]中扩展NSObject'(https://github.com/DougFischer/DFD JJD 2013-04-27 18:01:55

+0

我需要初始化NSCache实例,我不知道是否有可能使用类别,但我认为,它可能会从NSDateFormatter扩展,我错过了它 – 2013-04-27 18:54:21

+1

它应该是可能的在Objective-C中搜索“关联引用” – JJD 2013-04-28 21:02:03

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; 
} 
+1

这个onc很棒创建NSDateFormatter只有一次解决了我的问题。 – Jassi 2015-03-31 10:53:03

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; 
     } 
    } 
}