Serializing Data Transfer for Performance

Sometime when you review other’s code you see coding that causes you to ask “Why code it THAT way, instead of {2-6 alternative approaches}”. With CLR I have long stated, “What you get for performance is often NOT what you expected for performance”. I decided to look at the performance of three ways of passing data via an Array (best performer in early blog) via serialization. I exclude the actual data transfer via TCP that would occur in a Web Service/WCF – but included the LENGTH of data serialized as a data point.

  • Assembly the data
  • Convert to an Object or a Structure and add to an List
  • Convert the list to an Array
  • Serialize the array
  • Deserialize the array to return the original array.

The objects are simple: A Structure – light weight and one would expect best performance

   1: public struct ViaStructure
   2: {
   3:     public string Name;
   4:     public int NameLength;
   5:     public bool IsEmpty;
   6: }

A classic object equivalent:

   1: public class ViaObjectPast
   2: {
   3:     private string _Name;
   4:     private int _NameLength;
   5:     private bool _IsEmpty;
   6:     public ViaObjectPast()
   7:     {
   8:     }
   9:     public string Name { get { return _Name; } set { _Name = value; } }
  10:     public int NameLength { get { return _NameLength; } set { _NameLength = value; } }
  11:     public bool IsEmpty { get { return _IsEmpty; } set { _IsEmpty = value; } }
  12: }

And the new style object equivalent:

   1: public ViaObjectPresent()
   2: {
   3: }
   4: public string Name { get ;set;}
   5: public int NameLength { get ;set;}
   6: public bool IsEmpty { get ;set;}
   7:     }

There are two ways of creating and applying values to these:

  • A – Using Initializers
  • B – Classic assignment
   1: Array.ForEach(asList, item =>
   2: {
   3:     vitem = new ViaStructure() { Name = item.Name.LocalName, NameLength = item.Name.LocalName.Length, IsEmpty = item.IsEmpty };
   4:     list.Add(vitem);
   5: });

and B:

   1: Array.ForEach(asList, item =>
   2:          {
   3:              vitem = new ViaStructure();
   4:              vitem.NameLength = item.Name.LocalName.Length;
   5:              vitem.IsEmpty = item.IsEmpty;
   6:              vitem.Name = item.Name.LocalName;
   7:              list.Add(vitem);
   8:          });

Once the list was built, we converted it to an Array

   1: var list1 = list.ToArray();

Then serialized via this code, and then deserialize it.

   1: using (var sw = new StringWriter())
   2: {
   3:     XmlSerializer ser = new XmlSerializer(typeof(ViaStructure[]));
   4:     ser.Serialize(sw, list1);
   5:     build2=DateTime.Now;
   6:     slen = serialized.Length;
   7:     using(StringReader sw=new StringReader(serialized))
   8:     {
   9:     var list2=(ViaStructure[]) ser.Deserialize(sw);
  10:     build3=DateTime.Now;
  11:     }
  12:     
  13: }

A bunch of parallel code that follow the pattern of my prior blogs. Executing thirty loops for each and received the results below.

 

Platform Targeted for x86

  Values      
Row Labels Min of BuildArray Min of Deserialize Min of Serialize Min of Length
NoOp 0.0 0.0 0.0 0.0
Object Current A 24.4 1072.3 1030.3 20155197.0
Object Current B 23.4 1058.6 1058.6 20155197.0
Object Past A 23.4 1065.4 952.1 19266381.0
Object Past B 24.4 1015.6 955.1 19266381.0
Structure A 39.1 1003.9 2249.0 18970109.0
Structure B 35.2 986.3 2221.7 18970109.0

Platform Targeted for x64

  Values      
Row Labels Min of BuildArray Min of Deserialize Min of Serialize Min of Length
NoOp 0.0 0.0 0.0 0.0
Object Current A 23.4 1040.0 986.3 20155197.0
Object Current B 23.4 1063.5 994.1 20155197.0
Object Past A 22.5 1017.6 899.4 19266381.0
Object Past B 22.5 1046.9 894.5 19266381.0
Structure A 33.2 996.1 2211.9 18970109.0
Structure B 30.3 1000.0 2230.5 18970109.0

 

Platform Targeted for Any

  Values      
Row Labels Min of BuildArray Min of Deserialize Min of Serialize Min of Length
NoOp 0.0 0.0 0.0 0.0
Object Current A 25.4 1044.9 1137.7 20155197.0
Object Current B 25.4 1083.0 1117.2 20155197.0
Object Past A 23.4 1025.4 989.3 19266381.0
Object Past B 24.4 1078.1 966.8 19266381.0
Structure A 42.0 1015.6 2225.6 18970109.0
Structure B 37.1 1018.6 2282.2 18970109.0

Conclusions

 

There seems to be no significant difference between using initializers and the classic assignment of properties. One shocker is that targeting for ANY appears to result in the poorest performance (about 10% worst overall) – this makes sense because as “no man can serve two masters”, no code can perform better (or equivalent) when it has two targets – the flexibility of going either way comes at a cost!

  • For Building the Array
    • x64 appear marginally faster (longer runs are needed to determine if this is true).
    • There appears to be no clear difference between the two ways of defining properties.
    • Structures are about 50% slower (unexpected results -- .Net Clr may need tuning here)
  • Serialization
    • Current pattern ({get;set}) runs 10% slower (unexpected results) then using private variables
    • Structures are > 120% slower (unexpected results)
    • There appears to be no clear difference between the two ways of defining properties.
  • Deserialization
    • Current pattern ({get;set}) are fuzzy. With x64 it appears slightly faster, with x86 it appears slightly slower.
    • Structures are > 5% faster
    • There appears to be no clear difference between the two ways of defining properties.
  • Length has more variation then expected:
    • Structure was 5% more compact then current object pattern (get;set}
    • Structure was 1% more compact then past object pattern (private variables)

Before jumping to conclusions, I should mention that my source was XML. If I had simply transferred the Xml without serialization/deserialization and built the array at the destination the performance difference could be in the order of 2.2 seconds(serialization) versus 0.023 seconds (a 100 fold difference). This begs the question – why not move either a dataset(serialized to Xml via SaveXml) or raw xml instead of inflecting the heavy cost of serialization?

A forthcoming blog will look at moving XML from the database to a client with a variety of approaches.

Comments

Popular posts from this blog

Yet once more into the breech (of altered programming logic)

Simple WP7 Mango App for Background Tasks, Toast, and Tiles: Code Explanation

How to convert SVG data to a Png Image file Using InkScape