attribute is a meta-programming capability provided on the. NET platform that can be tagged to decorate various members. Whether it is component design, interoperability between languages, or the most common frame use, now is inseparable from the attribute. Due to the importance of attribute's function (Kent Beck believes that NUnit is better than the early junit design, one of the main aspects is the use of attribute), the Java language also introduced in version 5.0 Attribute a similar annotation concept. But attribute is also a reflection operation, usually normal use will not cause problems, but the dense call still has some impact on performance. This time we'll summarize how we can avoid some performance issues with attribute operations.
Source: HTTP://WWW.JINHUSNS.COM/PRODUCTS/DOWNLOAD/?TYPE=XCJ
Suppose we have a attribute, which is defined on a type:
01.
[AttributeUsage(AttributeTargets.Class,
02.
AllowMultiple =
true
,
03.
Inherited =
true
)]
04.
public
class
TestAttribute : Attribute
05.
{
06.
public
TestAttribute(
string
prop)
07.
{
08.
this
.Prop = prop;
09.
}
10.
11.
public
TestAttribute() { }
12.
13.
public
string
Prop {
get
;
set
; }
14.
}
15.
16.
[Test(
"Hello World"
)]
17.
[Test(Prop =
"Hello World"
)]
18.
public
class
SomeClass { }
So, if we need to get the testattribute marked on the SomeClass type, we will generally use the GetCustomAttributes method of the Type object. So what happened in it?
by using. NET Reflector to track the implementation, you will find that the logic is ultimately done by the CustomAttribute GetCustomAttributes method, and interested friends can find the most complex overload. Because of the complexity of the implementation, I did not understand the complete logic, but from the key code it can be seen that it actually uses the Activator.CreateInstance method to create the object, and uses reflection to set the properties of the attribute object. So I'm going to look at how much of this reflection operation accounts for the whole GetCustomAttributes method:
01.
CodeTimer.Time(
"GetCustomAttributes"
, 1000 * 100, () =>
02.
{
03.
var attributes =
typeof
(SomeClass).GetCustomAttributes(
typeof
(TestAttribute),
true
);
04.
});
05.
06.
CodeTimer.Time(
"Reflection"
, 1000 * 100, () =>
07.
{
08.
var a1 = (TestAttribute)Activator.CreateInstance(
typeof
(TestAttribute),
"Hello World"
);
09.
var a2 = (TestAttribute)Activator.CreateInstance(
typeof
(TestAttribute));
10.
typeof
(TestAttribute).GetProperty(
"Prop"
).SetValue(a2,
"Hello World"
,
null
);
11.
});
The results are as follows:
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
As you can see, although reflection is used in the GetCustomAttributes method for object creation and property setting, most of its overhead is used to get some metadata, which takes up 3/4 of the time, and the cost of reflection is actually only about 1/4. This is somewhat surprising, since it is a static metadata, why does the. NET framework not cache this data, but do it again every time? Even though we should not cache the last attribute object, the "information" used to construct the object is completely cacheable.
In fact, as pointed out by the last heros, the. NET framework has actually given enough information, that is, the GetCustomAttributes method of CustomAttributeData, which returns ilist< The Customattributedata> object that contains all the information needed to construct the attribute. In other words, I can "quickly build" attribute objects based on a customattributedata:
01.
public
class
AttributeFactory
02.
{
03.
public
AttributeFactory(CustomAttributeData data)
04.
{
05.
this
.Data = data;
06.
07.
var ctorInvoker =
new
ConstructorInvoker(data.Constructor);
08.
var ctorArgs = data.ConstructorArguments.Select(a => a.Value).ToArray();
09.
this
.m_attributeCreator = () => ctorInvoker.Invoke(ctorArgs);
10.
11.
this
.m_propertySetters =
new
List<Action<
object
>>();
12.
foreach
(var arg
in
data.NamedArguments)
13.
{
14.
var property = (PropertyInfo)arg.MemberInfo;
15.
var propertyAccessor =
new
PropertyAccessor(property);
16.
var value = arg.TypedValue.Value;
17.
this
.m_propertySetters.Add(o => propertyAccessor.SetValue(o, value));
18.
}
19.
}
20.
21.
public
CustomAttributeData Data {
get
;
private
set
; }
22.
23.
private
Func<
object
> m_attributeCreator;
24.
private
List<Action<
object
>> m_propertySetters;
25.
26.
public
Attribute Create()
27.
{
28.
var attribute =
this
.m_attributeCreator();
29.
30.
foreach
(var setter
in
this
.m_propertySetters)
31.
{
32.
setter(attribute);
33.
}
34.
35.
return
(Attribute)attribute;
36.
}
37.
}
Attributefactory used the Fastreflectionlib, The ConstructorInfo and PropertyInfo are packaged into high performance Constructorinvoker and PropertyAccessor objects, which can be used with an order of magnitude performance improvement. Let's take another test:
var factories = Customattributedata.getcustomattributes (typeof (SomeClass))
. Where (d = d.constructor.declaringtype = = typeof (Testattribute))
. Select (d = new Attributefactory (d)). ToList ();
Codetimer.time ("GetCustomAttributes", + +, () =
{
var attributes = typeof (SomeClass). GetCustomAttributes (typeof (Testattribute), true);
});
Codetimer.time ("attributefactory", + +, () = factories. ForEach (f = f.create ()));
The results are as follows:
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
Here, we first get all the defined CustomAttributeData objects in SomeClass, and then, based on their constructor type, determine which ones are used to construct the Testattribute object. They are then used to construct the attributefactory. Attributefactory instances can be cached and reused during actual use. In this way, we can get new attribute objects each time and avoid the inexplicable overhead of the GetCustomAttributes method.
In fact, we can completely use this method to achieve a higher performance Getcustomattributesex method, which behaves well. NET comes in exactly the same getcustomattributes, but the performance can be fast on countless--maybe 100 times times. However, although this method is not difficult to write, but more cumbersome. Because CustomAttributeData can only be used to obtain data that is "directly defined" on a member, the reality is that We also often have to decide whether or not to traverse the entire inheritance chain based on the AllowMultiple and inherited attributes of the AttributeUsage that are marked on a attribute. Only in this way can we reproduce the behavior of the GetCustomAttribute method completely.
But we have an advantage here, that is, "static." Once "static", we can use the "naked eye" to determine the specific processing mode for a particular scene, so that we do not need a very general Getcustomattributeex method. For example, in the actual use of the process, we can find that a attribute inherited property is False, then we can eliminate the hassle of traversing the inheritance chain.
Finally, there are two points that may be worth mentioning:
In addition to the GetCustomAttributes methods that are available to members such as type,assembly, attribute classes also have static getcustomattributes methods that can be used to get attribute objects. However, with. NET Reflector, we can see that static methods in the attribute class are ultimately delegated to their own instance methods, so there is no performance improvement. The only difference is parameterinfo--but I don't understand why it's so complicated and interested friends can explore it by themselves.
If you are merely judging whether a member defines a particular type of attribute object, you can use the attribute.isdefined static method. Its performance is many times more efficient than the length of the array after GetCustomAttributes. But personally, this is not very important, because this is a static information, even if we use a slower GetCustomAttributes method to judge, but also the final true or false results can be cached, which naturally does not have a performance problem.
The reason we call the GetCustomAttributes method over and over again is because each attribute object we get is new, so it might not be possible to cache them in some scenarios. Now, however, there is now a quicker way of doing so, and there will be no big problem in this area.
Performance optimization methods for attribute operations