After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 640073 - _setup_vfuncs unnecessarily searches for vfunc conflicts; makes class creation slow.
_setup_vfuncs unnecessarily searches for vfunc conflicts; makes class creatio...
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: introspection
unspecified
Other All
: Normal normal
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2011-01-20 15:40 UTC by Laszlo Pandy
Modified: 2011-01-25 23:51 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
[gi] Speed up class creation: only check for vfunc conflicts if vfunc is going to be hooked up. (3.37 KB, patch)
2011-01-20 15:40 UTC, Laszlo Pandy
none Details | Review
[gi] Speed up class creation: only check for vfunc conflicts if vfunc is going to be hooked up. (3.93 KB, patch)
2011-01-21 10:11 UTC, Laszlo Pandy
none Details | Review
[gi] Speed up class creation: only check for vfunc conflicts if vfunc is going to be hooked up. (3.67 KB, patch)
2011-01-21 16:24 UTC, Laszlo Pandy
none Details | Review
[gi] Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. (7.36 KB, patch)
2011-01-25 14:39 UTC, Laszlo Pandy
none Details | Review
[gi] Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. (8.49 KB, patch)
2011-01-25 16:11 UTC, Laszlo Pandy
accepted-commit_now Details | Review
[gi] Speed up class creation: rewrite _setup_vfuncs() to be much more efficient. (8.74 KB, patch)
2011-01-25 23:22 UTC, Laszlo Pandy
none Details | Review

Description Laszlo Pandy 2011-01-20 15:40:31 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.
Comment 1 Laszlo Pandy 2011-01-20 15:40:34 UTC
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__.
Comment 2 Laszlo Pandy 2011-01-21 10:11:53 UTC
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__.
Comment 3 Simon Schampijer 2011-01-21 10:27:01 UTC
Great, this speeds up significantly, works fine for me! :)
Comment 4 Laszlo Pandy 2011-01-21 16:24:51 UTC
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.
Comment 5 Sebastian Pölsterl 2011-01-25 13:42:51 UTC
Patch works flawlessly for me and speeds things up tremendously.
Comment 6 Laszlo Pandy 2011-01-25 14:39:34 UTC
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.
Comment 7 Steve Frécinaux 2011-01-25 14:55:41 UTC
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...
Comment 8 Laszlo Pandy 2011-01-25 16:11:48 UTC
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).
Comment 9 Tomeu Vizoso 2011-01-25 18:22:38 UTC
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?
Comment 10 Laszlo Pandy 2011-01-25 23:22:04 UTC
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.