Speedup in execution



Hello,

   I would like to propose two patches to speedup the bindings of
PyGObject through introspection.

   Profiling my code with cProfile in Python, I notice that some
getattr routines are called a huge number of time. Indeed, looking
closer, it comes from the fact that Python is looking for a __path__
attribute each time getattr() is called. So my first proposed
patch add a __path__ attribute in IntrospectionModule and
DynamicModule classes:
https://bugzilla.gnome.org/show_bug.cgi?id=630807

   Then, I notice that __getattr__ of DynamicModule is always called
when accessing a method or an attribute, while this method or attribute
may be found in override or in the introspection itself. The second
patch I suggest is a bit more intrusive and propose to move the
setattr() from the IntrospectionModule or the Override to the
DynamicModule itself. Currently, we have the following scheme:

from gi.repository import foo
a = foo.bar()
b = foo.bar()

will expand in:

getattr(foo, "bar") is None so
a = foo.introspection_module.__getattr__("bar") and register "bar" as a
method of foo.introspection_module
getattr(foo, "bar") is still None so
b = getattr(foo.introspection_module, "bar")

while, the proposed patch will give:
getattr(foo, "bar") is None so
a = foo.introspection_module.__getattr__("bar") and register "bar" as
method of foo
b = getattr(foo, "bar")

I.e. we save one getattr() call. Here is the patch:
https://bugzilla.gnome.org/show_bug.cgi?id=630810

I give you the following profiling information:
Current HEAD:
============
   ncalls  tottime  cumtime filename:lineno(function)
        1    0.000    7.758 <string>:1(<module>)
   886600    0.618    3.483 module.py:226(__getattr__)
   443301    0.914    2.269 module.py:92(__getattr__)
        4    0.000    0.000 types.py:37(Function)
    63726    0.046    2.300 types.py:39(function)
   886600    0.596    2.865 {getattr}
   443301    1.355    1.355 {method 'find_by_name' of 'gi.Repository'
    63726    1.476    2.254 {method 'invoke' of 'gi.FunctionInfo'

Adding __path__ attribute in DynamicModule and IntrospectionModule:
==================================================================
   ncalls  tottime  cumtime filename:lineno(function)
        1    0.000    3.864 <string>:1(<module>)
   443300    0.331    0.427 module.py:228(__getattr__)
        1    0.000    0.000 module.py:93(__getattr__)
        4    0.000    0.000 types.py:37(Function)
    63726    0.048    1.425 types.py:39(function)
   443300    0.096    0.096 {getattr}
        1    0.000    0.000 {method 'find_by_name' of 'gi.Repository'
    63726    1.277    1.377 {method 'invoke' of 'gi.FunctionInfo'

Registering attributes and methods in the DynamicModule instead of
in its introspection_module attribute:
==================================================================
   ncalls  tottime  cumtime filename:lineno(function)
        1    0.000    2.629 <string>:1(<module>)
        1    0.000    0.000 module.py:227(__getattr__)
        1    0.000    0.000 module.py:93(__getattr__)
        4    0.000    0.000 types.py:37(Function)
    63726    0.050    1.109 types.py:39(function)
        1    0.000    0.000 {method 'find_by_name' of 'gi.Repository'
    63726    1.059    1.059 {method 'invoke' of 'gi.FunctionInfo'

One can see that half of the __getattr__ (and all find_by_name) are
removed by the __path__ patch and that the other half of __getattr__ is
removed by the second patch.

I ran make check and make chek.valgrind, everything is fine.

What's your opinion on this optimisation ?

Damien.


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]