Tribler/tribler

Slow execution of `DiscoveryBooster.take_step` due to frequent calls to `netifaces.ifaddresses`

kozlovsky opened this issue · 1 comments

The DiscoveryBooster.take_step() method intermittently exhibits slow performance, sometimes taking several seconds to execute a single call, blocking the asyncio loop. This method internally invokes EdgeWalk.take_step(), where EdgeWalk is instantiated with the parameters neighborhood_size=25, edge_length=25.

I profiled the slow EdgeWalk.take_step() execution and discovered that the bulk of the execution time was consumed by calls to netifaces.ifaddresses and netifaces.interfaces. Here are the statistics derived from profiling:

50332 function calls in 2.015 seconds
Ordered by: internal time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   580    1.577    0.003    1.577    0.003 {built-in method netifaces.ifaddresses}
   145    0.380    0.003    0.380    0.003 {built-in method netifaces.interfaces}
   150    0.007    0.000    0.008    0.000 tribler\venv\lib\site-packages\libnacl\__init__.py:506(crypto_sign)
   189    0.006    0.000    0.009    0.000 py-ipv8\ipv8\peerdiscovery\network.py:219(get_verified_by_address)
   105    0.005    0.000    0.005    0.000 {method 'acquire' of '_thread.RLock' objects}
   150    0.004    0.000    0.004    0.000 {method 'sendto' of '_socket.socket' objects}
   725    0.003    0.000    1.965    0.003 py-ipv8\ipv8\messaging\interfaces\endpoint.py:171(_get_interface_addresses)
   145    0.002    0.000    0.002    0.000 {built-in method builtins.__build_class__}
  9765    0.002    0.000    0.002    0.000 py-ipv8\ipv8\peer.py:72(addresses)
   450    0.001    0.000    0.003    0.000 py-ipv8\ipv8\messaging\serialization.py:353(pack_serializable)
   580    0.001    0.000    0.003    0.000 py-ipv8\ipv8\messaging\interfaces\endpoint.py:185(__init__)
   145    0.001    0.000    0.001    0.000 {method 'getsockname' of '_socket.socket' objects}
   150    0.001    0.000    1.988    0.013 py-ipv8\ipv8\community.py:225(create_introduction_request)
   145    0.001    0.000    1.969    0.014 py-ipv8\ipv8\messaging\interfaces\endpoint.py:299(_get_lan_address)
     1    0.001    0.001    2.015    2.015 py-ipv8\ipv8\peerdiscovery\discovery.py:110(take_step)
   580    0.001    0.000    0.001    0.000 py-ipv8\ipv8\messaging\interfaces\endpoint.py:217(<listcomp>)
  9770    0.001    0.000    0.001    0.000 {method 'values' of 'dict' objects}
  1175    0.001    0.000    0.001    0.000 {built-in method _socket.inet_aton}
   145    0.001    0.000    1.971    0.014 py-ipv8\ipv8\community.py:204(my_preferred_address)
  3096    0.001    0.000    0.001    0.000 {method 'get' of 'dict' objects}
   150    0.001    0.000    0.001    0.000 Python39\lib\ctypes\__init__.py:48(create_string_buffer)
   150    0.001    0.000    0.014    0.000 py-ipv8\ipv8\lazy_community.py:233(_ez_pack)
  1160    0.001    0.000    0.001    0.000 {built-in method _struct.unpack_from}
   150    0.001    0.000    1.994    0.013 py-ipv8\ipv8\community.py:461(walk_to)
   435    0.000    0.000    0.001    0.000 py-ipv8\ipv8\messaging\serialization.py:195(pack)
  2212    0.000    0.000    0.001    0.000 {built-in method builtins.isinstance}

As seen, the total execution time for EdgeWalk.take_step() was 2.015 seconds. Of this, 1.577 seconds were spent in netifaces.ifaddresses and 0.380 seconds in netifaces.interfaces.

Even though each individual invocation of netifaces.ifaddresses or netifaces.interfaces was reasonably fast (0.003 seconds per call), the sheer quantity of these calls (580 calls to netifaces.ifaddresses and 145 to netifaces.interfaces) resulted in a significant accumulation of execution time.

Proposed Mitigation:

To enhance the performance of the take_step method, we can cache the results from netifaces.ifaddresses and netifaces.interfaces. The cache duration could be a few seconds, which should significantly boost the speed of the take_step calls while still maintaining network information accuracy.

This is fixed in IPv8 release 2.11. The workaround fro this issue can be removed when the IPv8 dependency version is next updated.