Skip to content

[Windows] net_io_counters() performance #2695

@prutschman-iv

Description

@prutschman-iv

Summary

  • OS: Windows
  • Type: performance

Description

I was troubleshooting a CPU usage issue in Dask dask/distributed#9161, and noticed that its usage of psutil.net_io_counters() was inducing nontrivial CPU load in the "Service Host: DNS Client" task. (Each Dask worker is calling this at least once a second, which is turning out to add up to something non-trivial on a many-core machine)

I traced this to the use of GetAdaptersAddresses in psutil_get_nic_addresses. Experimenting with this interface, I determined there are a few opportunities to speed this up, with differing orders of complexity, some anticipated by #1426

I made a CLI utility to make the same sequence of syscalls as psutil.net_io_counters, to benchmark. The base version calls GetAdapterAddresses once to get a buffer size, then again to fetch data. Then it calls GetIfEntry2 for each interface (of which there are maybe a dozen on my machine).

These are the optimization "phases"

Base version: 6.6 msec.
Skip flags: 2.8 msec.
Skip flags & remember size: 1.6 msec
Cache all interfaces: 0.5 ms (Possibly too aggressive)

First, there are flags that can be provided to GetAdaptersAddresses to skip the collection of various information. I tested with GAA_FLAG_SKIP_DNS_INFO | GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER which skips all the skippable options except the friendly name. This may render it incompatible with other uses of psutil_get_nic_addresses, so it might be necessary to take an input parameter to indicate whether the full information should be collected. But this is a big win.

If we remember the buffer size from the first call to GetAdaptersAddresses we can re-use it, and it will almost always be correct, differing only when a new interface appears. This means we only call GetAdaptersAddresses once per call, instead of twice, which helps a fair bit. (And if it does change, we'll still detect this and update the cached size, we won't miss any interfaces).

If we remember all the interfaces and don't call GetAdaptersAddresses at all, we get an even bigger speedup. This was suggested as a future optimization in #1426 but it means not detecting new interfaces when they come up, which changes the semantics since new interfaces won't be noticed.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions