Target: 1.4.0 (Tier 1 — earn the benchmark)
_ethertype_class() (layer2/ethernet.py:21) and
_ip_protocol_class() (layer3/ip.py:48) both run their deferred
imports and rebuild a dict literal on every single call. They are
called once per layer per frame, so this is the hottest path in the
library.
Profiling a corpus walk puts _ip_protocol_class at the top by
tottime, with _ethertype_class third.
Measured
Against a module-level dict built once:
| Function | Speedup |
|---|
_ip_protocol_class | 91× |
_ethertype_class | 42× |
What to do
Build each table once at import (lazily on first use, to preserve the
existing import-cycle avoidance) and reduce the call to a dict lookup.
The ipv6= gating of _IPV6_ONLY_NUMBERS must be preserved — an IPv4
packet with protocol=0 must still not decode a Hop-by-Hop layer.
This is the same change as the public registry in #87 (Tier 2).
Doing it as a plain table now and generalising it into the registry
later is fine, but the two should not be designed in isolation.
Acceptance criteria
Part of #103.
Target: 1.4.0 (Tier 1 — earn the benchmark)
_ethertype_class()(layer2/ethernet.py:21) and_ip_protocol_class()(layer3/ip.py:48) both run their deferredimports and rebuild a
dictliteral on every single call. They arecalled once per layer per frame, so this is the hottest path in the
library.
Profiling a corpus walk puts
_ip_protocol_classat the top bytottime, with_ethertype_classthird.Measured
Against a module-level dict built once:
_ip_protocol_class_ethertype_classWhat to do
Build each table once at import (lazily on first use, to preserve the
existing import-cycle avoidance) and reduce the call to a dict lookup.
The
ipv6=gating of_IPV6_ONLY_NUMBERSmust be preserved — an IPv4packet with
protocol=0must still not decode a Hop-by-Hop layer.This is the same change as the public registry in #87 (Tier 2).
Doing it as a plain table now and generalising it into the registry
later is fine, but the two should not be designed in isolation.
Acceptance criteria
Part of #103.