CGbR project
I am currently working on a project that generates code from classes. The idea is to achieve maximum performance by replacing reflection use cases with fast, generated code.
Current results look good, but I want to take this chance to find room for further improvement.
Class under test:
[DataContract]
public partial class Partial
{
[DataMember]
public float Price { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List<double> DecimalNumbers { get; set; }
}
Generated JSON
One of the generator creates methods to read and write JSON. Code is available in full at the benchmark project.
To JSON:
writer.Write(",\"Price\":");
writer.Write(Price.ToString(CultureInfo.InvariantCulture));
writer.Write(",\"Name\":");
writer.Write(string.Format("\"{0}\"", Name));
writer.Write(",\"DecimalNumbers\":");
if (DecimalNumbers == null)
writer.Write("null");
else
{
writer.Write('[');
foreach (var value in DecimalNumbers)
{
writer.Write(value.ToString(CultureInfo.InvariantCulture));
writer.Write(',');
}
writer.Write(']');
}
From JSON:
while (reader.Read())
{
// Break on EndObject
if (reader.TokenType == JsonToken.EndObject)
break;
// Only look for properties
if (reader.TokenType != JsonToken.PropertyName)
continue;
switch ((string) reader.Value)
{
case "Price":
reader.Read();
Price = Convert.ToSingle(reader.Value);
break;
case "Name":
reader.Read();
Name = Convert.ToString(reader.Value);
break;
case "DecimalNumbers":
reader.Read(); // Read token where array should begin
if (reader.TokenType == JsonToken.Null)
break;
var decimalnumbers = new List<double>();
while (reader.Read() && reader.TokenType != JsonToken.EndArray)
decimalnumbers.Add(Convert.ToDouble(reader.Value));
DecimalNumbers = decimalnumbers;
break;
Generated binary conversion
Another generator maps the class directly onto a byte[]
.
Converter methods:
public static unsafe void Include(ushort value, byte[] bytes, int index)
{
fixed (byte* b = bytes)
*((ushort*)(b + index)) = value;
}
To binary:
// Convert Price
GeneratorByteConverter.Include(Price, bytes, index);;
index += 4;
// Convert Name
// Two bytes length information for each dimension
GeneratorByteConverter.Include((ushort)(Name == null ? 0 : Name.Length), bytes, index);
index += 2;
if (Name != null) Buffer.BlockCopy(_encoder.GetBytes(Name), 0, bytes, index, Name.Length);
index += Name == null ? 0 : Name.Length;
// Convert DecimalNumbers
// Two bytes length information for each dimension
GeneratorByteConverter.Include((ushort)(DecimalNumbers == null ? 0 : DecimalNumbers.Count), bytes, index);
index += 2;
if (DecimalNumbers != null)
for(var i = 0; i < DecimalNumbers.Count; i++)
{
var value = DecimalNumbers[i];
GeneratorByteConverter.Include(value, bytes, index);;
index += 8;
}
From binary:
// Read Price
Price = BitConverter.ToSingle(bytes, index);
index += 4;
// Read Name
var nameLength = BitConverter.ToUInt16(bytes, index);
index += 2;
Name = _encoder.GetString(bytes, index, nameLength);
index += nameLength;
// Read DecimalNumbers
var decimalnumbersLength = BitConverter.ToUInt16(bytes, index);
index += 2;
var tempDecimalNumbers = new List<double>(decimalnumbersLength);
for (var i = 0; i < decimalnumbersLength; i++)
{
var value = BitConverter.ToDouble(bytes, index);
index += 8;
tempDecimalNumbers.Add(value);
}
DecimalNumbers = tempDecimalNumbers;
I couldn't post all the code here, but maybe one of you already sees something I am missing.
for
instead offoreach
. Back then I didn't know there is a dedicated community for questions like that. – Toxantron 2 days ago