SYSK 71: Dataset Footprint in Memory vs. Remoting Size

A lot of good discussions have been held over the last several years on the remoting size of a dataset.  Significant improvements have been made in ADO.NET 2.0 to reduce the remoting footprint of a dataset by using binary serialization.  But is the dataset object itself taking any less memory?  In other words, if I were to use the dataset instance vs. custom strongly typed object, what is the difference in the memory footprint?  I want to emphasize that, in this case, I’m more interested in the object’s memory utilization, not it’s remoted size (although, I’ve measured both).

 

I took the AdventureWorks database, with the following simple query -- select * from production.product.  This resulted in 504 rows.  Saving the results in notepad, ended up with a file of 100Kb (that’s counting tab characters, NULL spelled out as text, etc.)  This gives us a very rough idea on the number of bytes utilized by the data itself.

 

My measurements show the following:

  • Memory consumption using Dataset = 290,436 (3.5 times larger than using Products class)
  • Memory consumption using array of objects = 140,028 (1.7 times larger than using Products class)
  • Memory consumption using Product class = 82,656 (BEST)

 

  • Remoting footprint of DataSet using binary serialization & binary formatter = 124,742 (BEST for DataSet, 1.35 times larger than using Products class)
  • Remoting footprint of DataSet using xml serialization & soap formatter = 530,222 (4.25 times larger than binary/binary)
  • Remoting footprint of DataSet using xml serialization & binary formatter = 367,403 (3 times larger than binary/binary)
  • Remoting footprint of Products class using binary formatter = 92,598 (BEST overall)

 

So, no matter how you turn it, strongly typed custom classes are your best bet in terms of memory consumption!

 

Oh, one more thing – the serialization itself took 30 ms using binary formatter and 80 ms using XML/SOAP.

 

 

Below is the code used:

 

namespace WindowsApplication1

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

       

        private void button1_Click(object sender, EventArgs e)

        {

            System.Data.SqlClient.SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter("select * from production.product", "Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=True");

            System.Data.DataSet ds = new DataSet();

            long s1 = GC.GetTotalMemory(true);

            da.Fill(ds);

  long s2 = GC.GetTotalMemory(true);

            System.Diagnostics.Debug.WriteLine(string.Format("Memory consumption using Dataset = {0}", (s2-s1)));

            // 504 rows

           

            long dataSize = 0;

            object[] data = new object[ds.Tables[0].Rows.Count];

            int i = 0;

            long d1 = GC.GetTotalMemory(true);

            foreach (System.Data.DataRow row in ds.Tables[0].Rows)

            {

                data[i++] = row.ItemArray;

         }

            long d2 = GC.GetTotalMemory(true);

            dataSize += (d2 - d1);

            System.Diagnostics.Debug.WriteLine(string.Format("Memory consumption using array of objects = {0}", dataSize));

            long dataSize2 = 0;

           object[] data2 = new object[ds.Tables[0].Rows.Count];

            i = 0;

            long d11 = GC.GetTotalMemory(true);

            foreach (System.Data.DataRow row in ds.Tables[0].Rows)

            {

                data2[i++] = new Product(row.ItemArray);

            }

            long d22 = GC.GetTotalMemory(true);

            dataSize2 += (d22 - d11);

            System.Diagnostics.Debug.WriteLine(string.Format("Memory consumption using Product class = {0}", dataSize2));

         

  ds.RemotingFormat = SerializationFormat.Binary;

            long l1 = GetObjectSize(ds);

            System.Diagnostics.Debug.WriteLine(string.Format("Remoting footprint of DataSet using binary serialization & binary formatter = {0}", l1));

   ds.RemotingFormat = SerializationFormat.Xml;

            long l2 = GetSoapObjectSize(ds);

            System.Diagnostics.Debug.WriteLine(string.Format("Remoting footprint of DataSet using xml serialization & soap formatter = {0}", l2));

         ds.RemotingFormat = SerializationFormat.Xml;

            long l3 = GetObjectSize(ds);

            System.Diagnostics.Debug.WriteLine(string.Format("Remoting footprint of DataSet using xml serialization & binary formatter = {0}", l3));

            long l4 = GetObjectSize(data2);

            System.Diagnostics.Debug.WriteLine(string.Format("Remoting footprint of Products class using binary formatter = {0}", l4));

        }

        private long GetObjectSize(object data)

        {

          long result = 0;

            System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

            System.IO.MemoryStream stream = new System.IO.MemoryStream();

            long t1 = DateTime.Now.Ticks;

            formatter.Serialize(stream, data);

            long t2 = DateTime.Now.Ticks;

            result = stream.Length;

            stream.Close();

            System.Diagnostics.Debug.WriteLine((t2 - t1) / TimeSpan.TicksPerMillisecond + " ms");

            return result;

        }

        private long GetSoapObjectSize(object data)

        {

            long result = 0;

            System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();

            System.IO.MemoryStream stream = new System.IO.MemoryStream();

            long t1 = DateTime.Now.Ticks;

            formatter.Serialize(stream, data);

            long t2 = DateTime.Now.Ticks;

            result = stream.Length;

  stream.Close();

            System.Diagnostics.Debug.WriteLine((t2 - t1) / TimeSpan.TicksPerMillisecond + " ms");

            return result;

        }

    }

    [Serializable]

    public class Product

    {

        public int ProductID;

     public string Name;

        public string ProductNumber;

        public bool MakeFlag;

        public bool FinishedGoodsFlag;

        public string Color;

        public short SafetyStockLevel;

        public short ReorderPoint;

        public decimal StandardCost;

        public decimal ListPrice;

        public string Size;

        public string SizeUnitMeasureCode;

        public string WeightUnitMeasureCode;

        public decimal Weight;

        public int DaysToManufacture;

        public string ProductLine;

        public string Class;

        public string Style;

        public int ProductSubcategoryID;

        public int ProductModelID;

        public DateTime SellStartDate;

        public DateTime SellEndDate;

        public DateTime DiscontinuedDate;

        public Guid rowguid;

        public DateTime ModifiedDate;

        public Product(object[] data)

        {

            ProductID = (int) data[0];

            Name = (string) data[1];

            ProductNumber = (string) data[2];

           MakeFlag = (bool) data[3];

            FinishedGoodsFlag = (bool) data[4];

            if (!(data[5] is DBNull))

                Color = (string)data[5];

            SafetyStockLevel = (short)data[6];

            ReorderPoint = (short)data[7];

           StandardCost = (decimal)data[8];

            ListPrice = (decimal)data[9];

            if (!(data[10] is DBNull))

                Size = (string)data[10];

            if (!(data[11] is DBNull))

                SizeUnitMeasureCode = (string)data[11];

     if (!(data[12] is DBNull))

                WeightUnitMeasureCode = (string)data[12];

            if (!(data[13] is DBNull))

                Weight = (decimal)data[13];

            DaysToManufacture = (int)data[14];

            if (!(data[15] is DBNull))

                ProductLine = (string)data[15];

            if (!(data[16] is DBNull))

                Class = (string)data[16];

            if (!(data[17] is DBNull))

                Style = (string)data[17];

            if (!(data[18] is DBNull))

  ProductSubcategoryID = (int)data[18];

            if (!(data[19] is DBNull))

                ProductModelID = (int)data[19];

            SellStartDate = (DateTime) data[20];

            if (!(data[21] is DBNull))

                SellEndDate = (DateTime) data[21];

            if (!(data[22] is DBNull))

                DiscontinuedDate = (DateTime)data[22];

            rowguid = (System.Guid)data[23];

            ModifiedDate = (DateTime)data[24];

        }

    }

}