22

Core Data で UUID を効率的に保存および検索する方法を探しています。これらの UUID は、分散システム内の多くの iOS デバイスによって生成されます。これらのデバイスはそれぞれ、約 20 ~ 50k の UUID を保存できます。

UUID をコア データに文字列として格納すると、そのインデックス作成の効率が低下することは明らかです。しかし、一連の調査の結果、UUID をバイナリ データとしてコア データに格納する (およびインデックスを作成する) ことは、 String として格納するよりも効率が悪い可能性があることがわかりました。

SQL には BINARY のようなデータ型や VARBINARY のようなデータ型がないため、サポートされています。Core Data のバイナリ データ型のデータは、SQLit では BLOB として格納されていると思います。BLOB はインデックス作成に最も時間がかかるデータ型である可能性があるため、パフォーマンスに悪影響を及ぼします。

誰もが答えるのを助けることができます.UUIDをコアデータに保存するより効率的な方法はありますか?

4

2 に答える 2

45

それらを ASCII 文字列として保存し、フィールドをインデックスにします。

編集

Egads、私はたまたまいくつかのことをしていて、これに出くわしました。なんて恥ずかしい答えだ。その日はちょっと機嫌が悪かったに違いない。できれば、それを削除して次に進みたいと思います。ただし、それは不可能なので、更新の一部を提供します。

まず、何が「効率的」かを知る唯一の方法は、プログラムの時間とスペース、ソース コードの複雑さとプログラマの労力を考慮して測定することです。

幸いなことに、これは非常に簡単です。

非常に単純な OSX アプリケーションを作成しました。モデルは単一の属性で構成されています: identifier.

属性をインデックスとしてマークしない場合、これは問題になりません。ストアの作成にはさらに時間がかかりますが、クエリははるかに高速になります。

また、バイナリ属性の述語を作成することは、文字列の述語を作成することとまったく同じであることに注意してください。

fetchRequest.predicate =
    [NSPredicate predicateWithFormat:@"identifier == %@", identifier];

アプリケーションは非常に簡単です。まず、N 個のオブジェクトを作成し、UUID を identifier 属性に割り当てます。500 オブジェクトごとに MOC を保存します。次に、すべての識別子を配列に格納し、ランダムにシャッフルします。次に、CD スタック全体が完全に破棄され、メモリからすべてが削除されます。

次に、スタックを再度構築し、識別子を繰り返し処理して、単純なフェッチを行います。フェッチ オブジェクトは、その 1 つのオブジェクトをフェッチするための単純な述語を使用して構築されます。これはすべて autoreleasepool 内で行われ、各フェッチを可能な限り元の状態に保ちます (CD キャッシュとのやり取りがあることは承知しています)。さまざまな手法を比較しているだけなので、それはそれほど重要ではありません。

バイナリ識別子は、UUID の 16 バイトです。

UUID String は、[uuid UUIDString] を呼び出した結果の 36 バイトの文字列で、次のようになります (B85E91F3-4A0A-4ABB-A049-83B2A8E6085E)。

Base64 文字列は 24 バイトの文字列で、16 バイトの UUID バイナリ データを base-64 でエンコードした結果であり、同じ UUID に対して次のようになります (uF6R80oKSrugSYOyqOYIXg==)。

Count は、その実行のオブジェクトの数です。

SQLite サイズは、実際の sqlite ファイルのサイズです。

WAL サイズは、WAL (write-ahead-logging) ファイルの大きさです - 参考までに...

Create は、保存を含むデータベースの作成にかかる秒数です。

クエリは、各オブジェクトをクエリする秒数です。

Data Type     | Count (N) | SQLite Size | WAL Size  | Create  | Query
--------------+-----------+-------------+-----------+---------+---------
Binary        |   100,000 |   5,758,976 | 5,055,272 |  2.6013 |  9.2669
Binary        | 1,000,000 |  58,003,456 | 4,783,352 | 59.0179 | 96.1862
UUID String   |   100,000 |  10,481,664 | 4,148,872 |  3.6233 |  9.9160
UUID String   | 1,000,000 | 104,947,712 | 5,792,752 | 68.5746 | 93.7264
Base64 String |   100,000 |   7,741,440 | 5,603,232 |  3.0207 |  9.2446
Base64 String | 1,000,000 |  77,848,576 | 4,931,672 | 63.4510 | 94.5147

ここで最初に注意すべきことは、データベースの実際のサイズは、格納されているバイト数 (1,600,000 および 16,000,000) よりもはるかに大きいということです。これは、データベースで予想される値です。余分なストレージの量は、実際のオブジェクトのサイズに多少比例します...これは識別子のみを格納するため、オーバーヘッドの割合は高くなります)。

existingObjectWithID:error:次に、速度の問題については、参考までに、同じ 1,000,000 オブジェクトのクエリを実行しましたが、フェッチで object-id を使用すると約 82 秒かかりました (それと、なんと 0.3065 秒かかった呼び出しとの大きな違いに注意してください)。

実行中のコードでインストゥルメントを適切に使用するなど、独自のデータベースをプロファイリングする必要があります。複数の実行を行った場合、数値は多少異なると思いますが、非常に近いため、この分析には必要ありません。

ただし、これらの数値に基づいて、コード実行の効率測定を見てみましょう。

  • 予想どおり、未加工の UUID バイナリ データを格納する方が、スペースの点でより効率的です。
  • 作成時間はかなり近いです (違いは、文字列を作成する時間と必要な追加のストレージ スペースに基づいているようです)。
  • クエリ時間はほとんど同じように見えますが、バイナリ文字列の方が少し遅いようです。これが最初の懸念事項だったと思います-バイナリ属性でクエリを実行します。

バイナリは多くのスペースを獲得しており、作成時間とクエリ時間の両方で僅差の引き分けと見なすことができます。これらだけを考慮すると、バイナリ データの保存が明らかに勝者です。

ソースコードの複雑さとプログラマーの時間はどうですか?

最新バージョンの iOS と OSX を使用している場合、特に NSUUID の単純なカテゴリでは、実質的に違いはありません。

ただし、考慮すべき点が 1 つあります。それは、データベース内のデータの使いやすさです。バイナリ データを保存する場合、データを視覚的に把握するのは困難です。

したがって、何らかの理由で、データベース内のデータを人間にとってより効率的な方法で保存する必要がある場合は、文字列として保存することをお勧めします。そのため、base64 エンコーディング (またはその他のエンコーディング - 既に base-256 エンコーディングになっていることを思い出してください) を検討することをお勧めします。

FWIW、これは、NSData と base64 文字列の両方として UUID に簡単にアクセスできるようにするためのカテゴリの例です。

- (NSData*)data
{
    uuid_t rawuuid;
    [self getUUIDBytes:rawuuid];
    return [NSData dataWithBytes:rawuuid length:sizeof(rawuuid)];
}

- (NSString*)base64String
{
    uuid_t rawuuid;
    [self getUUIDBytes:rawuuid];
    NSData *data = [NSData dataWithBytesNoCopy:rawuuid length:sizeof(rawuuid) freeWhenDone:NO];
    return [data base64EncodedStringWithOptions:0];
}

- (instancetype)initWithBase64String:(NSString*)string
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    if (data.length == sizeof(uuid_t)) {
        return [self initWithUUIDBytes:data.bytes];
    }
    return self = nil;
}

- (instancetype)initWithString:(NSString *)string
{
    if ((self = [self initWithUUIDString:string]) == nil) {
        self = [self initWithBase64String:string];
    }
    return self;
}
于 2012-07-05T03:28:32.423 に答える