See the question and my original answer on StackOverflow

The first thing is to determine what ASP.NET is capable of serializing out-of-the-box. Since ASP.NET reference source is available, we can drill state serialization down to this class: AltSerialization

It shows that the following types are natively supported:

String
Int32
Boolean
DateTime
Decimal
Byte
Char
Single
Double
SByte
Int16
Int64
UInt16
UInt32
UInt64
TimeSpan
Guid
IntPtr
UIntPtr
Null // not really a type :-)

For all other types, the system uses a BinaryFormatter, which is a public .NET class. BinaryFormatter is very easy to use, it knows many classes by default, so you can do simple benchmarks comparing what BinaryFormatter will produce versus what you can yourself "manually".

In general you will always be able to beat BinaryFormatter's performance because you know your data better, but it means you'll have to write additional serialization/deserialization code. You can also create custom classes and implement ISerializable.

One last note, if you look at AltSerialization source, you'll see this:

internal static void WriteValueToStream(Object value, BinaryWriter writer)
{
    if (known type) { ... }
    else
    {
        ...
        var formatter = new BinaryFormatter();
        if (SessionStateUtility.SerializationSurrogateSelector != null)
        {
            formatter.SurrogateSelector = SessionStateUtility.SerializationSurrogateSelector;
        }

        formatter.Serialize(writer.BaseStream, value);
    }
}

which means that you can implement a global hook to handle specific types in a central place, which can be useful in terms of engeneering, or to do benchmarks (you can enable it or disable it easily). It may be better than create serializable custom classes.