First of all RTTI is a very nice feature I think. You can do a lot of funny things especially when you combine this technique with Generics. But there is a problem I’m not very glad about: the speed is not that good, so I had to change some things in my O/R mapping class. I do not want to explain what I’m working on because my intention of this post is to write down some words about my experiences with this RTTI feature and the speed.
At the moment I have a test application which does the following: it has one class TTest which has two properties, PropA: String and PropB: Integer. I create one instance of this class and set the property in a loop which starts at 100000. The setter call on the one side is the direct setter method and on the other the SetValue() method of the RTTI class TRttiProperty. These are my results; the last two columns are denoted in milliseconds:
loop count | simple setter | RTTI SetValue() |
---|---|---|
100000 | 0 | 359 |
600000 | 15 | 2169 |
1100000 | 15 | 4025 |
1600000 | 31 | 5772 |
2100000 | 16 | 7566 |
2600000 | 31 | 9360 |
3100000 | 47 | 11201 |
3600000 | 47 | 13041 |
4100000 | 47 | 14852 |
4600000 | 62 | 16567 |
5100000 | 63 | 18345 |
As you can see this shows that a call of the SetValue() method is really slow against directly setting a property. So it is not advising that you are using too much of SetValue() like I did ;) I know that there is a lot of magic behind this method and TValue is very powerful but i haven’t believed that there is such a huge difference. In my case now I’m using a mixture of direct setting and getting properties and the usage of the RTTI SetValue/GetValue.
Some further tests…
Before some minutes I had a beautiful idea how to improve the speed of setting values via RTTI. Imagine you have an index over the properties (e.g. THashedStringList in unit IniFiles) so that you can find very fast a pointer of the property setter method (okay, you could do this every time via a loop… but that’s stupid). When you want to set a value to a property you only have to get the associated pointer to the setter method (look-up in index), “create” a TMethod instance, set method and object pointer in the TMethod instance and call it. Yeah, it could be so easy, but it is not so easy. Think about properties which doesn’t have a setter method or are not writeable. What about the data type!? TMethod does only support pointers, and so on…
Because there are some traps there are already implementations of this functionality. Look through the code of TypInfo.pas, there you can find methods for nearly all data types which already implements my idea ;) Some examples are SetStrProp(), SetOrdProp() (for integers), SetObjectProp() (for classes), and so on. Here some new Results comparing the three told possibilities (now I used TStopWatch in Diagnostics.pas instead of GetTickCount() method for measure time):
loop count | simple setter | RTTI SetValue() | TypInfo methods |
---|---|---|---|
100000 | 1 | 349 | 52 |
600000 | 6 | 2088 | 313 |
1100000 | 12 | 3813 | 571 |
1600000 | 18 | 5594 | 834 |
2100000 | 24 | 7281 | 1096 |
2600000 | 30 | 9047 | 1357 |
3100000 | 36 | 10787 | 1617 |
3600000 | 42 | 12549 | 1888 |
4100000 | 48 | 14303 | 2146 |
4600000 | 53 | 15980 | 2436 |
5100000 | 60 | 17783 | 2659 |
How you can see the usage of the setter methods in TypInfo.pas is much better than the SetValue() method of the TRttiProperty class. But when you want to use this solution you have to set the properties you want to set via RTTI published and enable the RTTI feature via {$M+} (set this before the corresponding class). You can easily do this with you own classes but what about others?
Fast, Faster, Fastest (at the moment ;)
I have found a way combining the old (TypInfo.pas) and the new (Rtti.pas) version of RTTI to improve speed about 90% comparing the normal setter method usage told above (i mean using the methods which expects a string as property name and not a pointer to PPropInfo). Here some code:
// get references to properties p1 := T.GetProperty('PropA'); p2 := T.GetProperty('PropB'); // get pointers to PPropInfo of properties pi1 := TRttiInstanceProperty(p1).PropInfo; pi2 := TRttiInstanceProperty(p2).PropInfo; // run benchmark... for i := 0 to counter do begin SetStrProp(obj, pi1, 'User'); SetOrdProp(obj, pi2, 100000); end;
The trick is not to use the propertyname in SetStrProp() or SetOrdProp() because these methods has an internal loop searching for the PPropInfo of the given property (yes, there als also some other stuff…). So i came to this results:
loop count | simple setter | RTTI SetValue() | TypInfo methods | Combined methods (code above) |
---|---|---|---|---|
100000 | 1 | 349 | 52 | 4 |
600000 | 6 | 2088 | 313 | 26 |
1100000 | 12 | 3813 | 571 | 48 |
1600000 | 18 | 5594 | 834 | 70 |
2100000 | 24 | 7281 | 1096 | 92 |
2600000 | 30 | 9047 | 1357 | 114 |
3100000 | 36 | 10787 | 1617 | 136 |
3600000 | 42 | 12549 | 1888 | 159 |
4100000 | 48 | 14303 | 2146 | 181 |
4600000 | 53 | 15980 | 2436 | 203 |
5100000 | 60 | 17783 | 2659 | 224 |
Now i will use RTTI setter methods again but in my way :)
Here you can download the RTTI Speed Demo (152.4 KiB, 358 hits) with which i created these results. I think that it will compile only in Delphi 2010 because if the usage of Rtti.pas and Diagnostics.pas.
2 replies on “RTTI in Delphi 2010, nice but a little bit too slow”
[…] […]
Thanks for the posting! I ‘ve had the same problem (for my own ORM implementation by the way ;-) ) and thought about performance, too. Your comparison made my life a bit easier …