patterncsharpModerate
Runtime compiler for getting/setting runtime property values
Viewed 0 times
runtimegettingsettingpropertyforcompilervalues
Problem
I've been playing about with runtime compilation to allow me to get/set runtime property values in C#, and so far I have come up with a class.
This allows me to write code like the following which works well and is much faster than standard reflection:
What I'm wondering is whether there is a way to make this even faster. I could go into metaprogramming and use
Is there something obvious you can see?
Here's the current benchmarks using Benchmark.NET:
And the class itself:
`public class TypeWrapper
{
private readonly dynamic dyn;
private readonly Dictionary>> setters
= new Dictionary>>();
private readonly Dictionary>> getters
= new Dictionary>>();
public TypeWrapper(object d)
{
this.dyn = d;
Type type = d.GetType();
foreach (var p in typ
This allows me to write code like the following which works well and is much faster than standard reflection:
TypeWrapper wrapper = new TypeWrapper(target);
wrapper.Set("Value", "abc");
return (string)wrapper.Get("Value");What I'm wondering is whether there is a way to make this even faster. I could go into metaprogramming and use
Reflection.Emit, but that adds a vastly increased order of complexity for other developers to follow.Is there something obvious you can see?
Here's the current benchmarks using Benchmark.NET:
Total time: 00:01:47 (107.64 sec)
// Summary
BenchmarkDotNet=v0.9.4.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz, ProcessorCount=12
Frequency=3117484 ticks, Resolution=320.7715 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
JitModules=clrjit-v4.6.1063.1
Method | Median | StdDev | Scaled |
--------------------------- |------------ |----------- |------- |
1. Static C# | 2.5401 ns | 0.6457 ns | 1.00 |
2. Dynamic C# | 35.1664 ns | 0.4643 ns | 13.84 |
3. PropertyInfo | 430.8785 ns | 6.4399 ns | 169.63 |
4. PropertyDescriptor | 920.3483 ns | 59.1531 ns | 362.32 |
5. TypeWrapper | 117.0505 ns | 1.6790 ns | 46.08 |
// BenchmarkRunner: End
- Static C#: 2.54 ns
- Dynamic C#: 35.17 ns
- PropertyInfo: 430.88 ns
- PropertyDescriptor: 920.35 ns
- TypeWrapper: 117.05 ns
And the class itself:
`public class TypeWrapper
{
private readonly dynamic dyn;
private readonly Dictionary>> setters
= new Dictionary>>();
private readonly Dictionary>> getters
= new Dictionary>>();
public TypeWrapper(object d)
{
this.dyn = d;
Type type = d.GetType();
foreach (var p in typ
Solution
First, a trivial optimization: there is no reason for
On my computer, this makes
Another possibility is to use
The code:
dyn to be dynamic: all it does is to make the Target invocation dynamic, which here only makes it slower.On my computer, this makes
Get/Set about 25 % faster.Another possibility is to use
Expression instead of CallSite. Since Expression is not as dynamic as dynamic, it can compile to simpler code. For me, using this results in 50 % better speed than your code after applying the previous optimization.The code:
public class TypeWrapper
{
private readonly object dyn;
private readonly Dictionary> setters
= new Dictionary>();
private readonly Dictionary> getters
= new Dictionary>();
public TypeWrapper(object d)
{
this.dyn = d;
Type type = d.GetType();
foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
string name = p.Name;
var wrappedObjectParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
var setExpression = Expression.Lambda>(
Expression.Assign(
Expression.Property(
Expression.Convert(wrappedObjectParameter, type), p),
Expression.Convert(valueParameter, p.PropertyType)),
wrappedObjectParameter, valueParameter);
this.setters.Add(name, setExpression.Compile());
var getExpression = Expression.Lambda>(
Expression.Convert(
Expression.Property(
Expression.Convert(wrappedObjectParameter, type), p),
typeof(object)),
wrappedObjectParameter);
this.getters.Add(name, getExpression.Compile());
}
}
public void Set(string name, object value)
{
var set = this.setters[name];
set(this.dyn, value);
}
public object Get(string name)
{
var get = this.getters[name];
return get(this.dyn);
}
}Code Snippets
public class TypeWrapper
{
private readonly object dyn;
private readonly Dictionary<string, Action<object, object>> setters
= new Dictionary<string, Action<object, object>>();
private readonly Dictionary<string, Func<object, object>> getters
= new Dictionary<string, Func<object, object>>();
public TypeWrapper(object d)
{
this.dyn = d;
Type type = d.GetType();
foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
string name = p.Name;
var wrappedObjectParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
var setExpression = Expression.Lambda<Action<object, object>>(
Expression.Assign(
Expression.Property(
Expression.Convert(wrappedObjectParameter, type), p),
Expression.Convert(valueParameter, p.PropertyType)),
wrappedObjectParameter, valueParameter);
this.setters.Add(name, setExpression.Compile());
var getExpression = Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Property(
Expression.Convert(wrappedObjectParameter, type), p),
typeof(object)),
wrappedObjectParameter);
this.getters.Add(name, getExpression.Compile());
}
}
public void Set(string name, object value)
{
var set = this.setters[name];
set(this.dyn, value);
}
public object Get(string name)
{
var get = this.getters[name];
return get(this.dyn);
}
}Context
StackExchange Code Review Q#126819, answer score: 11
Revisions (0)
No revisions yet.