[pygccxml-development] Another performance tweak
Brought to you by:
mbaas,
roman_yakovenko
|
From: Allen B. <al...@vr...> - 2006-08-25 22:16:01
|
I just tracked down and fixed another major performance sink.
I saw in the profiler output that the majority of the time from my run
was spent in __eq__ in declaration.py (line 121) and __eq_calldef.py
line 121. This is the code that compares two calldefs to see if they
are equal. (note this was also where most of the calls to
algorithm.declration_path were coming from).
I was interested in tracking down where all these calls (over 6 million
of them) were coming from so I added some code to the __eq__ method of
calldef to keep track of all the ways it was called and store how many
hits it gets (an example of the output is at the end of the e-mail).
As a side note, I also counted the return value of true and false
separately just for fun. I found that out of the over 6million times it
was called, it returned True only 33 times and those only came from a
call path starting with _join_declarations. Every other test was false
every time, so there may be another optimization hiding in here to just
not call this test.
As it ended up I found that the vast majority of these calls cam from
the member_functions method in scopedef.py. I traced through there and
found that all the the __eq__ calls were coming from some nested calls
to _find_out_member_access_type that were coming from
access_type_matcher_t. I never did find out where access_type_matcher_t
was coming from since I was just asking for all the members.
Anyway, the way pygccxml works the decls don't actually know their
access type. Only their parents do. So if you want to know a decl
access type you have to ask the parent and then it loops over all of
it's internal members for each access type until it find the one you are
asking about. This meant that the member_functions method was at least
O(N^2) and possibly O(N^3).
So back to what I did to fix it. It seemed to me that for pygccxml the
access type of a member should remain static through a single
execution. So I added a caching mechanism to the
find_out_member_access_type that just stores the access type with the
member decl. Then the next time it is check we return it directly and
skip looping over all the lists and calling __eq__ so many millions of
times.
In the end the number of __eq__ calls from 6,010,000 to 271,500. This
took my build type from 344 seconds down to 116 seconds.
So when you combine this change with the one from yesterday the
generation process is now 7 times faster. Not bad for just modifying
two methods. :)
-Allen
PS. You can see the PerformanceTuning page on the wiki for pointers to
the tools I have been using.
---------- Example call chaining for __eq__: Eq: Called 238772 times
and *always* returned false ------
[0, 238772]: [('gen_bindings.py', 722, '?'), ('gen_bindings.py', 673,
'main'),
('/home/allenb/python/lib/python/pyplusplus/module_builder/builder.py',
236, 'build_code_creator'),
('/home/allenb/python/lib/python/pyplusplus/module_creator/creator.py',
541, 'create'),
('/home/allenb/python/lib/python/pygccxml/declarations/algorithm.py',
268, 'apply_visitor'),
('/home/allenb/python/lib/python/pyplusplus/module_creator/creator.py',
704, 'visit_class'),
('/home/allenb/python/lib/python/pyplusplus/module_creator/creator.py',
348, '_is_wrapper_needed'),
('/home/allenb/python/lib/python/pyplusplus/module_creator/creator.py',
287, 'redefined_funcs'),
('/home/allenb/python/lib/python/pygccxml/declarations/scopedef.py',
473, 'member_functions'),
('/home/allenb/python/lib/python/pygccxml/declarations/scopedef.py',
326, '_find_multiple'),
('/home/allenb/python/lib/python/pygccxml/declarations/matcher.py', 49,
'find'),
('/home/allenb/python/lib/python/pygccxml/declarations/scopedef.py',
258, '<lambda>'),
('/home/allenb/python/lib/python/pygccxml/declarations/matchers.py', 83,
'__call__'),
('/home/allenb/python/lib/python/pygccxml/declarations/matchers.py', 61,
'__call__'),
('/home/allenb/python/lib/python/pygccxml/declarations/matchers.py',
478, '__call__'),
('/home/allenb/python/lib/python/pygccxml/declarations/class_declaration.py',
321, 'find_out_member_access_type'),
('/home/allenb/python/lib/python/pygccxml/declarations/calldef.py', 310,
'__eq__'),
('/home/allenb/python/lib/python/pygccxml/declarations/calldef.py', 139,
'__eq__')]
|