GNOME Bugzilla – Bug 640073
_setup_vfuncs unnecessarily searches for vfunc conflicts; makes class creation slow.
Last modified: 2011-01-25 23:51:44 UTC
_setup_vfuncs searches for vfunc conflicts before checking if the vfunc is actually part of the base class (and therefore shouldn't be hooked up). This means subclasses which don't define any virtual function overrides will still require searching for conflicts for every possible super class virtual function. This makes creating subclasses and therefore importing modules very slow.
Created attachment 178845 [details] [review] [gi] Speed up class creation: only check for vfunc conflicts if vfunc is going to be hooked up. This patch moves the call of the method which checks if the vfunc is part of the base class to before the method for searching for a vfunc conflict. This means that we will not do a search if a method is not going to be used. This reduces the number of vfunc conflict searches during the import of Gtk from 13646 to 0, and the CPU time required to import from 2.54s to 0.18s. Also included is a small optimization for is_function_in_classes(). By passing the function we can retrieve the function directly, instead of searching through the entire class __dict__.
Created attachment 178919 [details] [review] [gi] Speed up class creation: only check for vfunc conflicts if vfunc is going to be hooked up. This patch moves the call of the method which checks if the vfunc is part of the base class to before the method for searching for a vfunc conflict. This means that we will not do a search if a method is not going to be used. This reduces the number of vfunc conflict searches during the import of Gtk from 13646 to 0, and the CPU time required to import from 2.54s to 0.18s. Another optimation is checking for an "do_*" methods before attempting to set up virtual functions, and skipping the vfunc method entirely. There is also a small optimization for is_function_in_classes(). By passing the function we can retrieve the function directly, instead of searching through the entire class __dict__.
Great, this speeds up significantly, works fine for me! :)
Created attachment 178950 [details] [review] [gi] Speed up class creation: only check for vfunc conflicts if vfunc is going to be hooked up. This patch only looks for virtual function overrides in the class __dict__. Previously it was using getattr() which means that another check had to be done to see if the function was actually part of the base class. This makes the code simpler, but also avoids doing a conflict check if the method to be wrapped is part of the base class. This means that we will not do a vfunc conflict search if a method is not going to be used. This reduces the number of vfunc conflict searches during the import of Gtk from 13646 to 0, and the CPU time required to import from 2.54s to 0.18s. Another optimation is checking for an "do_*" methods before attempting to set up virtual functions, and skipping the vfunc method entirely. The function is_function_in_classes() has been deleted. Because of the above changes, it is not used anymore.
Patch works flawlessly for me and speeds things up tremendously.
Created attachment 179292 [details] [review] [gi] Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. This patch rewrites the _setup_vfuncs() method to remove recursion and make the running time linear in the number of virtual functions to hook up (ie. methods starting with "do_") instead of linear in the number of virtual functions in the base class which could possibly be overridden. Since most classes to not override all of the virtual functions in the base class (and many override none), this runs much faster. It is possible to not recurse on all base classes because non-interface base classes will have the virtual function installed as an attribute. Thus getattr() can be called, which recurses to the base classes much faster than a custom implementation in Python. If the method cannot be found with getattr(), all interface bases classes are searched manually. The function is_function_in_classes() has been deleted. Because of the above changes, it is not used anymore.
Review of attachment 179292 [details] [review]: This is a nice improvement on the previous version. Do you have some numbers on some measurable speedups ? > Since most classes to not override I guess you wanted to say 'do not'. ::: gi/types.py @@ +117,3 @@ + if method: + vfunc_info = method.__info__ + break What if I'm having something like this: class Foo(Gtk.Widget, SomePythonClass) where SomePythonClass has a do_coffee() method? In this case it wouldn't have a __info__ attribute. @@ +178,3 @@ + return v + + bases.extend(base.__bases__) Is there a reason why you don't use proper recursion? This scheme is harder to follow...
Created attachment 179295 [details] [review] [gi] Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. Here is the edit of the previous patch, with the issues Steve pointed out. The comment in the commit message is fixed. A check for hasattr(method, '__info__') and that is it of the type VFuncInto is included. The find_vfunc_info_in_interface() function uses regular recursion now because as steve pointed out there is no point in making the code less clear when there is no speed improvment. Also a test case has been included which sets up classes similar to Steve's do_coffee() example, and checks that there is no regression for handling do_* methods which are not wrapped vfuncs. As for numbers, on my machine this patch reduces the import time of Gtk from 2.5s to 0.12s. According to the hotshot profiler, the time spent in _setup_vfuncs (including functions it calls) was 0.073s per call, and now is 0.0 per call (unmeasurable).
Review of attachment 179295 [details] [review]: Looks like a great job! ::: gi/types.py @@ +116,3 @@ + for base in cls.__bases__: + method = getattr(base, vfunc_name, None) + if method and hasattr(method, '__info__') and \ if method is not None (see PEP-8) @@ +127,3 @@ + # InterfaceInfo.get_vfuncs(). Note that the infos returned by + # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix). + if not vfunc_info: if vfunc_info is None: (check for other instances of this) @@ +173,3 @@ + continue + + for v in base.__info__.get_vfuncs(): s/v/vfunc?
Created attachment 179341 [details] [review] [gi] Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. Updated version of the previous patch with Tomeu's suggestions implemented. Also the test case has been updated to check that the do_* methods that are not wrapped virtual functions can be called correctly, and not just defined without causing error.