.net的反射技术(2)深究及 性能比较
FastReflection Library
基本使用方式
using System;
using System.Reflection;
using FastReflectionLib;
namespace SimpleConsole
{
class Program
{
static void Main(string[] args)
{
PropertyInfo propertyInfo = typeof(string).GetProperty("Length");
MethodInfo methodInfo = typeof(string).GetMethod("Contains");
string s = "Hello World!";
// get value by normal reflection
int length1 = (int)propertyInfo.GetValue(s, null);
// get value by the extension method from FastReflectionLib,
// which is much faster
int length2 = (int)propertyInfo.FastGetValue(s);
// invoke by normal reflection
bool result1 = (bool)methodInfo.Invoke(s, new object[] { "Hello" });
// invoke by the extension method from FastReflectionLib,
// which is much faster
bool result2 = (bool)methodInfo.FastInvoke(s, new object[] { "Hello" });
}
}
}
直接使用各工作对象
- PropertyInfo:IPropertyAccessor
- MethodInfo:IMethodInvoker
- ConstructorInfo:IConstructorInvoker
- FieldInfo:IFieldAccessor
我们可以使用FastReflectionCaches.MethodInvokerCache来获取一个IMethodInvoker对象:
static void Execute(MethodInfo methodInfo, object instance, int times)
{
IMethodInvoker invoker = FastReflectionCaches.MethodInvokerCache.Get(methodInfo);
object[] parameters = new object[0];
for (int i = 0; i < times; i++)
{
invoker.Invoke(instance, parameters);
}
}
工作对象的默认实现与扩展
public class BetterPropertyAccessor : IPropertyAccessor
{
public BetterPropertyAccessor(PropertyInfo propertyInfo) { ... }
...
}
public class BetterPropertyAccessorFactory :
IFastReflectionFactory<PropertyInfo, IPropertyAccessor>
{
public IPropertyAccessor Create(PropertyInfo key)
{
return new BetterPropertyAccessor(key);
}
}
class Program
{
static void Main(string[] args)
{
FastReflectionFactories.PropertyAccessorFactory =
new BetterPropertyAccessorFactory();
...
}
}
缓存的默认实现与扩展
public class BetterMethodInvokerCache :
IFastReflectionCache<MethodInfo, IMethodInvoker>
{
public IMethodInvoker Get(MethodInfo key) { ... }
}
class Program
{
static void Main(string[] args)
{
FastReflectionCaches.MethodInvokerCache =
new BetterMethodInvokerCache();
...
}
}
根据需要自行缓存工作对象
- 使用对象的类型和属性名同时作为缓存的Key获取对应的PropertyAccessor对象
- 使用PropertyAccessor获取“匿名对象”中的属性值
- 缓存的作用域为特定页面,而不是整个AppDomain。
性能测试
FastReflectionLib源码中包含了一个性能测试项目,您可以从中看出FastReflectionLib对于反射的性能改进。摘录部分数据如下(测试在我的笔记本上运行,Release编译)。
public class Test
{
public void MethodWithArgs(int a1, string a2) { }
}
调用方式 |
消耗时间(秒) |
方法直接调用 |
0.0071397 |
内置反射调用 |
1.4936181 |
工作对象调用 |
0.0468326 |
Fast方法调用 |
0.1373712 |
这下没理由嫌Eval的性能差了吧?
2009-01-08 18:32by 老赵, 13163 visits
好吧,你偏要说Eval性能差
protected void Page_Load(object sender, EventArgs e)
{
List<Comment> comments = GetComments();
this.rptComments.DataSource = comments;
this.rptComments.DataBind();
}
<asp:Repeater runat="server" ID="rptComments">
<ItemTemplate>
Title: <%# Eval("Title") %><br />
Conent: <%# Eval("Content") %>
</ItemTemplate>
<SeparatorTemplate>
<hr />
</SeparatorTemplate>
</asp:Repeater>
<asp:Repeater runat="server" ID="rptComments">
<ItemTemplate>
Title: <%# (Container.DataItem as Comment).Title %><br />
Conent: <%# (Container.DataItem as Comment).Content %>
</ItemTemplate>
<SeparatorTemplate>
<hr />
</SeparatorTemplate>
</asp:Repeater>
protected void Page_Load(object sender, EventArgs e)
{
List<Comment> comments = GetComments();
List<User> users = GetUsers();
this.rptComments.DataSource = from c in comments
from u in users
where c.UserID == u.UserID
order by c.CreateTime
select new
{
Title = c.Title,
Content = c.Content,
NickName = u.NickName
};
this.rptComments.DataBind();
}
不过我几乎可以肯定,又有人要叫了起来:“LINQ没有用!我们不用LINQ!Eval性能差!我们不用Eval!”。好吧,那么我免为其难地为他们用“最踏实”的技术重新实现一遍:
private Dictionary<int, User> m_users;
protected User GetUser(int userId)
{
return this.m_users[userId];
}
protected void Page_Load(object sender, EventArgs e)
{
List<Comment> comments = GetComments();
List<User> users = GetUsers();
this.m_users = new Dictionary<int, User>();
foreach (User u in users)
{
this.m_users[u.UserID] = u;
}
this.rptComments.DataSource = comments;
this.rptComments.DataBind();
}
<asp:Repeater runat="server" ID="rptComments">
<ItemTemplate>
Title: <%# (Container.DataItem as Comment).Title %><br />
Conent: <%# (Container.DataItem as Comment).Content %><br />
NickName: <%# this.GetUser((Container.DataItem as Comment).UserID).NickName %>
</ItemTemplate>
<SeparatorTemplate>
<hr />
</SeparatorTemplate>
</asp:Repeater>
嫌反射性能差?算有那么一点道理吧……
所以Eval不该使用?我不同意——怎能把孩子和脏水一起倒了?我们把反射访问属性的性能问题解决不就行了吗?
public class DynamicPropertyAccessor
{
private Func<object, object> m_getter;
public DynamicPropertyAccessor(Type type, string propertyName)
: this(type.GetProperty(propertyName))
{ }
public DynamicPropertyAccessor(PropertyInfo propertyInfo)
{
// target: (object)((({TargetType})instance).{Property})
// preparing parameter, object type
ParameterExpression instance = Expression.Parameter(
typeof(object), "instance");
// ({TargetType})instance
Expression instanceCast = Expression.Convert(
instance, propertyInfo.ReflectedType);
// (({TargetType})instance).{Property}
Expression propertyAccess = Expression.Property(
instanceCast, propertyInfo);
// (object)((({TargetType})instance).{Property})
UnaryExpression castPropertyValue = Expression.Convert(
propertyAccess, typeof(object));
// Lambda expression
Expression<Func<object, object>> lambda =
Expression.Lambda<Func<object, object>>(
castPropertyValue, instance);
this.m_getter = lambda.Compile();
}
public object GetValue(object o)
{
return this.m_getter(o);
}
}
这个方法是不是比较眼熟?没错,我在《方法的直接调用,反射调用与……Lambda表达式调用》一文中也使用了类似的做法。
测试一下性能?
我们来比对一下属性的直接获取值,反射获取值与……Lambda表达式获取值三种方式之间的性能。
var t = new Temp { Value = null };
PropertyInfo propertyInfo = t.GetType().GetProperty("Value");
Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (var i = 0; i < 1000000; i ++)
{
var value = propertyInfo.GetValue(t, null);
}
watch1.Stop();
Console.WriteLine("Reflection: " + watch1.Elapsed);
DynamicPropertyAccessor property = new DynamicPropertyAccessor(t.GetType(), "Value");
Stopwatch watch2 = new Stopwatch();
watch2.Start();
for (var i = 0; i < 1000000; i++)
{
var value = property.GetValue(t);
}
watch2.Stop();
Console.WriteLine("Lambda: " + watch2.Elapsed);
Stopwatch watch3 = new Stopwatch();
watch3.Start();
for (var i = 0; i < 1000000; i++)
{
var value = t.Value;
}
watch3.Stop();
Console.WriteLine("Direct: " + watch3.Elapsed);
Reflection: 00:00:04.2695397
Lambda: 00:00:00.0445277
Direct: 00:00:00.0175414
离快速Eval只有一步之遥了
public class DynamicPropertyAccessorCache
{
private object m_mutex = new object();
private Dictionary<Type, Dictionary<string, DynamicPropertyAccessor>> m_cache =
new Dictionary<Type, Dictionary<string, DynamicPropertyAccessor>>();
public DynamicPropertyAccessor GetAccessor(Type type, string propertyName)
{
DynamicPropertyAccessor accessor;
Dictionary<string, DynamicPropertyAccessor> typeCache;
if (this.m_cache.TryGetValue(type, out typeCache))
{
if (typeCache.TryGetValue(propertyName, out accessor))
{
return accessor;
}
}
lock (m_mutex)
{
if (!this.m_cache.ContainsKey(type))
{
this.m_cache[type] = new Dictionary<string, DynamicPropertyAccessor>();
}
accessor = new DynamicPropertyAccessor(type, propertyName);
this.m_cache[type][propertyName] = accessor;
return accessor;
}
}
}
经过测试之后发现,由于每次都要从缓存中获取DynamicPropertyAccessor对象,调用性能有所下降,但是依旧比反射调用要快几十上百倍。
FastEval——还有人会拒绝吗?
FastEval方法,如果在之前的.NET版本中,我们可以将其定义在每个页面的共同基类里。不过既然我们在用.NET 3.5,我们可以使用Extension Method这种没有任何侵入的方式来实现:
public static class FastEvalExtensions
{
private static DynamicPropertyAccessorCache s_cache =
new DynamicPropertyAccessorCache();
public static object FastEval(this Control control, object o, string propertyName)
{
return s_cache.GetAccessor(o.GetType(), propertyName).GetValue(o);
}
public static object FastEval(this TemplateControl control, string propertyName)
{
return control.FastEval(control.Page.GetDataItem(), propertyName);
}
}
其他
思考题解答
public class DynamicPropertyAccessor
{
...
private DynamicMethodExecutor m_dynamicSetter;
...
public DynamicPropertyAccessor(PropertyInfo propertyInfo)
{
...
MethodInfo setMethod = propertyInfo.GetSetMethod();
if (setMethod != null)
{
this.m_dynamicSetter = new DynamicMethodExecutor(setMethod);
}
}
...
public void SetValue(object o, object value)
{
if (this.m_dynamicSetter == null)
{
throw new NotSupportedException("Cannot set the property.");
}
this.m_dynamicSetter.Execute(o, new object[] { value });
}
}
在下面的评论中,SuchCloud已经想到了类似的做法,值得鼓励,同时多谢支持。
方法的直接调用,反射调用与……Lambda表达式调用
2008-11-24 01:59by 老赵, 27054 visits
class Program
{
static void Main(string[] args)
{
}
public void Call(object o1, object o2, object o3) { }
}
Call方法接受三个object参数却没有任何实现,这样我们就可以让测试专注于方法调用,而并非方法实现本身。于是我们开始编写测试代码,比较一下方法的直接调用与反射调用的性能差距:
static void Main(string[] args)
{
int times = 1000000;
Program program = new Program();
object[] parameters = new object[] { new object(), new object(), new object() };
program.Call(null, null, null); // force JIT-compile
Stopwatch watch1 = new Stopwatch();
watch1.Start();
for (int i = 0; i < times; i++)
{
program.Call(parameters[0], parameters[1], parameters[2]);
}
watch1.Stop();
Console.WriteLine(watch1.Elapsed + " (Directly invoke)");
MethodInfo methodInfo = typeof(Program).GetMethod("Call");
Stopwatch watch2 = new Stopwatch();
watch2.Start();
for (int i = 0; i < times; i++)
{
methodInfo.Invoke(program, parameters);
}
watch2.Stop();
Console.WriteLine(watch2.Elapsed + " (Reflection invoke)");
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
00:00:00.0119041 (Directly invoke)
00:00:04.5527141 (Reflection invoke)
Press any key to continue...
public Func<object, object[], object> GetVoidDelegate()
{
Expression<Action<object, object[]>> exp = (instance, parameters) =>
((Program)instance).Call(parameters[0], parameters[1], parameters[2]);
Action<object, object[]> action = exp.Compile();
return (instance, parameters) =>
{
action(instance, parameters);
return null;
};
}
public int Call(object o1, object o2) { return 0; }
public Func<object, object[], object> GetDelegate()
{
Expression<Func<object, object[], object>> exp = (instance, parameters) =>
((Program)instance).Call(parameters[0], parameters[1]);
return exp.Compile();
}
public class DynamicMethodExecutor
{
private Func<object, object[], object> m_execute;
public DynamicMethodExecutor(MethodInfo methodInfo)
{
this.m_execute = this.GetExecuteDelegate(methodInfo);
}
public object Execute(object instance, object[] parameters)
{
return this.m_execute(instance, parameters);
}
private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)
{
// parameters to execute
ParameterExpression instanceParameter =
Expression.Parameter(typeof(object), "instance");
ParameterExpression parametersParameter =
Expression.Parameter(typeof(object[]), "parameters");
// build parameter list
List<Expression> parameterExpressions = new List<Expression>();
ParameterInfo[] paramInfos = methodInfo.GetParameters();
for (int i = 0; i < paramInfos.Length; i++)
{
// (Ti)parameters[i]
BinaryExpression valueObj = Expression.ArrayIndex(
parametersParameter, Expression.Constant(i));
UnaryExpression valueCast = Expression.Convert(
valueObj, paramInfos[i].ParameterType);
parameterExpressions.Add(valueCast);
}
// non-instance for static method, or ((TInstance)instance)
Expression instanceCast = methodInfo.IsStatic ? null :
Expression.Convert(instanceParameter, methodInfo.ReflectedType);
// static invoke or ((TInstance)instance).Method
MethodCallExpression methodCall = Expression.Call(
instanceCast, methodInfo, parameterExpressions);
// ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
if (methodCall.Type == typeof(void))
{
Expression<Action<object, object[]>> lambda =
Expression.Lambda<Action<object, object[]>>(
methodCall, instanceParameter, parametersParameter);
Action<object, object[]> execute = lambda.Compile();
return (instance, parameters) =>
{
execute(instance, parameters);
return null;
};
}
else
{
UnaryExpression castMethodCall = Expression.Convert(
methodCall, typeof(object));
Expression<Func<object, object[], object>> lambda =
Expression.Lambda<Func<object, object[], object>>(
castMethodCall, instanceParameter, parametersParameter);
return lambda.Compile();
}
}
}
DynamicMethodExecutor executor = new DynamicMethodExecutor(methodInfo);
Stopwatch watch3 = new Stopwatch();
watch3.Start();
for (int i = 0; i < times; i++)
{
executor.Execute(program, parameters);
}
watch3.Stop();
Console.WriteLine(watch3.Elapsed + " (Dynamic executor)");
00:00:00.0125539 (Directly invoke)
00:00:04.5349626 (Reflection invoke)
00:00:00.0322555 (Dynamic executor)
Press any key to continue...
补充
不知道是否有对此感兴趣的朋友能够再做一个测试,不过请注意此类性能测试一定需要在Release编译下进行(这点很容易被忽视),否则意义其实不大。
附录:
A General Fast Method Invoker
By Luyan| 4 Jul 2006
Method reflecting invoke is nice, but veryfrequently it can be too slow. This article describes an alternative method fordynamic method invoke.
PrintArticle
Digg
Del.icio.us
Stumbleupon
Newsvine
Technorati
Mr. Wong
Yahoo!
Windows Live
Send as Email
Add to your CodeProject bookmarks
Discussthis article
66
Reportthis article as inappropriate
Article |
Browse Code |
Stats |
Revisions |
|||
|
4.84 (69 votes) |
|||||
1 |
2 |
3 |
4 |
5 |
Sponsored Links
See Also
· Articleslike this
· Articlesby this author
- Download source and performance test project - 4.04 Kb
Introduction
Sometimes, I run across the need to dynamically invoke themethod of an object, where the actual method might not be known until run-time.Usually, Reflecting is nice, but frequently doing it can be too slow. Thisarticle describes an alternative method for dynamic method invocation.
Background
When I read the article FastDynamic Property Accessors, I was thinking about my project, it has a lotsof reflecting methods in circle. But it's methods not properties. But theDynamicMethod
reminded me, maybe I could useEmit
to generate aDynamicMethod
to bind a special method before it can be invoked. I hope it will improveperformance.
Using the Code
First, I reflected out the method which will be invoked:
Collapse
MethodInfo methodInfo = typeof(Person).GetMethod("Say");
Then, I get the MethodInvoker
to invoke:
Collapse
FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo);
fastInvoker(new Person(), new object[]{"hello"});
Instead of using reflection method, invoke in the past:
Collapse
methodInfo.Invoke(new Person(), new object[]{"hello"});
Implementation
First, I need to define a delegate to adapt the dynamicmethod:
Collapse
public delegate object FastInvokeHandler(object target,
object[] paramters);
It looks the same as the class MethodInfo
's Invoke
method. Yes, that means I can write the same code to use it like in the past.
This code generates the DynamicMethod
:
Collapse
public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo)
{
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty,
typeof(object), new Type[] { typeof(object),
typeof(object[]) },
methodInfo.DeclaringType.Module);
ILGenerator il = dynamicMethod.GetILGenerator();
ParameterInfo[] ps = methodInfo.GetParameters();
Type[] paramTypes = new Type[ps.Length];
for (int i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = ps[i].ParameterType;
}
LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];
for (int i = 0; i < paramTypes.Length; i++)
{
locals[i] = il.DeclareLocal(paramTypes[i]);
}
for (int i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg_1);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
EmitCastToReference(il, paramTypes[i]);
il.Emit(OpCodes.Stloc, locals[i]);
}
il.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldloc, locals[i]);
}
il.EmitCall(OpCodes.Call, methodInfo, null);
if (methodInfo.ReturnType == typeof(void))
il.Emit(OpCodes.Ldnull);
else
EmitBoxIfNeeded(il, methodInfo.ReturnType);
il.Emit(OpCodes.Ret);
FastInvokeHandler invoder =
(FastInvokeHandler)dynamicMethod.CreateDelegate(
typeof(FastInvokeHandler));
return invoder;
}
Conclusion
Well, I think this is a general way that can be usedinstead of most of the reflection methods to get about 50 times performanceimprovement. Any suggestions for improvements are welcome.
Extra advantage (reminded by MaxGuernsey): If an exceptionoccurs in your code,FastInovker
would throw the original one, buttheMethod.Invoke
would throw aTargetInvocationException
.
History
- 2006-7-05: Updated to add static method support. Thanks Manuel Abadia.
- 2006-6-30: Updated to add ref/out parameter support. Thanks Roger for his nice suggestion.
License
This article has no explicit license attached to it but maycontain usage terms in the article text or the download files themselves. If indoubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here
About the Author
Luyan China Member |
I am currently working for a .NET framework names AgileFramework. It's introduction at here: Now I'm living in China. I have been designing and developing .NET based software applications for 5+ years. |
Attribute操作的性能优化方式
2009-11-18 02:09by 老赵, 16720 visits
Attribute是.NET平台上提供的一种元编程能力,可以通过标记的方式来修饰各种成员。无论是组件设计,语言之间互通,还是最普通的框架使用,现在已经都离不开Attribute了。迫于Attribute的功能的重要性(Kent Beck认为NUnit比早期JUnit设计的好,一个主要方面便是利用了Attribute),Java语言也在5.0版本中引入了与 Attribute类似的Annotation概念。不过Attribute说到底也是一种反射操作,平时正常使用不会带来问题,但是密集的调用还是对性 能有一定影响的。这次我们就来总结看看我们究竟可以如何回避Attribute操作的一些性能问题。
假设我们有一个Attribute,它定义在一个类型上:
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = true,
Inherited = true)]
public class TestAttribute : Attribute
{
public TestAttribute(string prop)
{
this.Prop = prop;
}
public TestAttribute() { }
public string Prop { get; set; }
}
[Test("Hello World")]
[Test(Prop = "Hello World")]
public class SomeClass { }
那么,如果我们需要获得SomeClass类型上所标记的TestAttribute,我们一般会使用Type对象的GetCustomAttributes方法。那么在其中又发生了什么呢?
通过.NET Reflector来追踪其中实现,会发现这些逻辑最终是由CustomAttribute的GetCustomAttributes方法完成的,感兴趣 的朋友们可以找到那个最复杂的重载。由于实现有些复杂,我没有看懂完整的逻辑,但从关键的代码上可以看出,它其实是使用了Activator.CreateInstance方法创建对象,并且使用反射对Attribute对象的属性进行设置。于是我便打算了解一下这些反射操作占整个GetCustomAttributes方法的多大比重:
CodeTimer.Time("GetCustomAttributes", 1000 * 100, () =>
{
var attributes = typeof(SomeClass).GetCustomAttributes(typeof(TestAttribute), true);
});
CodeTimer.Time("Reflection", 1000 * 100, () =>
{
var a1 = (TestAttribute)Activator.CreateInstance(typeof(TestAttribute), "Hello World");
var a2 = (TestAttribute)Activator.CreateInstance(typeof(TestAttribute));
typeof(TestAttribute).GetProperty("Prop").SetValue(a2, "Hello World", null);
});
结果如下:
GetCustomAttributes
Time Elapsed: 2,091ms
CPU Cycles: 5,032,765,488
Gen 0: 43
Gen 1: 0
Gen 2: 0
Reflection
Time Elapsed: 527ms
CPU Cycles: 1,269,399,624
Gen 0: 40
Gen 1: 0
Gen 2: 0
可以看出,虽然GetCustomAttributes方法中使用了反射进行对象的创建和属性设置,但是它的大部分开销还是用于获取一些元数据的,它们占据了3/4的时间,而反射的开销其实只占了1/4左右。这就有些令人奇怪了,既然是静态的元数据,为什么.NET Framework不对这些数据进行缓存,而是每次再去取一次呢?即便是我们不应该缓存最后得到的Attribute对象,但是用于构造对象的“信息”是完全可以缓存下来的。
事实上,经由上次heros同学指出,.NET Framework事实上已经给出了足够的信息,那便是CustomAttributeData的 GetCustomAttributes方法,它返回的是IList<CustomAttributeData>对象,其中包含了构造 Attribute所需要的全部信息。换句话说,我完全可以根据一个CustomAttributeData来“快速构建”Attribute对象:
public class AttributeFactory
{
public AttributeFactory(CustomAttributeData data)
{
this.Data = data;
var ctorInvoker = new ConstructorInvoker(data.Constructor);
var ctorArgs = data.ConstructorArguments.Select(a => a.Value).ToArray();
this.m_attributeCreator = () => ctorInvoker.Invoke(ctorArgs);
this.m_propertySetters = new List<Action<object>>();
foreach (var arg in data.NamedArguments)
{
var property = (PropertyInfo)arg.MemberInfo;
var propertyAccessor = new PropertyAccessor(property);
var value = arg.TypedValue.Value;
this.m_propertySetters.Add(o => propertyAccessor.SetValue(o, value));
}
}
public CustomAttributeData Data { get; private set; }
private Func<object> m_attributeCreator;
private List<Action<object>> m_propertySetters;
public Attribute Create()
{
var attribute = this.m_attributeCreator();
foreach (var setter in this.m_propertySetters)
{
setter(attribute);
}
return (Attribute)attribute;
}
}
AttributeFactory利用了FastReflectionLib,将ConstructorInfo和PropertyInfo封装成性能很高的ConstructorInvoker和PropertyAccessor对象,这样使用起来便有数量级的性能提高。我们再来进行一番测试:
var factories = CustomAttributeData.GetCustomAttributes(typeof(SomeClass))
.Where(d => d.Constructor.DeclaringType == typeof(TestAttribute))
.Select(d => new AttributeFactory(d)).ToList();
CodeTimer.Time("GetCustomAttributes", 1000 * 100, () =>
{
var attributes = typeof(SomeClass).GetCustomAttributes(typeof(TestAttribute), true);
});
CodeTimer.Time("AttributeFactory", 1000 * 100, () => factories.ForEach(f => f.Create()));
结果如下:
GetCustomAttributes
Time Elapsed: 2,131ms
CPU Cycles: 5,136,848,904
Gen 0: 43
Gen 1: 43
Gen 2: 0
Attribute Factory
Time Elapsed: 18ms
CPU Cycles: 44,235,564
Gen 0: 4
Gen 1: 4
Gen 2: 0
在这里,我们先获得SomeClass中所有定义过的CustomAttributeData对象,然后根据其Constructor的类型来判断 哪些是用于构造TestAttribute对象的,然后用它们来构造AttributeFactory。在实际使用过程 中,AttributeFactory实例可以缓存下来,并反复使用。这样的话,我们即可以每次得到新的Attribute对象,又可以避免 GetCustomAttributes方法所带来的莫名其妙的开销。
事实上,我们完全可以利用这个方法,来实现一个性能更高的GetCustomAttributesEx方法,它的行为可以和.NET自带的 GetCustomAttributes完全一致,但是性能可以快上无数——可能是100倍。不过,这个方法虽然不难编写,但比较麻烦。因为 CustomAttributeData只能用于获得“直接定义”在某个成员上的数据,而实际情况是,我们往往还必须根据某个Attribute上标记的 AttributeUsage的AllowMultiple和Inherited属性来决定是否要遍历整个继承链。只有这般,我们才能百分之百地重现GetCustomAttribute方法的行为。
不过我们在这里有个优势,那便是“静态”。一旦“静态”,我们便可以为某个特定的场景,用“肉眼”判断出特定的处理方式,这样便不需要一个非常通用 的GetCustomAttributeEx方法了。例如在实际使用过程中,我们可以可以发现某个Attribute的Inherited属性为 false,那么我们便可以免去遍历继承链的麻烦。
最后还有两点可能值得一提:
除了Type,Assembly等成员自带的GetCustomAttributes方法之外,Attribute类也有些静态 GetCustomAttributes方法可用于获取Attribute对象。但是,通过.NET Reflector,我们可以发现,Attribute类中的静态方法,最终还是委托给各自的实例方法,因此不会有性能提高。唯一区别对待的是 ParameterInfo——不过我没搞懂为什么那么复杂,感兴趣的朋友可以自行探索一番。
如果仅仅是判断一个成员是否定义了某个特定类型的Attribute对象,那么可以使用Attribute.IsDefined静态方法。它的性能 比GetCustomAttributes后再判断数组的Length要高效许多倍。不过个人认为这点倒并不是非常重要,因为这原本就是个静态的信息,即 便是我们使用较慢的GetCustomAttributes方法来进行判断,也可以把最终的true或false结果进行缓存,这自然也不会有性能问题 了。
我们之所以要反复调用GetCustomAttributes方法,就是因为每次得到的Attribute对象都是新建的,因此在某些场景下可能无法缓存它们。不过现在已经有了现在更快的做法,在这方面自然也就不会有太大问题了。
- Categories: .Net框架,实践优化
- Tags: 性能,FastReflectionLib,自定义属性
一个简单的性能计数器:CodeTimer
2009-03-10 01:03by 老赵, 28427 visits
有数据,有真相,相信大家在平时的工作或学习过程中,都需要比较几种不同方法或实现之间的性能差距。在这些时候,往往就需要我们不 断地创建Stopwatch,打开,关闭,然后打印时间。这种一遍又一遍的重复终有一天会让人忍无可忍,因此如果能有一个“标准”的性能计数器,那应该可 以让生活轻松许多。这个性能计数器不用复杂,够用就好;也不需要考虑扩展性,要扩展时直接修改代码就够了;同样不需要考虑输出格式,直接打印在 Console就行。
在上次的.NET技术大会中,Jeffrey Richter大叔在Keynote Session中进行了一个名为“The Performance ofEveryday Things”的主题演讲,展示了各种常用编程元素之间的性能对比。在演示中他使用了一个名为CodeTimer的简单计数器,用于统计每种做法的性能。可惜翻遍了每个地方都没发现JR大叔在哪里公开了这个计数器的实现。算了,那么就凭着印象写一个出来吧,反正也不复杂。
总的来说,CodeTimer有两个公开方法,一个是Initialize,一个是Time:
public static class CodeTimer
{
public static void Initialize()
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Time("", 1, () => { });
}
public static void Time(string name, int iteration, Action action)
{
...
}
}
CodeTimer.Initialize方法应该在测试开始前调用。首先它会把当前进程及当前线程的优先级设为最高,这样便可以相对减少操作系统在调度上造成的干扰。然后调用一次Time方法进行“预热”,让JIT将IL编译成本地代码,让Time方法尽快“进入状态”。Time方法则是真正用于 性能计数的方法,实现如下:
public static void Time(string name, int iteration, Action action)
{
if (String.IsNullOrEmpty(name)) return;
// 1.
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(name);
// 2.
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
int[] gcCounts = new int[GC.MaxGeneration + 1];
for (int i = 0; i <= GC.MaxGeneration; i++)
{
gcCounts[i] = GC.CollectionCount(i);
}
// 3.
Stopwatch watch = new Stopwatch();
watch.Start();
ulong cycleCount = GetCycleCount();
for (int i = 0; i < iteration; i++) action();
ulong cpuCycles = GetCycleCount() - cycleCount;
watch.Stop();
// 4.
Console.ForegroundColor = currentForeColor;
Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
Console.WriteLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));
// 5.
for (int i = 0; i <= GC.MaxGeneration; i++)
{
int count = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine("\tGen " + i + ": \t\t" + count);
}
Console.WriteLine();
}
private static ulong GetCycleCount()
{
ulong cycleCount = 0;
QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
return cycleCount;
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
Time方法接受三个参数,名称,循环次数以及需要执行的方法体。打印出花费时间,消耗的CPU时钟周期,以及各代垃圾收集的回收次数。具体实现分几个步骤,如下:
- 保留当前控制台前景色,并使用黄色输出名称参数。
- 强制GC进行收集,并记录目前各代已经收集的次数。
- 执行代码,记录下消耗的时间及CPU时钟周期1。
- 恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期。
- 打印执行过程中各代垃圾收集回收次数。
与传统计数方法相比,这段代码还输出了更多信息:CPU时钟周期及各代垃圾收集回收次数。CPU时钟周期是性能计数中的辅助参考,说明CPU分配了 多少时间片给这段方法来执行,它和消耗时间并没有必然联系。例如Thread.Sleep方法会让CPU暂时停止对当前线程的“供给”,这样虽然消耗了时 间,但是节省了CPU时钟周期:
CodeTimer.Time("Thread Sleep", 1, () => { Thread.Sleep(3000); });
CodeTimer.Time("Empty Method", 10000000, () => { });
结果如下:
而垃圾收集次数的统计,即直观地反应了方法资源分配(消耗)的规模:
int iteration = 100 * 1000;
string s = "";
CodeTimer.Time("String Concat", iteration, () => { s += "a"; });
StringBuilder sb = new StringBuilder();
CodeTimer.Time("StringBuilder", iteration, () => { sb.Append("a"); });
结果如下:
老赵最近在研究一个问题的几种不同做法在性能上的优劣,其中CodeTimer起到了很重要的作用——这边也先卖个关子,接下来老赵也将会写几篇文章来讲解这个问题。
注1:统计CPU时钟周期时使用P/Invoke访问QueryThreadCycleTime函数,这是Vista和Server 2008中新的函数。感谢装配脑袋在这里提供的帮助。
注2:对于.NET 2.0及Vista以下操作系统,请参考《对老赵写的简单性能计数器的修改》
- Categories: 实践优化,.Net框架
- Tags: 性能
· 对老赵写的简单性能计数器的修改
· 早上看到老赵写的这个性能计数器,感觉很实用,不过老赵用了很多.C# 3.0 的新语法,还用了 VISTA 和 Server 2008 下特有的Win32 API,对于还在用C#2.0 或者还工作在 XP 或者Server 2003 下的兄弟们,只能望代码心叹了。应老赵的要求,我修改了他的代码,增加了对低版本C# 和低版本windows 操作系统的支持。
· 老赵的原文:一个简单的性能计数器:CodeTimer
· 修改说明
· 1. 采用接口 取代了原代码中的 Lambda 表达式
· 2. 采用 GetThreadTimes 这个API 函数替代了原代码中的 QueryThreadCycleTime
· 这里需要说明的是 GetThreadTimes 给出了线程在内核态和用户态占用的时间,单位是 100ns。两个时间的总和就是线程占用的CPU时间。这个API的时间精度我看了一些资料似乎没有达到 100ns. 所以GetThreadTimes 这个API函数的进度没有 QueryThreadCycleTime 高。
· 下面是我修改后的代码
· 注释1: 2009-03-11 增加委托的调用,修改 GC.Collect 参数,兼容.Net 2.0. 增加每次调用时间统计
· 增加了委托调用后,我发现同样是测试空函数,采用接口比采用委托效率要略高一些,这和我的预计基本吻合,因为委托不是单纯的函数调用,具体原理超出本文范围,我就不多说了。
·
·
· using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
public interface IAction
{
void Action();
}
public static class CodeTimer
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime,
out long lpExitTime, out long lpKernelTime, out long lpUserTime);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
public delegate void ActionDelegate();
private static long GetCurrentThreadTimes()
{
long l;
long kernelTime, userTimer;
GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime,
out userTimer);
return kernelTime + userTimer;
}
static CodeTimer()
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
}
public static void Time(string name, int iteration, ActionDelegate action)
{
if (String.IsNullOrEmpty(name))
{
return;
}
if (action == null)
{
return;
}
//1. Print name
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(name);
// 2. Record the latest GC counts
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.Collect(GC.MaxGeneration);
int[] gcCounts = new int[GC.MaxGeneration + 1];
for (int i = 0; i <= GC.MaxGeneration; i++)
{
gcCounts[i] = GC.CollectionCount(i);
}
// 3. Run action
Stopwatch watch = new Stopwatch();
watch.Start();
long ticksFst = GetCurrentThreadTimes(); //100 nanosecond one tick
for (int i = 0; i < iteration; i++) action();
long ticks = GetCurrentThreadTimes() - ticksFst;
watch.Stop();
// 4. Print CPU
Console.ForegroundColor = currentForeColor;
Console.WriteLine("\tTime Elapsed:\t\t" +
watch.ElapsedMilliseconds.ToString("N0") + "ms");
Console.WriteLine("\tTime Elapsed (one time):" +
(watch.ElapsedMilliseconds / iteration).ToString("N0") + "ms");
Console.WriteLine("\tCPU time:\t\t" + (ticks * 100).ToString("N0")
+ "ns");
Console.WriteLine("\tCPU time (one time):\t" + (ticks * 100 /
iteration).ToString("N0") + "ns");
// 5. Print GC
for (int i = 0; i <= GC.MaxGeneration; i++)
{
int count = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine("\tGen " + i + ": \t\t\t" + count);
}
Console.WriteLine();
}
public static void Time(string name, int iteration, IAction action)
{
if (String.IsNullOrEmpty(name))
{
return;
}
if (action == null)
{
return;
}
//1. Print name
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(name);
// 2. Record the latest GC counts
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.Collect(GC.MaxGeneration);
int[] gcCounts = new int[GC.MaxGeneration + 1];
for (int i = 0; i <= GC.MaxGeneration; i++)
{
gcCounts[i] = GC.CollectionCount(i);
}
// 3. Run action
Stopwatch watch = new Stopwatch();
watch.Start();
long ticksFst = GetCurrentThreadTimes(); //100 nanosecond one tick
for (int i = 0; i < iteration; i++) action.Action();
long ticks = GetCurrentThreadTimes() - ticksFst;
watch.Stop();
// 4. Print CPU
Console.ForegroundColor = currentForeColor;
Console.WriteLine("\tTime Elapsed:\t\t" +
watch.ElapsedMilliseconds.ToString("N0") + "ms");
Console.WriteLine("\tTime Elapsed (one time):" +
(watch.ElapsedMilliseconds / iteration).ToString("N0") + "ms");
Console.WriteLine("\tCPU time:\t\t" + (ticks * 100).ToString("N0")
+ "ns");
Console.WriteLine("\tCPU time (one time):\t" + (ticks * 100 /
iteration).ToString("N0") + "ns");
// 5. Print GC
for (int i = 0; i <= GC.MaxGeneration; i++)
{
int count = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine("\tGen " + i + ": \t\t\t" + count);
}
Console.WriteLine();
}
}
·
· 测试类
·
·
· public class TestSleep3000 : IAction
{
#region IAction Members
public void Action()
{
Thread.Sleep(3000);
}
#endregion
}
public class TestEmptyMethod : IAction
{
#region IAction Members
public void Action()
{
}
#endregion
}
public class TestStringConcat : IAction
{
string s = "";
#region IAction Members
public void Action()
{
s += "a";
}
#endregion
}
public class TestStringBuilderConcat : IAction
{
StringBuilder s = new StringBuilder();
#region IAction Members
public void Action()
{
s.Append ("a");
}
#endregion
}
·
· 测试代码
· 采用接口
· CodeTimer.Time("Thread Sleep", 1, new TestSleep3000());
CodeTimer.Time("Thread Sleep", 10000000, new TestEmptyMethod());
CodeTimer.Time("String Concat", 100000, new TestStringConcat());
CodeTimer.Time("StringBuilder Conca", 100000,
new TestStringBuilderConcat());
·
· 测试结果
·
·
· Thread Sleep
TimeElapsed: 2,997ms
Time Elapsed (one time):2,997ms
CPUtime: 0ns
CPU time (onetime): 0ns
Gen0: 0
Gen 1: 0
Gen2: 0
· Empty Method
TimeElapsed: 138ms
Time Elapsed (one time):0ms
CPUtime: 125,000,000ns
CPU time (onetime): 12ns
Gen0: 0
Gen1: 0
Gen2: 0
· String Concat
TimeElapsed: 10,547ms
Time Elapsed (one time):0ms
CPUtime: 10,546,875,000ns
CPU time (onetime): 105,468ns
Gen0: 4102
Gen1: 2661
Gen2: 2545
· StringBuilder Conca
TimeElapsed: 4ms
Time Elapsed (one time):0ms
CPUtime: 0ns
CPU time (one time): 0ns
Gen0: 0
Gen1: 0
Gen2: 0
·
· 采用委托
·
· CodeTimer.Time("Thread Sleep", 1, delegate() { Thread.Sleep(3000); });
CodeTimer.Time("Empty Method", 10000000, delegate() { });
string a = "";
CodeTimer.Time("String Concat", 100000, delegate() { a += "a"; });
StringBuilder s = new StringBuilder();
CodeTimer.Time("StringBuilder Conca", 100000, delegate() { s.Append("a"); });
·
· 测试结果
·
· Thread Sleep
TimeElapsed: 2,989ms
Time Elapsed (one time):2,989ms
CPUtime: 0ns
CPU time (onetime): 0ns
Gen0: 0
Gen1: 0
Gen2: 0
· Empty Method
TimeElapsed: 156ms
Time Elapsed (one time):0ms
CPUtime: 156,250,000ns
CPU time (onetime): 15ns
Gen0: 0
Gen1: 0
Gen2: 0
· String Concat
TimeElapsed: 10,425ms
Time Elapsed (one time):0ms
CPUtime: 10,406,250,000ns
CPU time (onetime): 104,062ns
Gen0: 4102
Gen1: 2661
Gen2: 2545
· StringBuilder Conca
TimeElapsed: 4ms
Time Elapsed (one time):0ms
CPUtime: 0ns
CPU time (onetime): 0ns
Gen0: 0
Gen 1: 0
Gen2: 0
体面地处理程序的未捕获异常
经常有客户抱怨程序遇到错误的时候程序就退出了,甚至来不及保存窗体的输入数据,如果是普通的搜索界面也就罢了,但如果客户是呼叫中心,问题就严重了.按道理,我们的程序员应该处理所有的异常,但有事实上做到的很难,因为出乎意料的情况太多了.
在这篇文章,我给大家介绍一下我的处理方法。
首先介绍相应的C#相关知识:
System.Windows.Forms.Application类
提供 static 方法和属性以管理应用程序,例如启动和停止应用程序、处理 Windows 消息的方法和获取应用程序信息的属性。
System.Windows.Forms.Application.ThreadException 事件
在发生未捕获线程异常时发生。
System.Windows.Forms.Application.SetUnhandledExceptionMode()方法
指示应用程序如何响应未处理的异常。
· SetUnhandledExceptionMode(UnhandledExceptionMode)
指示应用程序如何响应未处理的异常。
· SetUnhandledExceptionMode(UnhandledExceptionMode,Boolean)
指示应用程序如何响应未处理的异常,同时可选择应用特定于线程的行为。
System.Windows.Forms.UnhandledExceptionMode枚举
定义 Windows 窗体应用程序应在何处发送未处理的异常。
public enumUnhandledExceptionMode
{
Automatic, //将所有异常都传送到 ThreadException 处理程序,除非应用程序的配置文件指定了其他位置。
ThrowException, //从不将异常传送到 ThreadException 处理程序。忽略应用程序配置文件。
CatchException //始终将异常传送到 ThreadException 处理程序。忽略应用程序配置文件。
}
以下是我的实现示例:
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
//应用程序的主入口点添加ThreadException的事件处理。
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
}
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
//作为示例,这里用消息框显示异常的信息
MessageBox.Show(e.Exception.Message,"异常",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
Tag标签: .NET,C#,Code,CSharp,DotNet,代码
.NET4.0 之 Dynamic VS Reflection 效率
在我先前的文章中,不断的推广.NET4.0新特性。特别是.NET4.0 Dynamic 这个新特性。随之而来的问题也出现了—Dynamic 执行效率如何?
我们做开发的不光需要代码简洁,能够希望自己能够写出好的架构。还有一点非常重要的就是,我们的写出来的代码效率。撇开Dynamic在.net4.0中的实现原理。本篇只考虑dynamic 的效率比起使用反射到底是快还是慢?难道.NET为引入了dynamic这个新鲜事物降低了我们的程序效率?有网友指出评论一个特性或者方法的效率如何,唯一的判定标准就是实测。
切入主题,按照以下代码,来结束你对dynamic的效率怀疑吧!!!
1、新建测试类:
view source
print?
|
|
|
|
|
|
|
|
|
|
2、控制台程序进行效率测试代码:
view source
print?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3、测试TestClass类,开始 dynamic VS reflection 测试结果如下:
怎样,dynamic 比起reflection 够快吧?!
最后希望本篇文章可以给您带来帮助,如有不足之处欢迎指出,谢谢!
作者:RyanDing
出处:http://www.cnblogs.com/ryanding/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如有疑问,可以通过 ryan.d@qq.com 联系作者本人。
发表评论
1988263
回复 引用 查看
#1楼2010-12-14 13:51 |Jeffrey Zhao
#3楼[楼主]2010-12-1413:55 |ryanding
引用Jeffrey Zhao:关键是“为什么”
内部实现,我稍候再去研究。先把效率提上。排除大家对dynamic效率的担心。
#4楼2010-12-14 14:02 |toEverybody
#5楼2010-12-14 14:03 |韦恩卑鄙v-zhewg @waynebaby
#6楼2010-12-14 14:03 |Jeffrey Zhao
@ryanding
严格来说一个实验是不够的,有没有问题要结合实际场景来,因为“担心”而不去用的人,其实是盲目的……所以还是要谈为什么……
#7楼[楼主]2010-12-1414:04 |ryanding
引用toEverybody:。NET上有什么效率可谈的呢、?
你这样说,还真的没法回答了。
#8楼[楼主]2010-12-1414:04 |ryanding
引用韦恩卑鄙 v-zhewg@waynebaby:你用的也不是最优的反射呀
#9楼[楼主]2010-12-1414:05 |ryanding
引用Jeffrey Zhao:
@ryanding
严格来说一个实验是不够的,有没有问题要结合实际场景来,因为“担心”而不去用的人,其实是盲目的……所以还是要谈为什么……
老赵就是老赵,心悦诚服!
#10楼2010-12-14 14:10 |Aloner [Sofire]
dynamic 第一次用的比较久,其后效率不错。其原理,我个人猜测委托。
效率上:
1、Delegate.CreateDelegate
2、Emit(其最终也是生成委托)
第一个是最恐怖的,那效率啊……竟然丝毫不差……可惜是强类型。所以才有 Emit。
#11楼2010-12-14 14:13 |韦恩卑鄙v-zhewg @waynebaby
我记得dynamic 的原理是类似的,
所以我按照原理直接做理当比dynamic封装的逻辑快,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#12楼2010-12-14 14:15 |温景良(Jason)
#13楼2010-12-14 14:16 |Jeffrey Zhao
@温景良(Jason)
dynamic根据CallSite缓存Emit出来的结果咯。
#14楼2010-12-14 14:20 |韦恩卑鄙v-zhewg @waynebaby
仔细看看还是做了缓存的
if (<Main>o__SiteContainer0.<>p__Site1== null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
||
|
||
|
|
|
|
||
|
|
||
|
|
|
|
|
|
|
#15楼2010-12-14 14:21 |韦恩卑鄙v-zhewg @waynebaby
引用Aloner [Sofire]:
dynamic 第一次用的比较久,其后效率不错。其原理,我个人猜测委托。
效率上:
1、Delegate.CreateDelegate
2、Emit(其最终也是生成委托)
第一个是最恐怖的,那效率啊……竟然丝毫不差……可惜是强类型。所以才有 Emit。
强类型也不打紧的你看我上面的例子
我还以为dynamic是编译器的语法糖,编译的时候自动寻找IL了。
#18楼[楼主]2010-12-1414:31 |ryanding
人多真的力量大。感谢 JeffreyZhao 、韦恩卑鄙 v-zhewg@waynebaby、Aloner [Sofire] 。
#19楼2010-12-14 14:34 |韦恩卑鄙v-zhewg @waynebaby
#20楼2010-12-14 14:35 |韦恩卑鄙v-zhewg @waynebaby
@ryanding
vb .net的延迟绑定基本上也是类似的代码形式做的语法糖 人家可是从.net 1.0就支持了。不过它1没用emit 2没用缓存 慢到飞起
#21楼[楼主]2010-12-1414:41 |ryanding
@韦恩卑鄙 v-zhewg@waynebaby
嗯,经过你们的大力支持。我对dynamic的内部实现也有了一个更深入的认识,很不错~
#22楼[楼主]2010-12-1414:42 |ryanding
@韦恩卑鄙 v-zhewg@waynebaby
特别是你提出来的那段把反射做成代理缓存起来的测试代码。非常有力道。呵呵
#23楼2010-12-14 14:54 |zhaohua_wang
引用韦恩卑鄙 v-zhewg@waynebaby:
把反射做成代理缓存起来效果吓人一跳。
我记得dynamic 的原理是类似的,
所以我按照原理直接做理当比dynamic封装的逻辑快,
[code=csharp]
//reflection 测试开始
TestClass testTypeByReflection = new TestClass();
Stopwatch watch1 = Stopwatch.StartNew();
var property = typeof(TestClass).GetPr...
#24楼2010-12-14 14:56 |韦恩卑鄙v-zhewg @waynebaby
修改了下你的
又做了一个测试如果用dictionary 作为代理的缓存delegate实现的速度反降了
|
|
|
|
|
|
||
|
|
|
|
|
|
||
|
|
|
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
所以在没有超越dictionary的代理缓存实现前 一般访问器还是dynamic快,因为人家的callsite用的是指针引用,不用算hash什么的。
如果是在某个地方对一两个字段做最快的操作,可以考虑就地建立delegate缓存,用引用保存起来。
#25楼2010-12-14 14:59 |韦恩卑鄙v-zhewg @waynebaby
希望我的测试能对你有所启发,最好能验证下我的测试,把咱们的实践结果总结到你的主文中去,这样方便了伸手党,咱也没白忙活
#26楼[楼主]2010-12-1415:02 |ryanding
#27楼2010-12-14 15:05 |韦恩卑鄙v-zhewg @waynebaby
@ryanding
小提醒 stopwatch 要在循环体前面开始 初始化工作不应该纳入计时呵呵
#28楼[楼主]2010-12-1415:07 |ryanding
引用韦恩卑鄙 v-zhewg@waynebaby:
@ryanding
小提醒 stopwatch 要在循环体前面开始 初始化工作不应该纳入计时呵呵
@韦恩卑鄙 v-zhewg@waynebaby
哇塞。。反過來整整快了一個數量級
#30楼[楼主]2010-12-1415:34 |ryanding
@SeaSunK
所以不存在快与慢,主要看到底是如何去使用它!
呵呵, 不错的好文,提出了4.0中新引入的动态类型带来的惊喜,所以经过本文证明,新东西好不好用,只有自己试过了才知道!支持!
#32楼2010-12-14 16:01 |allentranks
#33楼[楼主]2010-12-1416:03 |ryanding
#34楼[楼主]2010-12-1416:03 |ryanding
引用allentranks:
Good, 好贴~
缓存的力量不可小觑啊~
同感!
动态类型在解析属性的时候先查引用韦恩卑鄙 v-zhewg@waynebaby:
修改了下你的
又做了一个测试如果用dictionary 作为代理的缓存delegate实现的速度反降了
[code=csharp]
static void Main(string[] args)
{
string value = "Dynamic VS Reflection";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
从这里我们可以看出其实Dynamic也是建立了一个Delegate
#37楼2010-12-14 16:34 |Ivony...
#40楼2010-12-14 16:45 |Ivony...
sorry看错了我看的是CallSite。 是lambda表达使用的。Dynamic使用的是CallSite<>
其中的Cache使用的是Dictionary<Type,object>
#42楼2010-12-14 17:01 |韦恩卑鄙v-zhewg @waynebaby
@愚溪
這個緩存初始化後就不調用了,你看我上面反編結果中的注釋。
初始化以後都是調用callsite。target。invoke的開銷。
#43楼2010-12-14 17:06 |Code Man
#44楼2010-12-14 17:14 |Kevin.Jee
Dynamic类型用着很方面,关键在于它内部会有一个缓存机制,这样的话,第二次使用同一个类型的Dynamic对象就不会有开销。
引用toEverybody:。NET上有什么效率可谈的呢、?
非常好奇,为什么就没有效率可谈
一般常用的反射的地方是什么?
两种Case
一种是访问非public的Member
一种是访问不能直接访问的对象的Member。
很明显Dynamic的目的是代替后者的作用。
#50楼2010-12-14 20:07 |Jeffrey Zhao
#51楼2010-12-14 20:09 |Jeffrey Zhao
@韦恩卑鄙 v-zhewg@waynebaby
是在损我还是真的在问啊。。。
我说的IL是静态的,以为dynamic在编译器级别就直接被翻译了对应的IL了。emit是程序运行时动态的。
#54楼2010-12-15 08:11 |yongfa365
#55楼2010-12-15 08:19 |Aloner [Sofire]
友情提醒:普通测试中,使用dict.TryGetValue或等的集合操作,是影响结果的重要因素之一。所以,封装的时候要谨慎了。
该测试不够严谨。
看看我以前做的简陋测试。
http://www.cnblogs.com/downmoon/archive/2008/09/01/1281118.html
#57楼[楼主]2010-12-1508:32 |ryanding
#58楼[楼主]2010-12-1508:33 |ryanding
引用Aloner [Sofire]:友情提醒:普通测试中,使用dict.TryGetValue或等的集合操作,是影响结果的重要因素之一。所以,封装的时候要谨慎了。
原因是什么?这个口吻是学老赵的。
#59楼[楼主]2010-12-1508:36 |ryanding
#60楼2010-12-15 08:38 |Aloner [Sofire]
@ryanding
~o(╯□╰)o~
无法和老赵相比的~~
ORM中,一般是要对属性进行取值赋值。
这个 getter 或 setter 通常又是极为简单的
public string Username {get;set;}
#61楼[楼主]2010-12-1508:52 |ryanding
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在下才疏学浅,还是不太明白为何加入dictionary后速度反降了?dictionary内部在算hash么?赐教一下吧?谢谢
#62楼2010-12-15 08:56 |yongfa365
#63楼2010-12-15 08:58 |yongfa365
#64楼[楼主]2010-12-1509:00 |ryanding
@yongfa365
不用啊,我在想你为什么不能序列化呢,看我的代码:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#65楼2010-12-15 09:14 |yongfa365
#66楼2010-12-15 09:22 |韦恩卑鄙v-zhewg @waynebaby
@辰
我是真问啊。我的概念中只觉得emit是在生成il,把运行时动态的特点忘了呵呵
我觉得就算是静态生成il也只能生成按照字符串反射的il,因为很可能传入的类型不同,所以和emit没啥区别呀。
#67楼2010-12-15 09:28 |韦恩卑鄙v-zhewg @waynebaby
@yongfa365
你要用wcf数据契约序列化的话匿名类型肯定有困难,那个很严格的,复杂类型需要datacontract特性支持,换字典吧dictionary<string,object>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#68楼[楼主]2010-12-1509:39 |ryanding
@yongfa365
我在mvc2.0下测试序列化dynamic对象为JSON通过了,是因为我用了System.Web.Mvc 程序集底下的:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#69楼2010-12-15 09:41 |韦恩卑鄙v-zhewg @waynebaby
@ryanding
ajax脚本神马的 和wcf的严格程度是不同的世界。你总不能让人家服务端全用mvc吧 呵呵
#70楼[楼主]2010-12-1509:42 |ryanding
@韦恩卑鄙 v-zhewg@waynebaby
是啊,我意识到了。还有我对WCF一点都不熟悉。。
#71楼[楼主]2010-12-1509:48 |ryanding
@yongfa365
JavaScriptSerializer 替代 DataContractJsonSerializer 行么?
#72楼[楼主]2010-12-1509:56 |ryanding
@韦恩卑鄙 v-zhewg@waynebaby
对了,昨天你在反射测试的时候加了dictionary缓存字段,反而速度降了,内部是由于算hash导致?这点我还是有点迷糊,指教一下啊!
#74楼2010-12-15 11:05 |Aloner [Sofire]
#75楼2010-12-15 11:08 |Aloner [Sofire]
#77楼[楼主]2010-12-1511:43 |ryanding
#78楼[楼主]2010-12-1511:44 |ryanding
引用铁目诱惑:不反对所谓的新技术,但完全不支持项目中到处泛滥了这种东西。
没说到处泛滥的使用,要用在该用的地方!
#79楼2010-12-1513:33 |韦恩卑鄙 v-zhewg @waynebaby
动手实现扩展属性为对象动态添加获取数据
在实现扩展属性时我也参考了依赖属性的源码,它的设计思想的确很“先进”。
1:privatestatic ExtendProperty InfoProperty =
2: ExtendProperty.RegisterProperty("Info", typeof(string), typeof(UserInfo),"you win");
3: var user = new UserInfo() { Age=21, Name="maxzhang" };
4:
5: user.SetValue(InfoProperty, "hello");
6:string rrr = (string)user.GetValue(InfoProperty);
1: dynamic userDynamic = user.AsDynamic();
2: rrr= userDynamic.Info;
3: userDynamic.Info = "1";
4: userDynamic.Age = 50;
5: rrr = userDynamic.Info;
public class UserInfo1{ public string Name{set;get;} } 这个类不继承任何类。
1: AttachObject user1Aobj = new AttachObject(user1);
2: var dyuser = user1Aobj.ToDynamicAttachObject();
3://var dyuser = user1.ToDynamicAttachObject();
4: dyuser.Memo = "haha my name i's maxzhang......";
5: rrr = dyuser.Memo;
其实AttachObject 类型也是一个ExtendObject可以把它看成是一个ExtendObject的装饰。
RegisterPropertypublic static ExtendProperty RegisterProperty(string propertyName, Type propertyType, Type ownerType,object defaultValue)
{
var property = new ExtendProperty(propertyName, propertyType,ownerType);
property.OverrideDefaultValue(ownerType, defaultValue);
ExtendPropertysProvider.Set(property.GetHashCode(), property);
return property;
}
AddOwnerpublic ExtendProperty AddOwner(Type ownerType,object defaultValue)
{
int newOwnerHash = ownerType.GetHashCode() ^ this.PropertyName.GetHashCode();
if(defaultValue!=null)
this.OverrideDefaultValue(ownerType, defaultValue);
ExtendPropertysProvider.Set(newOwnerHash, this);
return this;
}
ExtendObject的源码,呵呵 public class ExtendObject
{
protected Dictionary<int, object> propertyValues = new Dictionary<int, object>();
private Type OwnerType = null;
public ExtendObject()
{
OwnerType = this.GetType();
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public virtual object GetOwner()
{
return this;
}
protected void AttachOwner(Type ownerType)
{
this.OwnerType = ownerType;
}
public bool IsExtendProperty(string propertyName)
{
return !OwnerType.GetProperties().Any(p => p.Name == propertyName); ;
}
protected ExtendProperty GetProperty(string name)
{
int propertyKey = OwnerType.GetHashCode() ^ name.GetHashCode();
var property = ExtendPropertysProvider.Get(propertyKey);
return property;
}
public object GetValue(ExtendProperty property)
{
int propertyHash = property.GetHashCode();
int key = this.GetHashCode() ^ propertyHash;
object result = null;
if (!propertyValues.TryGetValue(key, out result))
{
result = property.GetDefaultValue(this.OwnerType);
}
return result;
}
public bool ClearValue(ExtendProperty property)
{
bool result = false;
int propertyHash = property.GetHashCode();
int key = this.GetHashCode() ^ propertyHash;
if (propertyValues.Keys.Any(k => k == key))
{
propertyValues.Remove(key);
result = true;
}
return result;
}
public void SetValue(ExtendProperty property, object value)
{
var changedItemArgs = new ExtendPropertyValueChangedArgs();
int propertyHash = property.GetHashCode();
int key = this.GetHashCode() ^ propertyHash;
if (propertyValues.Keys.Any(k => k == key))
{
changedItemArgs.OldValue = propertyValues[key];
propertyValues[key] = value;
}
else
{
changedItemArgs.OldValue = null;
propertyValues.Add(key, value);
}
changedItemArgs.Item = GetOwner();
changedItemArgs.PropertyType = property.PropertyType;
changedItemArgs.PropertyName = property.PropertyName;
changedItemArgs.NewValue = value;
property.OnValueChanged(changedItemArgs);
}
public bool ClearValue(string propertyName)
{
var property = this.GetProperty(propertyName);
if (property != null)
return this.ClearValue(property);
return false;
}
public object GetValue(string propertyName)
{
var property = this.GetProperty(propertyName);
if (property != null)
return this.GetValue(property);
return null;
}
public void SetValue(string propertyName, object value)
{
var property = this.GetProperty(propertyName);
if (property != null)
{
this.SetValue(property, value);
}
else
{
var newProperty = ExtendProperty.RegisterProperty(propertyName, typeof(object), OwnerType);
this.SetValue(newProperty, value);
}
}
public ExtendDynamicObject AsDynamic()
{
return new ExtendDynamicObject(this);
}
}
AttachObject类通过调用AttachOwner方法使用了这个技巧,同时把同样为ExtendObject的对象的属性统统都Copy过来.
AttachObjectpublic class AttachObject : ExtendObject
{
private object owner;
public AttachObject(object obj)
: base()
{
owner = obj;
if (owner is ExtendObject)
{
Type ownerType = typeof(ExtendObject);
FieldInfo fInfo = ownerType.GetField("propertyValues", BindingFlags.Default | BindingFlags.NonPublic | BindingFlags.Instance);
var ownerValues = fInfo.GetValue(owner) as Dictionary<int, object>;
foreach (var v in ownerValues)
this.propertyValues.Add(v.Key, v.Value);
}
this.AttachOwner(owner.GetType());
}
public override object GetOwner()
{
return owner;
}
public override int GetHashCode()
{
return owner.GetHashCode();
}
}
下一节中我将介绍如何实现动态性以及一些使用场景,代码下载……
.net的反射技术(2)深究及 性能比较相关推荐
- java技术详解_Java反射技术详解及实例解析
前言 相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替 ...
- 反射创建对象_如何应用Java反射技术灵活地创建程序类的对象实例
软件项目实训及课程设计指导--如何应用Java反射技术灵活地创建程序类的对象实例 1.如何应用属性配置文件实现对系统中的配置信息进行读写操作 Java中的属性配置文件主要可以作为软件应用系统及项目的配 ...
- 你知道C# 反射技术的应用吗?
反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类.结构.委托.接口和枚举等)的成员,包括方法.属性.事件,以及构造函数等.还可以获得每个成员的 ...
- java反射技术_java反射技术,逆向开发必备技能
相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了, ...
- Java反射技术详解
前言 相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替 ...
- 彻底搞懂java反射技术及其原理
概述:反射是java中最强大的技术之一,很多高级框架都用到了反射技术,面试中也是经常问的点,所以搞懂反射非常重要! 文章目录 1.反射是什么? 2.反射的底层原理 3.三种方式获取Class对象 4. ...
- 谈吉日嘎拉的《白话反射技术》及其他(技术篇)
社区又掀起了腥风血雨,这次又是吉日嘎拉这一博客园的众矢之的所引发的惨案.他的一篇<白话反射技术>发表之后,被包同学一篇文章狠狠地踩在脚底下,言辞之激烈令人罕见.从两片文章的内容与评论来看, ...
- 秋色园QBlog技术原理解析:性能优化篇:用户和文章计数器方案(十七)
2019独角兽企业重金招聘Python工程师标准>>> 上节概要: 上节 秋色园QBlog技术原理解析:性能优化篇:access的并发极限及分库分散并发方案(十六) 中, 介绍了 ...
- 如何用JNI技术提高Java的性能详解
阻碍Java获得广泛应用的一个主要因素是Java程序的运行效率.Java是介于解释型和编译型之间的一种语言,同样的程序,如果用编译型语言C来实现,其运行速度一般要比Java快一倍以上.Java具有平台 ...
- 秋色园QBlog技术原理解析:性能优化篇:数据库文章表分表及分库减压方案(十五)...
文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 3: 秋色 ...
最新文章
- 数据包接收系列 — IP协议处理流程(一)
- 【数据结构总结】第三章:栈和队列(线性结构)
- 『 效率工具 』Spring Boot版的轻量级代码生成器,减少70%以上的开发任务
- d0...while循环结构讲解
- Tuxera NTFS使用教程:关于Tuxera NTFS mac还有你不知道的用法
- java 传绝对路径无效_又传噩耗!知名主持人小济南因突发肺栓塞不幸去世,享年36岁...
- HTML特殊字符显示
- 图片转文字,手机摇身一变就是万能扫描仪!
- 微信小程序快递查询插件
- VM中安装虚拟工具Vmware tools
- esp32摄像显示时间_ESP32彩屏显示入门:我要五彩斑斓的黑,还有五光十色的白
- python处理ts_python将ts转换成MP4
- 【JS】用JS发送电子邮件
- Why my APNs push certificate did not work?
- 5款在线制图工具分享,快来看看!
- c#阿里CSB接口对接--
- 一文带你读懂何为 macOS App 公证,以及如何自动化实现
- C++ QT开发人机象棋(搜索算法)
- m利用SIMILINK仿真模块实现多径信道的动态仿真模拟
- 瑞昱RTL8201G(I)-VB-CG 规格应用--电口传输距离(500M)之王
热门文章
- 三角形主机linux,受热捧的三角形主机是什么?我来为你解析
- 圆形标定板_机器视觉学习笔记(2)--如何检测圆点标定板
- 无法软关机(关机变重启或关机不切断电源而显示:您可以安全关机)解决方法+ACPI精解...
- Python——切片操作
- 在 vue 路由懒加载中给 Webpack Chunks 命名
- 单片机原理及应用 实验一 计数显示器
- PySide6的安装
- 【C++/嵌入式笔试面试八股】大纲介绍
- 隐函数求导和相关变化率
- 打开新页打开企业邮箱FOXMAIL常见错误提示“Message format error”