Performance: CustomAttributeData is NOT for performance in current releases
I've detailed a number of specific types of tests in order to measure various areas of working with custom attributes. I've been comparing the CustomAttributeData.GetCustomAttributes and Attribute.GetCustomAttributes methods since they have a similar signature and perform similar work. The speed differences are terrible, so it almost doesn't make sense to compare them, but what the hell.
Attribute Retrieval
This is a measure of how fast the attributes are retrieved. While the tests themselves are definitive, I also want to point out the differences in terms of work and the different types of information you are going to retrieve. In general getting CustomAttributeData is going to take 50% longer than getting custom attributes.
int iterations = 100000;
Attribute[] looseAttrs =
Attribute.GetCustomAttributes(
typeof( Nana ), false );
IList<CustomAttributeData> attrs =
CustomAttributeData.GetCustomAttributes(
typeof( Nana ) );
DateTime start, end;
start = DateTime.Now;
for ( int i = 0; i < iterations; i++ ) {
attrs =
CustomAttributeData.GetCustomAttributes(
typeof( Nana ) );
}
end = DateTime.Now;
Console.WriteLine( "New Methods::Retrieve: {0}", end - start );
start = DateTime.Now;
for ( int i = 0; i < iterations; i++ ) {
looseAttrs =
Attribute.GetCustomAttributes(
typeof( Nana ), false );
}
end = DateTime.Now;
Console.WriteLine( "Old Methods::Retrieve: {0}", end - start );
The test isn't 100% fair. The two methods are retrieving slightly different information.
-
CustomAttributeData
-
Enumerate custom attributes at the meta-data level
-
Map the class and constructor values to a ConstructorInfo
-
Map all constructor arguments to a type/value structure
-
Map all named arguments to a type/value/name structure
-
Custom Attribute Objects
-
Enumerate custom attributes at the meta-data level
-
Prepare argument blob for reflection APIs
-
Create a new instance of the attribute class using the reflection APIs and arguments
-
Map named argument blobs to properties
What I've noticed is that the CustomAttributeData path isn't quite optimized. In fact, the more properties you place on the custom attribute (named arguments) the slower the performance of the methods get. I'm not sure why because they don't do anything with the additional properties. They don't add these to the NamedArguments collection and there isn't any place where they surface the values, so they shouldn't be attacking them with such vigor.
Attribute Value Examination
Here is where we get the values off of the custom attribute. If grabbing the individual values from the CustomAttributeData is going to be fast, it might make up for some of the time we lost in loading it in the first place, bringing it a bit closer to the old attribute methods. I'll give you a hint, we definitely aren't going to approach the speeds that a strongly typed attribute class with strongly typed properties is going to have. Here is some code you might use to test.
start = DateTime.Now;
for ( int i = 0; i < iterations*100; i++ ) {
string eek1 = (string) attrs[0].ConstructorArguments[0].Value;
int bug1 = (int) attrs[0].ConstructorArguments[1].Value;
}
end = DateTime.Now;
Console.WriteLine( "New Methods::Investigate: {0}", end - start );
start = DateTime.Now;
for ( int i = 0; i < iterations*100; i++ ) {
MCA mca = looseAttrs[0] as MCA;
string eek = mca.Foo;
int bug = mca.Bar;
}
end = DateTime.Now;
Console.WriteLine( "Old Methods::Investigate: {0}", end - start );
The old methods for investigating the attributes are about 4 times faster. Honestly, that doesn't make much sense until you realize the cost of unboxing the integer from an object type. There is also the overhead of accessing the various collections. I didn't bother optimizing either of the loops because of the large initial discrepancy in speed. The margin of error here could put the CustomAttributeData at 3 times instead of 4 times, but not much faster.
Making CustomAttributeData Faster
I'm a nice guy, so I'll make up some tests to ensure we get SOMETHING out of the new methods. While I've been stacking things in terms of a comparison, the CustomAttributeData class provides far more information for investigating the signature of the attribute than it does towards providing the data inside the attribute. Here is a comparison of what you get with both sets of methods.
| New Style | | Old Style |
| Separation of Constructor Arguments and Named Arguments | | No differentiation. You'd have to use reflection to try and get this type of information. |
| Differentiation between how the custom attribute was constructed in the meta-data. Whether or not various properties were used in the construction, including various different constructors and whether or not specific named properties were set or given default values. | | You can't get this information without specifically setting up your custom attribute to tell you how it was constructed. No amount of reflection will allow you to tell the difference between constructed versus named parameters. |
| Security: Data required to construct a custom attribute | | Security: The fully constructed custom attribute |
What do you really get from this? Well, these new methods are a new view, not necessarily a replacement. They give you more information than was available before, and are much closer to the meta-data persistence format of the custom attribute than anything that has been previously available. It allows you to fully examine all aspects of the custom attribute without resorting to the reflection APIs. They are clearly more useful for inspecting an assembly and it's meta-data, than they are for getting usable data at run-time. You'll still want to use the much faster APIs for getting strongly typed fully constructed custom attributes if you just plan on using the values. I think I'll switch to the CustomAttributeData methods because of the increased security and higher level of control. Now if only they could increase the speed. I'll let you know if I find out any tricks.