Commit 9dc7d722 authored by Jon Skeet's avatar Jon Skeet

Optimize enum parsing. In a small enum-heavy benchmark, this improved the time

of the benchmark (parsing a repeated message, where each message had 5 "small"
enum values and 5 "large" enum values) from ~39s to ~11s.

There is a small memory cost per enum used, but I expect this to be trivial compared
with other per-type costs.

Fixes issue 97.
parent 39182753
......@@ -474,7 +474,7 @@ namespace Google.ProtocolBuffers
/// <summary>
/// Reads an enum field value from the stream. If the enum is valid for type T,
/// then the ref value is set and it returns true. Otherwise the unkown output
/// then the ref value is set and it returns true. Otherwise the unknown output
/// value is set and this method returns false.
/// </summary>
[CLSCompliant(false)]
......@@ -482,10 +482,9 @@ namespace Google.ProtocolBuffers
where T : struct, IComparable, IFormattable
{
int number = (int) ReadRawVarint32();
if (Enum.IsDefined(typeof(T), number))
if (EnumHelper<T>.TryConvert(number, ref value))
{
unknown = null;
value = (T) (object) number;
return true;
}
unknown = number;
......@@ -1861,5 +1860,84 @@ namespace Google.ProtocolBuffers
}
#endregion
/// <summary>
/// Helper class to make parsing enums faster.
/// </summary>
private static class EnumHelper<T> where T : struct
{
/// <summary>
/// We use the array form if all values are in the range [0, LimitForArray),
/// otherwise we build a dictionary.
/// </summary>
private const int LimitForArray = 32;
// Only one of these will be populated.
private static readonly Dictionary<int, T> dictionary;
private static readonly T?[] values;
static EnumHelper()
{
// It will actually be a T[], but the CLR will let us convert.
int[] array = (int[]) Enum.GetValues(typeof (T));
if (array.Length == 0)
{
// Empty enum; model with an empty values array.
values = new T?[0];
return;
}
int min = int.MaxValue;
int max = int.MinValue;
foreach (int number in array)
{
min = Math.Min(number, min);
max = Math.Max(number, max);
}
if (min >= 0 && max < LimitForArray)
{
values = new T?[max + 1];
foreach (int number in array)
{
values[number] = (T)(object)number;
}
}
else
{
dictionary = new Dictionary<int, T>();
foreach (int number in array)
{
dictionary[number] = (T)(object)number;
}
}
}
/// <summary>
/// Tries to convert an integer to its enum representation. This would take an out parameter,
/// but the caller uses ref, so this approach is simpler.
/// </summary>
internal static bool TryConvert(int number, ref T value)
{
if (values != null)
{
if (number < 0 || number >= values.Length)
{
return false;
}
T? maybeValue = values[number];
if (maybeValue != null)
{
value = maybeValue.Value;
return true;
}
return false;
}
T converted;
if (dictionary.TryGetValue(number, out converted))
{
value = converted;
return true;
}
return false;
}
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment