多次元汎用配列のバイナリシリアル化の最適化
-
03-07-2019 - |
質問
バイナリシリアル化する必要があるクラスがあります。クラスには、次の1つのフィールドが含まれます。
private T[,] m_data;
これらの多次元配列は、かなり大きく(数十万の要素)、任意のプリミティブ型にすることができます。オブジェクトで標準の.netシリアル化を試みたとき、ディスクに書き込まれたファイルは大きく、.netは要素タイプに関する多くの繰り返しデータを格納しており、おそらく可能な限り効率的ではないと考えています。
カスタムシリアライザーを探しましたが、多次元の汎用配列を扱うものは見ていません。また、シリアル化に続いてメモリストリームのバイト配列で組み込みの.net圧縮を試しましたが、期待したほど速く/圧縮されませんでした。
私の質問は、カスタムシリアライザーを作成して適切な型に合わせてこの配列を最適にシリアル化する必要がありますか(これは少し厄介なようです)、または標準の.netシリアル化を使用して圧縮を追加する必要がありますか?
最良のアプローチに関するアドバイス、または多次元の汎用配列のシリアル化に取り組む方法を示すリソースへのリンクをお勧めします-既存の例このような構造はサポートされていません。
解決
これが私が思いついたものです。以下のコードはint [1000] [10000]を作成し、BinaryFormatterを使用して2つのファイルに書き込みます(1つは圧縮され、もう1つは圧縮されません)。
zipファイルは1.19 MB(1,255,339バイト) 解凍されたサイズは38.2 MB(40,150,034バイト)
int width = 1000;
int height = 10000;
List<int[]> list = new List<int[]>();
for (int i = 0; i < height; i++)
{
list.Add(Enumerable.Range(0, width).ToArray());
}
int[][] bazillionInts = list.ToArray();
using (FileStream fsZ = new FileStream("c:\\temp_zipped.txt", FileMode.Create))
using (FileStream fs = new FileStream("c:\\temp_notZipped.txt", FileMode.Create))
using (GZipStream gz = new GZipStream(fsZ, CompressionMode.Compress))
{
BinaryFormatter f = new BinaryFormatter();
f.Serialize(gz, bazillionInts);
f.Serialize(fs, bazillionInts);
}
これを行うためのより良い/簡単な方法は考えられません。 zipバージョンは非常にタイトです。
BinaryFormatter + GZipStreamを使用します。何かをカスタマイズすることはまったく面白くないでしょう。
[MGによる編集] 編集によって気分を害されないことを願っていますが、均一な繰り返しRange(0、width)が物事を大きく歪ませています。変更先:
int width = 1000;
int height = 10000;
Random rand = new Random(123456);
int[,] bazillionInts = new int[width, height];
for(int i = 0 ; i < width;i++)
for (int j = 0; j < height; j++)
{
bazillionInts[i, j] = rand.Next(50000);
}
そして試してみてください。 40MBの temp_notZipped.txt
、62MBの temp_zipped.txt
が表示されます。あまり魅力的ではない...
他のヒント
最良のコード長/出力サイズ比は、BitConverterを使用して配列をエンコードし、すべての要素をコンパクトなバイナリ形式に変換することです。マニュアルですが、.NETバイナリシリアル化に比べて80〜90%のスペースを節約できます。
「大」を定義できますか? 1000x10000xintの例(別の投稿)は40Mbで出てきます。 1000x10000x4バイト(= int)は38MBです。オーバーヘッドがかかるので、それはひどいことではありません。
Tはどのようなデータになる可能性がありますか?ただの原始人? protobuf-net を編集して、長方形の配列をサポートできると考えています< code> * -ただし、何らかのワイヤ互換性を保つには、おそらく要素ごとにヘッダー(1バイト)が必要になります -1000x10000の例では9MBのオーバーヘッドです。
これはおそらく float
、 double
などの場合には価値がありません(&quot; protocol buffers&quot;の下にそのまま格納されるため)-しかし、 int
のようなものの節約は、単純にintをパックする方法によるものです(特に、それらが小さい側[大きさ]にある場合)。最後に、Tが実際に Person
などのオブジェクトである場合、オブジェクトのパッキングが非常に優れているため、バイナリシリアル化よりも lot である必要があります。
長方形の配列で靴べらを鳴らすのは簡単なことではありませんが、これが試してみたいものかどうかを教えてください。
*
:&quot; protocol buffers&quot;以来、現時点ではありません。スペックはそれらをサポートしていませんが、それを回避することができます...
型について非常に多くのデータが必要な理由は、Tの配列は任意の型である可能性がありますが、より具体的には、Tは型SomeBaseClassであり、その配列にSomeDerivedClassを格納でき、デシリアライザーこれを知る必要があります。
しかし、この冗長データは、他の人が指摘したように、圧縮の良い候補になります。