Commit 326cf5e
Improve performance in packet dissection (#5005)
* refactor: improve field handling and performance optimizations in packet processing
Profiled packet dissection and optimized the critical path. Benchmark on `Ether/IP/TCP/Raw` (10K iterations): **6153 → 7197 pkt/s (+17%)**, function calls reduced from 6.71M to 4.92M (-27%).
## Changes
### `scapy/fields.py`
- **`Field.getfield()`**: Use `struct.unpack_from(buf)` instead of `struct.unpack(buf[:n])` to avoid temporary slice allocation. Falls back to slice for bytes subclasses (e.g. `TrailerBytes`) that override `__getitem__`.
- **`Field.m2i/h2i/i2m`**: Remove `typing.cast()` — 260K+ no-op function calls per 10K packets.
- **`_FieldContainer`/`Field`**: Add class-level `_is_conditional`/`_may_end` flags to avoid `isinstance()` in tight loops.
### `scapy/packet.py`
- **`do_dissect()`**: Check pre-computed field flags instead of `isinstance(ConditionalField)` / `isinstance(MayEnd)` per iteration.
- **`guess_payload_class()`**: Inline `getfieldval` with local variable caching of `self.fields`/`self.overloaded_fields`/`self.default_fields`. Original did 3 dict lookups + deprecated field check per field per candidate layer.
- **`getfieldval()`**: Replace `if k in d1 ... elif k in d2 ...` with single `try/except KeyError` on fast path.
- **`__init__()`**: Skip `time.time()` syscall for internal sub-layer packets (`_internal=1`).
- **`_raw_packet_cache_field_value()`**: Replace per-call lambda with direct attribute access.
- **`do_init_cached_fields()`**: Eliminate redundant `dict.get()` pattern.
## API
No public API changes.
## Dissection Throughput (10K iterations each)
| Packet Type | Baseline | Optimized | Δ |
|---|---|---|---|
| `Ether/IP/TCP/Raw(100B)` | 7,026 pkt/s | 7,508 pkt/s | **+6.9%** |
| `Ether/IP/UDP/DNS(query)` | 5,286 pkt/s | 5,685 pkt/s | **+7.5%** |
| `Ether/IP/UDP/DNS(response)` | 2,726 pkt/s | 2,889 pkt/s | **+6.0%** |
| `Ether/IP/ICMP` | 5,603 pkt/s | 6,000 pkt/s | **+7.1%** |
| `IP/TCP/Raw(50B)` | 9,389 pkt/s | 10,063 pkt/s | **+7.2%** |
| `Ether/IP/TCP/Raw(1400B)` | 6,870 pkt/s | 7,439 pkt/s | **+8.3%** |
| `Ether` (minimal) | 62,548 pkt/s | 64,819 pkt/s | **+3.6%** |
| `IP/UDP/Raw(5B)` | 7,998 pkt/s | 8,776 pkt/s | **+9.7%** |
| Batch 1000×`Ether/IP/TCP/Raw` (100K pkts) | 7,117 pkt/s | 7,781 pkt/s | **+9.3%** |
## Profile Comparison (5,000 `Ether/IP/TCP/Raw` dissections)
| Metric | Baseline | Optimized | Δ |
|---|---|---|---|
| Total function calls | 2,915,002 | 2,350,002 | **−19.4%** |
| Total time | 1.559s | 1.357s | **−13.0%** |
| `isinstance()` calls | 380,000 | 230,000 | **−39.5%** |
| `guess_payload_class` time | 0.137s | 0.062s | **−55%** |
## Key Observations
- Consistent **6–10% throughput improvement** across all packet types
- Larger improvement on simpler packets (IP/UDP, IP/TCP) where overhead is proportionally higher
- DNS response shows less improvement since most time is spent in complex DNS field parsing
- `isinstance()` calls reduced by ~40% via pre-computed `_is_conditional`/`_may_end` flags
- `guess_payload_class` is **2× faster** due to inlined field lookups with local variable caching
AI-Assisted: yes (Claude Code Opus 4.6)
* fix flake8 and mypy
AI-Assisted: no
* Potential fix for pull request finding
AI-Assisted: yes (GitHub Copilot)
* Apply feedback
AI-Assisted: no
---------
Co-authored-by: Nils Weiss <nils.weiss@dissecto.com>1 parent 69fb728 commit 326cf5e
2 files changed
Lines changed: 44 additions & 21 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
| 163 | + | |
| 164 | + | |
163 | 165 | | |
164 | 166 | | |
165 | 167 | | |
| |||
198 | 200 | | |
199 | 201 | | |
200 | 202 | | |
201 | | - | |
| 203 | + | |
202 | 204 | | |
203 | 205 | | |
204 | 206 | | |
| |||
208 | 210 | | |
209 | 211 | | |
210 | 212 | | |
211 | | - | |
| 213 | + | |
212 | 214 | | |
213 | 215 | | |
214 | 216 | | |
215 | 217 | | |
216 | 218 | | |
217 | | - | |
| 219 | + | |
218 | 220 | | |
219 | | - | |
220 | | - | |
| 221 | + | |
| 222 | + | |
221 | 223 | | |
222 | 224 | | |
223 | 225 | | |
| |||
257 | 259 | | |
258 | 260 | | |
259 | 261 | | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
260 | 266 | | |
261 | 267 | | |
262 | 268 | | |
| |||
311 | 317 | | |
312 | 318 | | |
313 | 319 | | |
| 320 | + | |
| 321 | + | |
314 | 322 | | |
315 | 323 | | |
316 | 324 | | |
| |||
349 | 357 | | |
350 | 358 | | |
351 | 359 | | |
| 360 | + | |
352 | 361 | | |
353 | 362 | | |
354 | 363 | | |
| |||
380 | 389 | | |
381 | 390 | | |
382 | 391 | | |
| 392 | + | |
383 | 393 | | |
384 | 394 | | |
385 | 395 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
36 | 35 | | |
37 | 36 | | |
38 | 37 | | |
| |||
156 | 155 | | |
157 | 156 | | |
158 | 157 | | |
159 | | - | |
| 158 | + | |
160 | 159 | | |
161 | 160 | | |
162 | 161 | | |
| |||
353 | 352 | | |
354 | 353 | | |
355 | 354 | | |
356 | | - | |
| 355 | + | |
| 356 | + | |
357 | 357 | | |
| 358 | + | |
358 | 359 | | |
359 | 360 | | |
360 | | - | |
361 | 361 | | |
362 | 362 | | |
363 | 363 | | |
| |||
517 | 517 | | |
518 | 518 | | |
519 | 519 | | |
520 | | - | |
| 520 | + | |
521 | 521 | | |
522 | | - | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
523 | 525 | | |
524 | | - | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
525 | 529 | | |
| 530 | + | |
| 531 | + | |
526 | 532 | | |
527 | 533 | | |
528 | 534 | | |
| |||
726 | 732 | | |
727 | 733 | | |
728 | 734 | | |
729 | | - | |
730 | 735 | | |
731 | 736 | | |
732 | 737 | | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
733 | 743 | | |
734 | | - | |
| 744 | + | |
735 | 745 | | |
736 | 746 | | |
737 | | - | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
738 | 751 | | |
739 | | - | |
| 752 | + | |
740 | 753 | | |
741 | 754 | | |
742 | 755 | | |
| |||
1082 | 1095 | | |
1083 | 1096 | | |
1084 | 1097 | | |
1085 | | - | |
| 1098 | + | |
1086 | 1099 | | |
1087 | 1100 | | |
1088 | 1101 | | |
| |||
1091 | 1104 | | |
1092 | 1105 | | |
1093 | 1106 | | |
1094 | | - | |
1095 | | - | |
1096 | | - | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
1097 | 1110 | | |
1098 | 1111 | | |
1099 | 1112 | | |
| |||
1859 | 1872 | | |
1860 | 1873 | | |
1861 | 1874 | | |
1862 | | - | |
| 1875 | + | |
1863 | 1876 | | |
1864 | 1877 | | |
1865 | 1878 | | |
| |||
0 commit comments