如何加快序列化代码?

时间:2009-10-19 21:18:26

标签: c# performance serialization visual-studio-2005

我有以下代码将List序列化为字节数组,以便通过Web Services进行传输。代码在较小的实体上运行相对较快,但这是一个包含60,000个左右项目的列表。执行formatter.Serialize方法需要几秒钟。无论如何要加速这个?

    public static byte[] ToBinary(Object objToBinary)
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
            formatter.Serialize(memStream, objToBinary);
            memStream.Seek(0, SeekOrigin.Begin);
            return memStream.ToArray();
        }
    }

5 个答案:

答案 0 :(得分:4)

您遇到的效率低下来自多个来源:

  1. 默认的序列化例程使用反射来枚举对象字段并获取它们的值。
  2. 二进制序列化格式将事物存储在以字段的字符串名称为键的关联列表中。
  3. 你那里有一个虚假的ToArray(如Danny所说)。
  4. 通过对ISerializable中包含的对象类型实施List,您可以获得相当大的改进。这将删除使用反射的默认序列化行为。

    如果减少包含序列化数据的关联数组中的元素数量,则可以获得更快的速度。确保您在该关联数组中存储的元素是基本类型。

    最后,你可以消除ToArray但我怀疑你甚至会注意到给你带来的撞击。

答案 1 :(得分:3)

如果您想要一些真正的序列化速度,请考虑使用protobuf-net这是google协议缓冲区的c#版本。它应该是order of magnitude faster二进制格式化程序。

答案 2 :(得分:1)

将一个镜头中60,000个项目的整个数组(或集合)序列化为单个大型byte []数组,而不是单独的块,可能要快得多。让每个单独的对象都由它自己的byte []数组表示您正在使用的系统的其他部分的要求吗?是否已知对象的实际类型?如果您使用特定的Type(可能是所有这些60,000个对象的一些公共基类),那么框架就不必执行那么多的转换和搜索预构建的序列化程序集。现在你只给它Object。

答案 3 :(得分:1)

.ToArray()创建一个新数组,使用不安全的方法将数据复制到现有数组更有效(例如使用fixed访问流的内存,然后使用MemCopy()通过DllImport复制内存)。

还要考虑使用更快的自定义格式化程序。

答案 4 :(得分:0)

我启动了一个代码生成器项目,其中包含二进制文件DataContract-Serialzer that beats at least Json.NET by a factor of 30。您所需要的只是the generator nuget packageadditional lib,它可以更快地替换BitConverter

然后,您创建一个分部类,并使用DataContract和每个可序列化属性DataMember进行装饰。然后,生成器将创建一个ToBytes - 方法,并且可以与附加的lib一起序列化集合。从this post看看我的例子:

var objects = new List<Td>();
for (int i = 0; i < 1000; i++)
{
    var obj = new Td
    {
        Message = "Hello my friend",
        Code = "Some code that can be put here",
        StartDate = DateTime.Now.AddDays(-7),
        EndDate = DateTime.Now.AddDays(2),
        Cts = new List<Ct>(),
        Tes = new List<Te>()
    };
    for (int j = 0; j < 10; j++)
    {
        obj.Cts.Add(new Ct { Foo = i * j });
        obj.Tes.Add(new Te { Bar = i + j });
    }
    objects.Add(obj);
}

使用此生成的ToBytes()方法:

public int Size
{
    get 
    { 
        var size = 24;
        // Add size for collections and strings
        size += Cts == null ? 0 : Cts.Count * 4;
        size += Tes == null ? 0 : Tes.Count * 4;
        size += Code == null ? 0 : Code.Length;
        size += Message == null ? 0 : Message.Length;

        return size;              
    }
}

public byte[] ToBytes(byte[] bytes, ref int index)
{
    if (index + Size > bytes.Length)
        throw new ArgumentOutOfRangeException("index", "Object does not fit in array");

    // Convert Cts
    // Two bytes length information for each dimension
    GeneratorByteConverter.Include((ushort)(Cts == null ? 0 : Cts.Count), bytes, ref index);
    if (Cts != null)
    {
        for(var i = 0; i < Cts.Count; i++)
        {
            var value = Cts[i];
            value.ToBytes(bytes, ref index);
        }
    }
    // Convert Tes
    // Two bytes length information for each dimension
    GeneratorByteConverter.Include((ushort)(Tes == null ? 0 : Tes.Count), bytes, ref index);
    if (Tes != null)
    {
        for(var i = 0; i < Tes.Count; i++)
        {
            var value = Tes[i];
            value.ToBytes(bytes, ref index);
        }
    }
    // Convert Code
    GeneratorByteConverter.Include(Code, bytes, ref index);
    // Convert Message
    GeneratorByteConverter.Include(Message, bytes, ref index);
    // Convert StartDate
    GeneratorByteConverter.Include(StartDate.ToBinary(), bytes, ref index);
    // Convert EndDate
    GeneratorByteConverter.Include(EndDate.ToBinary(), bytes, ref index);
    return bytes;
}

它在~1.5微秒内将每个物体序列化 - &gt; 1,7ms 中的1000个对象。