4

protobuf.netを使用してかなり大量のデータをシリアル化しようとしています。OutOfMemoryExceptionsがスローされるという問題が発生しています。IEnumerable<DTO>メモリを使いすぎないように、を使用してデータをストリーミングしようとしています。エラーの原因となるプログラムの簡略版は次のとおりです。

class Program
{
    static void Main(string[] args)
    {
        using (var f = File.Create("Data.protobuf"))
        {
            ProtoBuf.Serializer.Serialize<IEnumerable<DTO>>(f, GenerateData(1000000));
        }

        using (var f = File.OpenRead("Data.protobuf"))
        {
            var dtos = ProtoBuf.Serializer.DeserializeItems<DTO>(f, ProtoBuf.PrefixStyle.Base128, 1);
            Console.WriteLine(dtos.Count());
        }
        Console.Read();
    }

    static IEnumerable<DTO> GenerateData(int count)
    {
        for (int i = 0; i < count; i++)
        {
            // reduce to 1100 to use much less memory
            var dto = new DTO { Data = new byte[1101] };
            for (int j = 0; j < dto.Data.Length; j++)
            {
                // fill with data
                dto.Data[j] = (byte)(i + j);
            }
            yield return dto;
        }
    }
}

[ProtoBuf.ProtoContract]
class DTO
{
    [ProtoBuf.ProtoMember(1, DataFormat=ProtoBuf.DataFormat.Group)]
    public byte[] Data
    {
        get;
        set;
    }
}

興味深いことに、それぞれの配列のサイズDTOを1100に減らすと、問題は解決します。私の実際のコードでは、似たようなことをしたいのですが、バイトではなく、シリアル化するのはfloatの配列です。注意:問題をスピードアップするために、データ部分の入力をスキップできると思います。

これはprotobufバージョン2.0.0.594を使用しています。どんな助けでも大歓迎です!

編集:

バージョン2.0.0.480でも同じ問題が発生します。コードはバージョン1.0.0.280では実行されませんでした。

4

2 に答える 2

3

k; これは残念なタイミングでした - 基本的に、バッファがいっぱいになるたびにフラッシュするかどうかをチェックするだけで、長さがプレフィックスされたアイテムを書き込んでいる最中の結果として、その時点で適切にフラッシュすることはできませんでした. フラッシュ可能な状態に達し、フラッシュする価値のあるもの (現在は 1024 バイト) が見つかった場合は常に、より積極的にフラッシュするように微調整を追加しました。これは r597 としてコミットされています。そのパッチにより、期待どおりに動作するようになりました。

当面の間、バージョンを変更せずにこの問題を回避する方法があります。ソースのデータを反復処理し、プレフィックス スタイルのベース 128 とフィールド番号 1を指定してそれぞれを個別にシリアル化します。SerializeWithLengthPrefixこれは、ネットワーク上で何が行われるかという点では 100% 同一ですが、それぞれに個別のシリアル化サイクルがあります。

using (var f = File.Create("Data.protobuf"))
{
    foreach(var obj in GenerateData(1000000))
    {
        Serializer.SerializeWithLengthPrefix<DTO>(
            f, obj, PrefixStyle.Base128, Serializer.ListItemTag);
    }
}

気づいてくれてありがとう;p

于 2012-10-31T20:12:45.850 に答える
2

1.5 GB の制限を超えているようです: 32 ビット .NET プロセスで 1,000 MB を超えるメモリを割り当てています

サンプル サイズを小さくすると、アプリケーションが正常に動作することは既にお気づきでしょう。これは protobuf の問題ではありませんが (私は推測します)、1.5 GB を超えるメモリの割り当てを必要とする配列を作成しようとしています。

アップデート

簡単なテストを次に示します。

byte[] data = new byte[2147483648];

これにより、 が発生するはずOutOfMemoryExceptionです。これは次のようになります。

byte[][] buffer = new byte[1024][];
for (int i = 0; i < 1024; i++)
{
    buffer[i] = new byte[2097152];
}

何かがデータ バイトを 1.5 GB を超える連続したコンテナーに集約しています。

于 2012-10-31T16:46:32.460 に答える