9

NSDateFormatterを使用すると非常にコストがかかることに気付きました。オブジェクトの割り当てと初期化にはすでに多くの時間がかかっていることがわかりました。
さらに、NSDateFormatter複数のスレッドで を使用すると、コストが増加するようです。スレッドが互いに待機しなければならないブロッキングが発生する可能性はありますか?

この問題を説明するために、小さなテスト アプリケーションを作成しました。チェックアウトしてください。

そのようなコストの理由は何ですか?また、使用方法を改善するにはどうすればよいですか?


17.12. - 私の観察を更新するには: 並列に処理された場合に、シリアルに実行された場合と比較して、スレッドが長く実行される理由がわかりません。時間差は、NSDateFormatter が使用されている場合にのみ発生します。

4

6 に答える 6

17

:サンプルプログラムは非常にマイクロベンチマークであり、日付フォーマッタのコストを非常に効果的に最大限に増幅します。あなたは何もしないことと何かをすることを比較しています。したがって、それが何であれ、何もないよりも倍も遅いように見えます。

このようなテストは非常に価値があり、非常に誤解を招きます。マイクロベンチマークは、通常、Teh Slow の実際のケースがある場合にのみ役立ちます。このベンチマークを 10 倍高速化したとしても (実際、以下で提案する方法でおそらく可能です)、実際のケースはアプリで使用される全体の CPU 時間の 1% にすぎない場合、最終結果は次のようにはなりません。劇的な速度の向上 - ほとんど目立たないでしょう。

そのような費用の理由は何ですか?

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

ほとんどの場合、コストは、日付形式文字列を解析/検証する必要があることと、あらゆる種類のロケール固有のグープを実行する必要があることの両方に関連してNSDateFormatterいます。Cocoa はローカリゼーションを非常に完全にサポートしていますが、そのサポートには複雑さが伴います。

かなり素晴らしいサンプル プログラムをどのように作成したかを見て、Instruments でアプリを起動し、さまざまな CPU サンプリング インストゥルメントを試して、何が CPU サイクルを消費しているのか、そして Instruments がどのように機能するのかを理解することができます (興味深いものが見つかったら、質問を更新してください!)。

スレッドが互いに待機しなければならない場所でブロッキングが発生する可能性はありますか?

複数のスレッドから単一のフォーマッタを使用しても、単純にクラッシュしないことに驚いています。 NSDateFormatterスレッドセーフであることは特に言及していません。したがって、スレッドセーフではないと想定する必要があります。

使い勝手を良くするにはどうすればいいですか?

多くの日付フォーマッタを作成しないでください!

操作のバッチ用に 1 つ保持してから削除するか、常に使用する場合は、アプリの実行の開始時に 1 つ作成し、形式が変更されるまで保持します。

スレッド化の場合、本当に必要な場合は、スレッドごとに 1 つ保持します (これは過剰だと思います。アプリのアーキテクチャは、操作のバッチごとに 1 つ作成する方が賢明です)。

于 2010-12-14T17:54:12.130 に答える
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;
}
于 2011-03-03T17:14:33.020 に答える
3

GDC dispath_once を使用すれば問題ありません。これにより、複数のスレッド間での同期が保証され、日付フォーマッタが 1 回だけ作成されるようになります。

+ (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;
}
于 2014-03-18T12:00:13.993 に答える
3

後に続く-initWithDateFormat:allowNaturalLanguage:代わりに使用すると、はるかに高速になります(おそらく〜2倍)。-init-setDateFormat:

ただし、一般的には、bbum が言ったこと: ホット コード用に日付フォーマッタをキャッシュします。

(編集: これは iOS 6/OSX 10.8 では当てはまりません。現在はすべて同じように高速になっているはずです)

于 2010-12-14T19:26:30.507 に答える
2

ANDの作成/初期化のためNSDateFormatter、フォーマットとロケールの変更には多くのコストがかかります。の再利用を処理する「ファクトリー」クラスを作成しましたNSDateFormatters

作成した瞬間に、フォーマットとロケール情報に基づいてNSCache最大 15 個のインスタンスを保存するインスタンスがあります。NSDateFormatterそのため、後で再びそれらが必要になったときに、NSDateFormatterロケール「pt-BR」を使用して「dd/MM/yyyy」の形式でクラスに問い合わせると、クラスは対応するすでにロードされているNSDateFormatterインスタンスを提供します。

ほとんどの標準アプリケーションでランタイムごとに 15 を超える日付形式を使用するのはエッジ ケースであることに同意する必要があるため、これはそれらをキャッシュするための大きな制限であると思います。1 つまたは 2 つの異なる日付形式のみを使用する場合、読み込まれるNSDateFormatterインスタンスの数はこの数だけになります。私のニーズにはいいですね。

試してみたい方はGitHub で公開しました

于 2013-04-27T07:08:47.253 に答える
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;
         }
    }
}
于 2016-04-18T14:04:09.763 に答える