【发布时间】:2011-09-22 15:26:01
【问题描述】:
我有一个将 3d 坐标保存在 3 个整数中的结构。在测试中,我将 100 万个随机点的 List 放在一起,然后将二进制序列化用于内存流。
内存流大约为 21 MB - 这似乎非常低效,因为 1000000 点 * 3 个坐标 * 4 个字节应该以至少 11MB 出现
在我的测试台上也需要大约 3 秒。
有任何提高性能和/或尺寸的想法吗?
(如果有帮助,我不必保留 ISerialzable 接口,我可以直接写出到内存流中)
编辑 - 根据下面的答案,我整理了一个序列化摊牌,比较 BinaryFormatter、'Raw' BinaryWriter 和 Protobuf
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using ProtoBuf;
namespace asp_heatmap.test
{
[Serializable()] // For .NET BinaryFormatter
[ProtoContract] // For Protobuf
public class Coordinates : ISerializable
{
[Serializable()]
[ProtoContract]
public struct CoOrd
{
public CoOrd(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
[ProtoMember(1)]
public int x;
[ProtoMember(2)]
public int y;
[ProtoMember(3)]
public int z;
}
internal Coordinates()
{
}
[ProtoMember(1)]
public List<CoOrd> Coords = new List<CoOrd>();
public void SetupTestArray()
{
Random r = new Random();
List<CoOrd> coordinates = new List<CoOrd>();
for (int i = 0; i < 1000000; i++)
{
Coords.Add(new CoOrd(r.Next(), r.Next(), r.Next()));
}
}
#region Using Framework Binary Formatter Serialization
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Coords", this.Coords);
}
internal Coordinates(SerializationInfo info, StreamingContext context)
{
this.Coords = (List<CoOrd>)info.GetValue("Coords", typeof(List<CoOrd>));
}
#endregion
# region 'Raw' Binary Writer serialization
public MemoryStream RawSerializeToStream()
{
MemoryStream stream = new MemoryStream(Coords.Count * 3 * 4 + 4);
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(Coords.Count);
foreach (CoOrd point in Coords)
{
writer.Write(point.x);
writer.Write(point.y);
writer.Write(point.z);
}
return stream;
}
public Coordinates(MemoryStream stream)
{
using (BinaryReader reader = new BinaryReader(stream))
{
int count = reader.ReadInt32();
Coords = new List<CoOrd>(count);
for (int i = 0; i < count; i++)
{
Coords.Add(new CoOrd(reader.ReadInt32(),reader.ReadInt32(),reader.ReadInt32()));
}
}
}
#endregion
}
[TestClass]
public class SerializationTest
{
[TestMethod]
public void TestBinaryFormatter()
{
Coordinates c = new Coordinates();
c.SetupTestArray();
// Serialize to memory stream
MemoryStream mStream = new MemoryStream();
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Serialize(mStream, c);
Console.WriteLine("Length : {0}", mStream.Length);
// Now Deserialize
mStream.Position = 0;
Coordinates c2 = (Coordinates)bformatter.Deserialize(mStream);
Console.Write(c2.Coords.Count);
mStream.Close();
}
[TestMethod]
public void TestBinaryWriter()
{
Coordinates c = new Coordinates();
c.SetupTestArray();
MemoryStream mStream = c.RawSerializeToStream();
Console.WriteLine("Length : {0}", mStream.Length);
// Now Deserialize
mStream.Position = 0;
Coordinates c2 = new Coordinates(mStream);
Console.Write(c2.Coords.Count);
}
[TestMethod]
public void TestProtoBufV2()
{
Coordinates c = new Coordinates();
c.SetupTestArray();
MemoryStream mStream = new MemoryStream();
ProtoBuf.Serializer.Serialize(mStream,c);
Console.WriteLine("Length : {0}", mStream.Length);
mStream.Position = 0;
Coordinates c2 = ProtoBuf.Serializer.Deserialize<Coordinates>(mStream);
Console.Write(c2.Coords.Count);
}
}
}
结果(注意 PB v2.0.0.423 beta)
Serialize | Ser + Deserialize | Size
-----------------------------------------------------------
BinaryFormatter 2.89s | 26.00s !!! | 21.0 MB
ProtoBuf v2 0.52s | 0.83s | 18.7 MB
Raw BinaryWriter 0.27s | 0.36s | 11.4 MB
显然这只是看速度/大小,并没有考虑其他任何因素。
【问题讨论】:
-
@Ryan, this answer 建议使用protobuf-net 进行快速序列化。
-
@Ryan 和 protobuf-net "v2" 支持结构。让我稍后再看看(目前不是在 PC 上),但这是一个确定的选择。
-
二进制序列化使用反射。这很慢,但从来都不是真正的问题,因为您将它用于 I/O。为什么你序列化到内存是不可猜测的。
-
内存流 - 因为这将存储在 SQL 数据库而不是文件 sys 中(但不是 11MB,我使用过大的列表来强调性能问题)即使输出到文件同样的问题适用 - 二进制流如果这是原因,文件将不会消除反射的需要。
-
为了澄清 18.7MB - 这被包括整个 Int32 范围所扭曲,其中 protobuf 默认情况下针对较小的数字进行了优化。如果您确实需要 2^31 范围,则可能首选使用固定宽度
标签: c# .net generics serialization