perf(discovery): Windows discovery no longer slows down with every COM port on the machine - #515
Conversation
…COM port Both Windows discovery providers answered "which PnP entity is COM9?" with their own Win32_PnPEntity search per port, per pass — a LIKE-predicate query that costs 100-500 ms warm. A machine with a dozen COM ports therefore spent seconds of sequential blocking work before opening a single port, and continuous discovery repeated it every second (issue #487). WindowsPnpPortMap runs one PNPClass='Ports' query per pass, builds the port -> device-instance-id map from the captions, and caches it for 2 s, the shape MacOsUsbPortDescriptorProvider already uses for ioreg. Both providers read it; the location provider asks with refreshOnMiss because a miss there is a real answer (no location key), while for the descriptor provider a miss just means "probe the port anyway". The VID/PID parsing moves to PnpDeviceIdParser so it is unit testable off Windows, mirroring HidDevicePathParser/LocationParentWalker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by Qodoperf(discovery): Batch WMI port lookup via cached WindowsPnpPortMap
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1.
|
Qodo round 1: GetDeviceIds handed back the List<string> stored in the shared map, so a caller could cast it back and edit what every concurrent lookup sees. The lists are wrapped with AsReadOnly before publication — the wrapper shares the storage, and the only reference to the list itself goes out of scope with the build — plus a test pinning that a lookup's result rejects mutation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 5937454 |
|
Qodo-clean, CI green — ready for review. (2 rounds on head One note on CI: the first two runs of this head failed on |
What was wrong
On Windows, discovering a DAQiFi over USB got slower with every unrelated COM port on the machine. Before opening a single port, Core asked Windows "which device is COM3? which is COM4? …" one port at a time, and each of those questions is a WMI query costing roughly 100-500 ms. A normal laptop has eight to twelve COM ports (Bluetooth pairs, vendor virtual ports), so the filtering step alone burned one to five seconds of blocking work — and continuous discovery repeated the whole thing every second, so it never caught up. macOS and Linux were never affected; they already ask once and reuse the answer.
How it was fixed
Ask once. A single query now lists every serial port on the machine with the device it belongs to, and both Windows providers read that one map instead of running their own per-port query. The map is reused for two seconds, the same window the macOS provider has always used, so one discovery pass costs one query no matter how many COM ports are present.
The thing worth pushing back on is that a two-second-old map can be wrong about a port that appeared since it was built. The two callers need different things there, so they get different rules. For the port filter, an unknown port simply gets probed the old-fashioned way, so a stale miss costs one probe and can never hide a device — it takes the cached answer. For location keys, a miss is a real answer (no key at all) and it is asked precisely about a port that just showed up, so it forces a fresh map before answering no. The residual risk both share is a stale hit: a device plugged into a COM number that another device just vacated keeps the old identity until the map expires, so it is found on the next pass rather than this one. That is the same trade the macOS provider makes today.
Verification
refreshOnMissfails 2, refreshing on every miss fails 1, publishing the map before the query returns fails 1, checking only the first device id fails 1, case-sensitive port keys fail 1, and overwriting instead of appending fails 1. Full suite green on net9.0 (3070 Core + 86 Mcp) and net10.0 (3070), 0 warnings.ubuntu-latestonly — the same caveatdocs/DEVICE_INTERFACES.mdalready records forWindowsUsbLocationProvider. What is verified by inspection is that the new query is the old one minus its per-portAND Caption LIKE '%(COMn)%'clause, and that matching(COMn)including the parentheses reproduces that clause exactly (notably,(COM9)still does not match(COM90))./dev/cu.usbmodem1101— a no-regression check only, since macOS never constructs these providers: serial discovery foundsn=9090539562006014104fw 3.7.2, 3 s @ 500 Hz on channels 0-2 gave 1186 samples (this unit's known ~79 % clock ratio), SD storage query 7.80 GB, clean disconnect. No reboot, format, delete,SD:GET, firmware or LAN writes.closes #487
Not merging — this is for your review.