c#最快的序列化机制是什么?

这适用于小型有效载荷。

我期待每100毫秒达到1,000,000,000。

标准的BinaryFormatter非常慢。 DataContractSerializer比BinaryFormatter慢。

对于小对象,协议缓冲区( http://code.google.com/p/protobuf-net/ )似乎比BinaryFormatter慢!

是否有更多的序列化机制应该关注硬编码或开源项目?

编辑:我在内存中序列化然后通过tcp在异步套接字上传输有效负载。 在内存中生成的有效负载是具有ulong标识符的小型双数组(10到500个点)。

您的性能要求将可用的序列化程序限制为0.自定义BinaryWriter和BinaryReader将是您获得的最快速度。

我已经预计Protobuf-net即使对于小对象也会更快……但你也可能想尝试我的协议缓冲端口 。 我有一段时间没有使用过Marc的端口 – 当我上次进行基准测试时,我的速度更快,但是我知道他从那时起经历了一次完全重写:)

我怀疑你无论你做什么都会在100ms内完成10亿个项目的序列化……我认为这只是一个不合理的期望,特别是如果这是写入磁盘的话。 (显然,如果你只是重复覆盖相同的内存 ,你会获得比串行化到磁盘更好的性能,但我怀疑这是你真正要做的。)

如果您能给我们更多的背景信息,我们可以提供更多帮助。 例如,您是否能够将负载分散到多台机器上? (多个内核序列化到同一IO设备不太可能有所帮助,因为如果它写入磁盘或网络,我不希望这是一个CPU绑定操作。)

编辑:假设每个对象是10个双精度数(每个8个字节),带有ulong标识符(4个字节)。 这至少是每个对象84个字节。 所以你试图在100ms内序列化8.4GB。 无论你用什么,我真的不认为这是可以实现的。

我现在正在运行我的Protocol Buffers基准测试(它们每秒都会对字节序列化),但我非常怀疑他们会给你你想要的东西。

你声称小项目比BinaryFormatter慢,但每次测量它我发现完全相反,例如:

WCF绑定使用的序列化的性能测试

我总结一下,特别是对于v2代码,这可能是你最快的选择。 如果您可以发布您的特定基准测试场景,我很乐意帮助您查看“up”内容…如果您不能在此处发布,如果您想直接通过电子邮件发送给我(请参阅个人资料)也可以。 我不知道你所说的时间是否可以在任何方案下进行,但我非常确定我能比你看到的任何东西都快得多。

使用v2代码, CompileInPlace提供了最快的结果 – 它允许一些IL技巧,如果编译为物理dll则无法使用它们。

序列化对象的唯一原因是使它们与通用传输介质兼容。 网络,磁盘等串行器的性能永远不重要,因为传输介质总是比CPU核心的原始性能慢得多。 容易两个数量级或更多。

这也是属性是可接受的权衡的原因。 它们也是I / O绑定的,它们的初始化数据必须从程序集元数据中读取。 这需要第一次读取磁盘。

因此,如果要设置性能要求,则需要将99%的重点放在传输介质的能力上。 100毫秒内的十亿个“有效载荷”需要非常强大的硬件。 假设有效载荷为16字节,您需要在一秒钟内移动160千兆字节。 这甚至超出了机器内部的内存总线带宽。 DDR RAM的速度约为每秒5千兆字节。 一个千兆以太网NIC以每秒125兆字节的速度移动,突发。 假设没有寻求,商品硬盘以每秒65兆字节的速度移动。

使用当前的硬件function,您的目标是不现实的。

您可以通过在数据结构上实现ISerailizable来编写自定义序列化。 无论如何,你可能会面临硬件本身的一些“阻碍”,无法按照这些要求进行序列化。

Proto-Buff非常快,但已经有限制。 => http://code.google.com/p/protobuf-net/wiki/Performance

根据我的经验, Marc的Protocol Buffers实现非常好。 我没有用过Jon的 。 但是,您应该尝试使用技术来最小化数据而不是序列化整个数据。

我想看看以下内容。

  1. 如果消息很小,你应该看看你有什么熵。 您可能拥有可以部分或完全重复数据删除的字段。 如果通信是在两方之间进行的,那么您可以从两端建立字典中获益。

  2. 您使用的TCP具有足够的开销而没有有效负载。 您应该通过将邮件批量处理到更大的捆绑包和/或查看UDP来最小化这一点。 与#1结合使用时进行批量处理可能会让您在平均总体通信时更接近您的要求。

  3. 是完整的数据宽度是双倍还是为了方便起见? 如果不使用额外位,则在转换为二进制流时,这将是优化的机会。

