Testing the performance of known types

In my last post, I claimed that one of the reasons you should try to avoid known types is that it significantly degrades performance. But you should never take anyone’s word about performance, so here’s an actual test comparing the performance of using known types and not using known types:

 

Setup

The test measures serialization and deserialization of a type using known types and without known types. Without known types, we try serializing and deserializing the following DogContainer class:

 

[DataContract]

public class DogContainer

{

    public DogContainer(Dog dog)

    {

        this.animal = dog;

    }

    [DataMember]

    public Dog animal;

}

[DataContract]

public class Animal

{

    [DataMember]

    public int age = 4;

    [DataMember]

    public string name = "Rusty";

}

[DataContract]

public class Dog : Animal

{

    [DataMember]

    public DogBreed breed = DogBreed.LabradorRetriever;

}

public enum DogBreed {

    GermanShepherd,

    LabradorRetriever

}

In the known types case, the only difference is that we use the following AnimalContainer class instead:

[DataContract]

[KnownType(typeof(Dog))]

public class AnimalContainer

{

    public AnimalContainer(Animal animal)

    {

        this.animal = animal;

    }

    [DataMember]

    public Animal animal;

}

 

We’ll be serializing two instances of AnimalContainer/DogContainer:

 

new DogContainer(new Dog())

new AnimalContainer(new Dog())

 

Now, serializing these instances produces very similar XML:

 

<DogContainer xmlns:i="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://schemas.datacontract.org/2004/07/">

  <animal>

    <age>4</age>

    <name>Rusty</name>

    <breed>LabradorRetriever</breed>

  </animal>

</DogContainer>

<AnimalContainer xmlns:i="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://schemas.datacontract.org/2004/07/">

  <animal i:type="Dog">

    <age>4</age>

    <name>Rusty</name>

    <breed>LabradorRetriever</breed>

  </animal>

</AnimalContainer>

The only differences are that the class names are slightly different and AnimalContainer uses xsi:type. This is expected because that instance uses known types, and as I showed in the last post, this is the mechanism by which known types can be resolved correctly.

 

Test

For serialization, I used the following code:

 

XmlObjectSerializer serializer = new DataContractSerializer(graph.GetType());

MemoryStream ms = new MemoryStream();

for (int i = 0; i < 1000000; i++)

{

    serializer.WriteObject(ms, graph);

}

And for deserialization I used:

 

XmlObjectSerializer serializer = new DataContractSerializer(graph.GetType());

MemoryStream ms = new MemoryStream();

serializer.WriteObject(ms, graph);

ms.Flush();

for (int i = 0; i < 1000000; i++)

{

    ms.Position = 0;

    serializer.ReadObject(ms);

}

 

In both cases, graph corresponds to the instance we’re serializing (either AnimalContainer or DogContainer) and I only measured the time spent in the for loop.

 

Results

 Known Types Performance

 

As it turns out, using known types in this situation reduced the serialization throughput by 31% and the deserialization throughput by 24%. So using known types can in fact have a significant impact on your serialization performance.