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