msgpack / msgpack-cli

MessagePack implementation for Common Language Infrastructure / msgpack.org[C#]
http://msgpack.org
Apache License 2.0
835 stars 175 forks source link

First call to MessagePackSerializer.Get takes several hundred milliseconds #318

Open marcbarry opened 5 years ago

marcbarry commented 5 years ago

In agreement with the previous comment, when I serialise a simple class

public class Person
{
    public string Name { get; set; }
}

The initial call to MessagePackSerializer.Get takes several hundred milliseconds (somewhere between 350ms to 400ms) and the first call to MessagePackSerializer.Unpack() takes a further 30ms~ or so.

Subsequent calls to both MessagePackSerializer.Pack() and MessagePackSerializer.Unpack() are fast.

The delay only seems to happen when the program first runs. I assume this behaviour is by-design, but is there a way to work around, or at least reduce the "slow start" behaviour?

Tested on MSgPack.Cli v1.0.0 targeting .NET Framework 4.5.

Code to reproduce:

using System;
using System.Diagnostics;
using System.IO;
using MsgPack.Serialization;

namespace ConsoleApp1
{
    public static class Program
    {
        static void Main()
        {
            Sw.Start();

            Console.WriteLine($"{Sw.ElapsedMilliseconds}ms");

            // create serializer
            var serializer = MessagePackSerializer.Get<Person>();

            Console.WriteLine($"{Sw.ElapsedMilliseconds}ms");

            for (var i = 0; i < 5; i++)
            {
                var msgpack = Serialise(serializer, new Person { Name = "Bob" });
                var person = Deserialise(serializer, msgpack);

                Console.WriteLine($"{Sw.ElapsedMilliseconds}ms {person.Name}");
            }

            Console.ReadLine();
        }

        static byte[] Serialise(MessagePackSerializer serializer, Person person)
        {
            using (var stream = new MemoryStream())
            {
                serializer.Pack(stream, person);
                return stream.ToArray();
            }
        }

        static Person Deserialise(MessagePackSerializer serializer, byte[] packedBytes)
        {
            using (var stream = new MemoryStream(packedBytes))
            {
                return (Person) serializer.Unpack(stream);
            }
        }

        static readonly Stopwatch Sw = new Stopwatch();
    }

    public class Person
    {
        public string Name { get; set; }
    }
}

Example output:

0ms           <- application starts, executes MessagePackSerializer.Get<Person>()
341ms         <- MessagePackSerializer.Get() returns after 341ms
374ms Bob     <- First call to MessagePackSerializer.Pack() has a slight delay
374ms Bob     <- Subsequent calls to MessagePackSerializer.Pack() complete quickly.
374ms Bob
374ms Bob
374ms Bob

Is there anything I can do to reduce this initial time calls to MessagePackSerializer.Get() take?

See also https://github.com/msgpack/msgpack-cli/issues/191

Thanks!

HPgx commented 5 years ago

Hi,

I am using MessagePack-CSharp and I'm facing the same issue. Did you find a solution to reduce the first call delay ?

Thanks. Harold

marcbarry commented 5 years ago

@yfakariya is the answer to the slow-start / warm-up question to use the SerializerGenerator api? https://github.com/msgpack/msgpack-cli/wiki/Xamarin-and-Unity

yfakariya commented 5 years ago

In fact, serializer generation path is not optmized well. The overhead should be come from runtime code generation. I recognize this should be filed in backlog, mainly memory usage optimization to reduce gc. As @marcbarry said, SerializerGenerator API may help you.