title: Improve Binary Serialization Performance for large List of structs
body: <p>I have a structure holding 3d co-ordinates in 3 ints. In a test I've put together a List&lt;> of  1 million random points and then used Binary serialization to a memory stream.</p>
  
  <p>The memory stream is coming in a ~ 21 MB - which seems very inefficient as 1000000 points * 3 coords * 4 bytes should come out at 11MB minimum</p>
  
  <p>Its also taking ~ 3 seconds on my test rig.</p>
  
  <p>Any ideas for improving performance and/or size? </p>
  
  <p>(I don't have to keep the ISerialzable interface if it helps, I could write out directly to a memory stream)</p>
  
  <p><strong>EDIT</strong> - From answers below I've put together a serialization showdown comparing BinaryFormatter, 'Raw' BinaryWriter and Protobuf</p>
  
  <pre><code>using System;
  using System.Text;
  using System.Collections.Generic;
  using System.Linq;
  using Microsoft.VisualStudio.TestTools.UnitTesting;
  using System.Runtime.Serialization;
  using System.Runtime.Serialization.Formatters.Binary;
  using System.IO;
  using ProtoBuf;
  
  namespace asp_heatmap.test
  {
      [Serializable()] // For .NET BinaryFormatter
      [ProtoContract] // For Protobuf
      public class Coordinates : ISerializable
      {
          [Serializable()]
          [ProtoContract]
          public struct CoOrd
          {
              public CoOrd(int x, int y, int z)
              {
                  this.x = x;
                  this.y = y;
                  this.z = z;
              }
              [ProtoMember(1)]            
              public int x;
              [ProtoMember(2)]
              public int y;
              [ProtoMember(3)]
              public int z;
          }
  
          internal Coordinates()
          {
          }
  
          [ProtoMember(1)]
          public List&lt;CoOrd&gt; Coords = new List&lt;CoOrd&gt;();
  
          public void SetupTestArray()
          {
              Random r = new Random();
              List&lt;CoOrd&gt; coordinates = new List&lt;CoOrd&gt;();
              for (int i = 0; i &lt; 1000000; i++)
              {
                  Coords.Add(new CoOrd(r.Next(), r.Next(), r.Next()));
              }
          }
  
          #region Using Framework Binary Formatter Serialization
  
          void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
          {
              info.AddValue("Coords", this.Coords);
          }
  
          internal Coordinates(SerializationInfo info, StreamingContext context)
          {
              this.Coords = (List&lt;CoOrd&gt;)info.GetValue("Coords", typeof(List&lt;CoOrd&gt;));
          }
  
          #endregion
  
          # region 'Raw' Binary Writer serialization
  
          public MemoryStream RawSerializeToStream()
          {
              MemoryStream stream = new MemoryStream(Coords.Count * 3 * 4 + 4);
              BinaryWriter writer = new BinaryWriter(stream);
              writer.Write(Coords.Count);
              foreach (CoOrd point in Coords)
              {
                  writer.Write(point.x);
                  writer.Write(point.y);
                  writer.Write(point.z);
              }
              return stream;
          }
  
          public Coordinates(MemoryStream stream)
          {
              using (BinaryReader reader = new BinaryReader(stream))
              {
                  int count = reader.ReadInt32();
                  Coords = new List&lt;CoOrd&gt;(count);
                  for (int i = 0; i &lt; count; i++)                
                  {
                      Coords.Add(new CoOrd(reader.ReadInt32(),reader.ReadInt32(),reader.ReadInt32()));
                  }
              }        
          }
          #endregion
      }
  
      [TestClass]
      public class SerializationTest
      {
          [TestMethod]
          public void TestBinaryFormatter()
          {
              Coordinates c = new Coordinates();
              c.SetupTestArray();
  
              // Serialize to memory stream
              MemoryStream mStream = new MemoryStream();
              BinaryFormatter bformatter = new BinaryFormatter();
              bformatter.Serialize(mStream, c);
              Console.WriteLine("Length : {0}", mStream.Length);
  
              // Now Deserialize
              mStream.Position = 0;
              Coordinates c2 = (Coordinates)bformatter.Deserialize(mStream);
              Console.Write(c2.Coords.Count);
  
              mStream.Close();
          }
  
          [TestMethod]
          public void TestBinaryWriter()
          {
              Coordinates c = new Coordinates();
              c.SetupTestArray();
  
              MemoryStream mStream = c.RawSerializeToStream();
              Console.WriteLine("Length : {0}", mStream.Length);
  
              // Now Deserialize
              mStream.Position = 0;
              Coordinates c2 = new Coordinates(mStream);
              Console.Write(c2.Coords.Count);
          }
  
          [TestMethod]
          public void TestProtoBufV2()
          {
              Coordinates c = new Coordinates();
              c.SetupTestArray();
  
              MemoryStream mStream = new MemoryStream();
              ProtoBuf.Serializer.Serialize(mStream,c);
              Console.WriteLine("Length : {0}", mStream.Length);
  
              mStream.Position = 0;
              Coordinates c2 = ProtoBuf.Serializer.Deserialize&lt;Coordinates&gt;(mStream);
              Console.Write(c2.Coords.Count);
          }
      }
  }
  </code></pre>
  
  <p>Results <em>(Note PB v2.0.0.423 beta)</em></p>
  
  <pre><code>                Serialize | Ser + Deserialize    | Size
  -----------------------------------------------------------          
  BinaryFormatter    2.89s  |      26.00s !!!      | 21.0 MB
  ProtoBuf v2        0.52s  |       0.83s          | 18.7 MB
  Raw BinaryWriter   0.27s  |       0.36s          | 11.4 MB
  </code></pre>
  
  <p>Obviously this is just looking at speed/size and doesn't take into account anything else.</p>