0

を使用してBinaryFormatter、フロートのかなり単純な多次元配列をシリアル化しますが、問題は任意のプリミティブ型で発生すると思われます。私の多次元アレイには10000x16フロート(160k)が含まれており、PCでのシリアル化は最大8 MB /秒で実行されます(60秒のベンチマークでSSDドライブに最大500 MBを書き込みます)。コード:

        Stopwatch stopwatch = new Stopwatch();

        float[,] data = new float[10000 , 16];  // Two-dimensional array of 160,000 floats.
        // OR
        float[]  data = new float[10000 * 16];  // One-dimensional array of 160,000 floats.

        var formatter = new BinaryFormatter();
        var stream = new FileStream("C:\\Temp\\test_serialization.data", FileMode.Create, FileAccess.Write);

        // Serialize to disk the array 1000 times.
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < 1000; i++)
        {
            formatter.Serialize(stream, data);
        }
        stream.Close();
        stopwatch.Stop();

        TimeSpan ts = stopwatch.Elapsed;

        // Format and display the TimeSpan value.
        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:000}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds);
        Console.WriteLine("Runtime " + elapsedTime);
        var info = new FileInfo(stream.Name);
        Console.WriteLine("Speed: {0:0.00} MB/s", info.Length / ts.TotalSeconds / 1024.0 / 1024.0);

同じことを行いますが、160kフロートの1次元配列を使用すると、同じ量のデータが最大179 MB/sでディスクにシリアル化されます。20倍以上高速です! を使用して2次元配列をシリアル化すると、BinaryFormatterパフォーマンスが低下するのはなぜですか? メモリ内の2つのアレイの基本的なストレージは同一である必要があります。(安全でないネイティブpin_ptrを実行し、C ++ / CLIで2D配列との間でコピーを実行しました)。

ISerializableハックな解決策は、2D配列を1D配列に実装してmemcopy(unsafe / ptr pinning / block memcopy)を実行し、それと次元をシリアル化することです。私が検討しているもう1つのオプションは、への切り替えprotobuf-netです。

4

1 に答える 1

1

データ構造を放棄したり値をコピーしたりする必要はありません。次のコードを使用して同じパフォーマンスを実現できます。

            fixed (float* ptr = data)
            {
                byte* arr = (byte*)ptr;
                int size = sizeof(float);

                for (int j = 0; j < data.Length * size; j++)
                {
                    stream.WriteByte(arr[j]);
                }
            }

基本的に、出力ストリームを自分で書いています。あなたが言ったように、メモリ構造が同じであるため、 float[] を byte[] として使用しているだけです。

逆シリアライズは同じです。StreamReader を使用して float を読み取るか、unsafe を使用してデータをメモリにロードするだけです。

このような基本的なニーズがある場合は、protobuf.net の使用を強くお勧めしません。開発は遅くなり、一人の人間に基づいていたので、かなり危険です (私がパフォーマンスの問題について助けようとしたとき、彼は私が提案した変更をわざわざ見ようともしませんでした)。ただし、複雑なデータ構造をシリアライズしたい場合、バイナリ シリアライゼーションは protobuf よりも遅くはありませんが、後者は .NET プラットフォームで正式にサポートされていません (Google は Java、Python、および C++ 用のコードをリリースしました)。

于 2012-01-25T11:18:56.163 に答える