Skip to content

Profile by self time: aenum.extend_enum is 16.7% of extraction, dispatch and imports under 1% #575

Description

@JarryShaw

Profiling pcapkit.extract() by self time rather than cumulative time puts the largest single cost in aenum.extend_enum, not in dispatch or imports. Filed so the next performance attempt starts from the measurement rather than from a guess.

Credit: this came out of profiling prompted by @Ts-Boom's #551 and #563. Both proposed optimisations that measurement did not support — struct.calcsize is already memoised inside CPython, and the import path's self time is under 1% — but the profiling those PRs prompted is what surfaced the figures below, and the suggestion to profile a real extract() run came from the #551 discussion.

The measurement

cProfile on main at 8cfd6ab01, http.pcap, 1117 frames, 2.09 s total, interpreter 3.14.7:

function calls tottime (self) cumtime
_import_next_layer 3351 0.0121 s (0.58%) 1.888 s (90.2%)
_lookup_next_layer 4695 0.0028 s (0.14%) 0.005 s (0.2%)
_lookup_registry 8490 0.0019 s (0.09%) 0.002 s (0.1%)

Top self-time consumers, same run: aenum.extend_enum at 16.7% — the single largest item in the whole profile — then schema unpack, getattr at 976k calls, schema __new__, isinstance/abc.__instancecheck__, and field unpack.

The lesson for anyone reading a profile of this library: _import_next_layer is a recursive-descent dispatcher, so it sits in the call chain of every nested protocol layer and cumulative time attributes the entire parse beneath it to that one frame. Reading its 90% cumulative figure as a hotspot is the mistake #563's ~40% claim most plausibly came from.

Why extend_enum is plausibly addressable

It is called when a constant lookup meets a value the enumeration does not define and extends the enumeration at runtime — the _missing_ path. On a capture with many unrecognised codes that happens repeatedly. Whether the right answer is memoising the extension, avoiding it for values that will never be reused, or something else is exactly the design work this issue is for; it should not be guessed at.

Worth pairing with the getattr count: 976k calls on a 1117-frame capture is roughly 874 per frame, which suggests attribute access in a hot loop that may be hoistable.

What would make a PR here convincing

Self-time figures before and after on at least two captures — one ordinary such as http.pcap, one exercising the unrecognised-code path heavily such as many_interfaces.pcapng — plus wall-clock extract() timings with the repetition count and host load stated. Note that on a shared host single-digit-millisecond variance swamps microsecond-scale changes, so a claimed improvement smaller than the noise should be reported as unmeasurable rather than as a number. Byte-identical output across the sample captures, as #420 and #427 did.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementperfPull requests that improve performance (perf: subject prefix)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions