Performance in 3.0

Sep 3, 2008 at 11:35 AM
Hi,

I updated my existing 2.0 install to 3.0 and as I was running tests when I noticed that one of my tests was running very slow.

One of my tests runs a simple loop test of serializing a fairly complex objects meant to stress various different types on an object and it started really holding up the tests. I swap between serializers and JSON.NET 3.0 runs 8-10 times slower now than JavaScriptSerializer or my home grown parser. The test loops 10000 serializations and takes 6 seconds in JSON.NET, and less than 7/100th with both my custom parser and the JavaScriptSerializer. I'd expect JSON.NET to be a little slower because of the extra feature set, but this difference is fairly significant. By comparison running JSON.NET 2.0 runs around 1.7 seconds which is still a bit slower, but much closer at least.

I'm a little worried about this trend as I have a couple of apps that are running very heavy server side JSON generation and while the individual perf is still not terrible it does add up in a heavy server load scenario.

I haven't done any profiling but thought I'd throw this out here before I start looking at this a little closer.

Any thoughts?

+++ Rick ---
Coordinator
Sep 4, 2008 at 5:33 AM
Could you post code of the offending test? I have a copy of dotTrace and can profile what is going on.
Sep 7, 2008 at 12:12 PM
Hi James,

Sorry for the delay in getting back.

I had to remove a bunch of dependencies from the code because I test against my pluggable serializer.

The following code should be fully self contained. I'm testing against version 3 which is the one that's really slow. The same code should also work against V2. V3 is about 3 times+ slower than than V2. And V2 about 2x slower than JavaScriptSerializer.

+++ Rick ---

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Globalization;
using System.Data;
using System.Diagnostics;

using Newtonsoft.Json;
using System.IO;
using System.Web.Script.Serialization;

namespace Westwind.AjaxToolkitTest
{
    /// <summary>
    /// Summary description for JsonSerializerTest
    /// </summary>
    [TestClass]
    public class JsonNetPerf
    {
        private DateTime BaseDate = DateTime.Parse("01/01/2000");

        //JSONSerializer ser = null;

        public JsonNetPerf()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        public string SerializeJsonNet(object value)
        {
            Type type = value.GetType();

            Newtonsoft.Json.JsonSerializer json = new Newtonsoft.Json.JsonSerializer();

            json.NullValueHandling = NullValueHandling.Ignore;

            json.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace;
            json.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore;
            json.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;


            StringWriter sw = new StringWriter();
            Newtonsoft.Json.JsonTextWriter writer = new JsonTextWriter(sw);
           
            if (false)
                writer.Formatting = Formatting.Indented;
            else
                writer.Formatting = Formatting.None;

            writer.QuoteChar = '"';
            json.Serialize(writer, value);

            string output = sw.ToString();
            writer.Close();

            return output;
        }

        public string SerializeWebExtensions(object value)
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();

            List<JavaScriptConverter> converters = new List<JavaScriptConverter>();

            return ser.Serialize(value);
        }


        [TestMethod]
        public void PerformanceTest2()
        {

            TestClass test = new TestClass();

            test.Address1.Address = "fff Street";
            test.Address1.Entered = DateTime.Now.AddDays(20);

            test.BigNumber = 34123123123.121M;
            test.Now = DateTime.Now.AddHours(1);
            test.strings = new List<string>() { "Rick", "Markus egger ][, (2nd)", "Kevin McNeish" };

            cAddress address = new cAddress();
            address.Entered = DateTime.Now.AddDays(-1);
            address.Address = "array address";

            test.Addresses.Add(address);

            address = new cAddress();
            address.Entered = DateTime.Now.AddDays(-2);
            address.Address = "array 2 address";
            test.Addresses.Add(address);

            Stopwatch timed = new Stopwatch();
            timed.Start();

            string json = null;
            for (int x = 0; x < 10000; x++)
            {
                json =  this.SerializeJsonNet(test);
                //json = this.SerializeWebExtensions(test);
            }
            timed.Stop();

            TestContext.WriteLine("{0}", timed.ElapsedMilliseconds + " ms");
            TestContext.WriteLine("{0}", json);
        }
    }


    public class TestClass
    {

        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }
        private string _Name = "Rick";


        public DateTime Now
        {
            get { return _Now; }
            set { _Now = value; }
        }
        private DateTime _Now = DateTime.Now;


        public decimal BigNumber
        {
            get { return _BigNumber; }
            set { _BigNumber = value; }
        }
        private decimal _BigNumber = 1212121.22M;


        public cAddress Address1
        {
            get { return _Address1; }
            set { _Address1 = value; }
        }
        private cAddress _Address1 = new cAddress();




        public List<cAddress> Addresses
        {
            get { return _Addresses; }
            set { _Addresses = value; }
        }
        private List<cAddress> _Addresses = new List<cAddress>();


        public List<string> strings = new List<string>() { "Rick", "Markus", "Kevin" };


        public Dictionary<string, int> dictionary = new Dictionary<string, int> { { "Val asd1", 1 }, { "Val2", 3 }, { "Val3", 4 } };
    }

    public class cAddress
    {

        public string Address
        {
            get { return _Address; }
            set { _Address = value; }
        }
        private string _Address = "32 Kaiea";


        public string Phone
        {
            get { return _Phone; }
            set { _Phone = value; }
        }
        private string _Phone = "(503) 814-6335";


        public DateTime Entered
        {
            get { return _Entered; }
            set { _Entered = value; }
        }
        private DateTime _Entered = DateTime.Parse("01/01/2007", CultureInfo.CurrentCulture.DateTimeFormat);

    }
}


Coordinator
Sep 8, 2008 at 10:11 AM
Thanks for that.

I made a couple of small modifications to cache static type information when serializing. It went from about 7 seconds to 1 second for me.

Updated source code is checked into CodePlex.