Performance test of the methods suggested here along with others found elsewhere:
Note: [MethodImpl(MethodImplOptions.AggressiveInlining)]
Provides a very small but measurable performance difference. Small enough to be insignificant but big enough to be measurable.
Looped 100 million times.
Baseline Time : 00:00:00.5938502
Baseline +Method: 00:00:00.8098170
Marshal : 00:00:10.5734336
Boxed Conversion: 00:00:01.9270779
GenericConv<T> : 00:00:01.3276721
UnsafeConv<t> : 00:00:02.4099777
(dynamic)Conv<T>: 00:00:02.8901075
Surprisingly Marshal provides the slowest performance
While the solution suggested by @MichaC provides the best performance.
Here is the benchmark code:
static void Main(string[] args)
{
int NumReps;
NumReps = 100000000;
Stopwatch SW;
SW = new Stopwatch();
byte[] buffer = new byte[4];
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = BitConverter.GetBytes(I);
}
SW.Stop();
}
Console.WriteLine(@"Baseline Time : {0}", SW.Elapsed.ToString());
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = BitConversion(I);
}
SW.Stop();
}
Console.WriteLine(@"Baseline +Method: {0}", SW.Elapsed.ToString());
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = MarshalConv<int>(I);
}
SW.Stop();
}
Console.WriteLine(@"Marshal : {0}", SW.Elapsed.ToString());
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = BoxedConversion<int>(I);
}
SW.Stop();
}
Console.WriteLine(@"Boxed Conversion: {0}", SW.Elapsed.ToString());
GenericClass<int> GenericConverter = new GenericClass<int>();
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = GenericConverter.GetBytes(I);
}
SW.Stop();
}
Console.WriteLine(@"GenericConv<T> : {0}", SW.Elapsed.ToString());
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = UnsafeConversion<int>(I);
}
SW.Stop();
}
Console.WriteLine(@"UnsafeConv<t> : {0}", SW.Elapsed.ToString());
for (int N = 0; N < 2; N++)
{
SW.Restart();
for (int I = 0; I < NumReps; I++)
{
buffer = DynamicConv<int>(I);
}
SW.Stop();
}
Console.WriteLine(@"(dynamic)Conv<T>: {0}", SW.Elapsed.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static byte[] BitConversion(int input)
{
return BitConverter.GetBytes(input);
}
interface IValueConverter<T> where T : struct
{
byte[] FromValue(T value);
}
class ValueConverter :
IValueConverter<int>,
IValueConverter<double>,
IValueConverter<float>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
byte[] IValueConverter<int>.FromValue(int value)
{
return BitConverter.GetBytes(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
byte[] IValueConverter<double>.FromValue(double value)
{
return BitConverter.GetBytes(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
byte[] IValueConverter<float>.FromValue(float value)
{
return BitConverter.GetBytes(value);
}
}
public class GenericClass<T> where T : struct
{
IValueConverter<T> converter = new ValueConverter() as IValueConverter<T>;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte[] GetBytes(T value)
{
if (converter == null)
{
throw new InvalidOperationException("Unsuported type");
}
return converter.FromValue(value);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static byte[] BoxedConversion<T>(T input)
where T : struct, IConvertible
{
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean:
return BitConverter.GetBytes((bool)(object)input);
case TypeCode.Byte:
return BitConverter.GetBytes((byte)(object)input);
case TypeCode.Char:
return BitConverter.GetBytes((char)(object)input);
case TypeCode.Double:
return BitConverter.GetBytes((double)(object)input);
case TypeCode.Int16:
return BitConverter.GetBytes((short)(object)input);
case TypeCode.Int32:
return BitConverter.GetBytes((int)(object)input);
case TypeCode.Int64:
return BitConverter.GetBytes((long)(object)input);
case TypeCode.SByte:
return BitConverter.GetBytes((sbyte)(object)input);
case TypeCode.Single:
return BitConverter.GetBytes((float)(object)input);
case TypeCode.UInt16:
return BitConverter.GetBytes((ushort)(object)input);
case TypeCode.UInt32:
return BitConverter.GetBytes((uint)(object)input);
case TypeCode.UInt64:
return BitConverter.GetBytes((ulong)(object)input);
default:
throw new NotSupportedException(@"Unsupported privitive type. Example null reference or datetime.");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static byte[] UnsafeConversion<T>(T input)
where T : struct, IConvertible
{
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean:
return BitConverter.GetBytes(Unsafe.As < T, bool>(ref input));
case TypeCode.Byte:
return BitConverter.GetBytes(Unsafe.As < T, byte>(ref input));
case TypeCode.Char:
return BitConverter.GetBytes(Unsafe.As < T, char>(ref input));
case TypeCode.Double:
return BitConverter.GetBytes(Unsafe.As < T, double>(ref input));
case TypeCode.Int16:
return BitConverter.GetBytes(Unsafe.As < T, short>(ref input));
case TypeCode.Int32:
return BitConverter.GetBytes(Unsafe.As < T, int>(ref input));
case TypeCode.Int64:
return BitConverter.GetBytes(Unsafe.As < T, long>(ref input));
case TypeCode.SByte:
return BitConverter.GetBytes(Unsafe.As < T, sbyte>(ref input));
case TypeCode.Single:
return BitConverter.GetBytes(Unsafe.As < T, float>(ref input));
case TypeCode.UInt16:
return BitConverter.GetBytes(Unsafe.As < T, ushort>(ref input));
case TypeCode.UInt32:
return BitConverter.GetBytes(Unsafe.As < T, uint>(ref input));
case TypeCode.UInt64:
return BitConverter.GetBytes(Unsafe.As < T, ulong>(ref input));
default:
throw new NotSupportedException(@"Unsupported privitive type. Example null reference or datetime.");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static byte[] DynamicConv<T>(T input)
where T : struct, IConvertible
{
return BitConverter.GetBytes((dynamic)input);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] MarshalConv<T>(T input) where T : struct
{
int size = Marshal.SizeOf(typeof(T));
var result = new byte[size];
var gcHandle = GCHandle.Alloc(input, GCHandleType.Pinned);
Marshal.Copy(gcHandle.AddrOfPinnedObject(), result, 0, size);
gcHandle.Free();
return result;
}