[fpc-devel] Optimization for TObject.InheritsFrom (and the 'is' operator)
Bram Kuijvenhoven
kuifwaremailinglists at xs4all.nl
Sun Jul 22 11:37:40 CEST 2007
Peter Popov wrote:
> Even if packages won't work, there is a compromise solution. One can
> still re-arrange the existing hierarchy. The default
> TObject.InheritsFrom is as before - traverses the tree. However, a
> custom class may use the fact that the arrangement is linear. This way,
> if one has a big custom hierarchy of classes in a single-sourced
> library, then the search can be O(1) within the library and O(Depth)
> outside. If the custom class is near the top of the tree, say
> TPersistent, that the algorithm will be fast
This is indeed a comprise solution if re-numbering VMTs on package loading is not feasible or not desired. Of course the compiler must still number the VMTs within the package, and since InheritsFrom is not virtual, any use of 'is' and 'as' will still call TObject.InheritsFrom. In other words: the programmer must replace 'is' by calls to TMyRootObject.InheritsFrom, and 'as' by a macro at best.
Below I give more details of an efficient VMT re-indexing algorithm.
> On Sat, 21 Jul 2007 15:14:56 -0500, Bram Kuijvenhoven
> <kuifwaremailinglists at xs4all.nl> wrote:
>> If a package is loaded dynamically, the preorder indices in the VMTs
>> would need to be recalculated. If it is loaded statically, the
>> compiler can do the calculation on beforehand. Of course the loading
>> of packages must be done synchronized (in a critical section), but
>> that might already have been the case. Also, it should be made
>> possible to enumerate the VMTs that do reside in a package.
>>
>> I think this outweighs the performance gain for InheritsFrom (and 'is'
>> and 'as'), and it is most beneficial for applications that do not use
>> packages.
In fact I have figured out that the renumbering can be done without additional memory requirements and in linear time.
So we add two fields to the VMT:
- vmtPreOrderIndex
- vmtLastInSubtreeIndex
Both are to be of type PtrUInt, so they can be casted to pointers. This is used in the algorithm below.
The algorithm consists of three steps:
1) Set the vmtPreOrderIndex and vmtLastInSubtreeIndex fields of all VMTs to nil
2) Build linked lists of childs for each class by using vmtPreOrderIndex as FirstChild and vmtLastInSubtreeIndex as NextSibling
3) Walk the class hierarchy in a depth first fashion, replacing vmtPreOrderIndex/FirstChild by the current Index when a node is encountered first, and replacing vmtLastInSubtreeIndex/NextSibling when the node is encountered last (i.e. its entire subtree has been walked)
(Step 2) gathers exactly that information about the class hierarchy that is required for the depth-first walk in step 3).)
The nice thing is that steps 2) and 3) can be done in *linear time* and *without stack requirements* (i.e. without recursion)!
Pseudo-code for 2):
for vmt in VMTs do
parent = vmt.parent
if parent = nil then
root := vmt // this will be TObject
else // add vmt to linked list of children of parent
if parent.FirstChild = nil then
parent.FirstChild = vmt
else
vmt.NextSibling = parent.FirstChild
parent.FirstChild = vmt
Pseudo-code for 3):
vmt = root // the vmt of TObject
index = 0
while vmt<>nil do
firstChild = vmt.FirstChild
vmt.vmtPreOrderIndex = index
if firstChild<>nil then
vmt = firstChild
else // find next sibling of first ancestor that has one
while vmt<>nil do
nextSibling = vmt.nextSibling
vmt.LastInSubtreeIndex = index
if nextSibling<>nil then
vmt = nextSibling
Break
else
vmt = vmt.parent
So how about implementing this? The same could could be used in the compiler to fill in the initial values of vmtPreOrderIndex/vmtLastInSubtreeIndex.
Bram
More information about the fpc-devel
mailing list