通常,当您需要通过单个接口处理多条消息或者您不知道完整的实现细节时,通用序列化非常有用。 在这种情况下,构建自己的序列化方法以将单个消息结构直接转换为字节数组可能会更好。 既然你知道完整的实现双方直接转换将不会是一个问题。 它还可以确保您可以内联代码并尽可能地防止打包/取消装箱。

这是我所知道的最快的方法。 它确实有它的缺点。 像火箭一样,你不会想要它在你的车上,但它有它的位置。 就像你需要设置你的结构并在管道的两端都有相同的结构。 结构需要是一个固定的大小,或者它比这个例子更复杂。

这是我的机器上的性能(i7 920,12gb ram)释放模式,没有连接调试器。 它在测试期间使用100%cpu,因此该测试受CPU限制。

 Finished in 3421ms, Processed 52.15 GB For data write rate of 15.25 GB/s Round trip passed 

..和代码……

  class Program { unsafe static void Main(string[] args) { int arraySize = 100; int iterations = 10000000; ms[] msa = new ms[arraySize]; for (int i = 0; i < arraySize; i++) { msa[i].d1 = i + .1d; msa[i].d2 = i + .2d; msa[i].d3 = i + .3d; msa[i].d4 = i + .4d; msa[i].d5 = i + .5d; msa[i].d6 = i + .6d; msa[i].d7 = i + .7d; } int sizeOfms = Marshal.SizeOf(typeof(ms)); byte[] bytes = new byte[arraySize * sizeOfms]; TestPerf(arraySize, iterations, msa, sizeOfms, bytes); // lets round trip it. var msa2 = new ms[arraySize]; // Array of structs we want to push the bytes into var handle2 = GCHandle.Alloc(msa2, GCHandleType.Pinned);// get handle to that array Marshal.Copy(bytes, 0, handle2.AddrOfPinnedObject(), bytes.Length);// do the copy handle2.Free();// cleanup the handle // assert that we didnt lose any data. var passed = true; for (int i = 0; i < arraySize; i++) { if(msa[i].d1 != msa2[i].d1 ||msa[i].d1 != msa2[i].d1 ||msa[i].d1 != msa2[i].d1 ||msa[i].d1 != msa2[i].d1 ||msa[i].d1 != msa2[i].d1 ||msa[i].d1 != msa2[i].d1 ||msa[i].d1 != msa2[i].d1) {passed = false; break; } } Console.WriteLine("Round trip {0}",passed?"passed":"failed"); } unsafe private static void TestPerf(int arraySize, int iterations, ms[] msa, int sizeOfms, byte[] bytes) { // start benchmark. var sw = Stopwatch.StartNew(); // this cheats a little bit and reuses the same buffer // for each thread, which would not work IRL var plr = Parallel.For(0, iterations/1000, i => // Just to be nice to the task pool, chunk tasks into 1000s { for (int j = 0; j < 1000; j++) { // get a handle to the struc[] we want to copy from var handle = GCHandle.Alloc(msa, GCHandleType.Pinned); Marshal.Copy(handle.AddrOfPinnedObject(), bytes, 0, bytes.Length);// Copy from it handle.Free();// clean up the handle // Here you would want to write to some buffer or something :) } }); // Stop benchmark sw.Stop(); var size = arraySize * sizeOfms * (double)iterations / 1024 / 1024 / 1024d; // convert to GB from Bytes Console.WriteLine("Finished in {0}ms, Processed {1:N} GB", sw.ElapsedMilliseconds, size); Console.WriteLine("For data write rate of {0:N} GB/s", size / (sw.ElapsedMilliseconds / 1000d)); } } [StructLayout(LayoutKind.Explicit, Size= 56, Pack=1)] struct ms { [FieldOffset(0)] public double d1; [FieldOffset(8)] public double d2; [FieldOffset(16)] public double d3; [FieldOffset(24)] public double d4; [FieldOffset(32)] public double d5; [FieldOffset(40)] public double d6; [FieldOffset(48)] public double d7; } 

如果您不想花时间实现全面的显式序列化/反序列化机制,请尝试以下方法: http : //james.newtonking.com/json/help/html/JsonNetVsDotNetSerializers.htm …

在我使用大对象(1GB +序列化到磁盘时)时,我发现NewtonSoft库生成的文件比使用BinaryFormatter时要小4.5倍,处理秒数要少6倍。