perf: parallelize batch NIO creation across nodes

create_batch_nios bound NIOs in a serial for-loop, so during project open
every builtin L2 node (ethernet_switch/hub/cloud/nat) started its uBridge
one at a time (~0.5s each for fork + AF_UNIX connect). Group entries by
node and bind in parallel with asyncio.gather — mirroring update_batch_nios
— so independent uBridge processes start concurrently. Within a node,
entries stay serial to respect the per-node uBridge command lock.
This commit is contained in:
YueGuobin 2026-08-11 22:11:44 +08:00
parent 958aa59d7e
commit 1dd334caff
No known key found for this signature in database

View File

@ -245,21 +245,36 @@ async def create_batch_nios(
binding in memory; started nodes additionally wire uBridge. binding in memory; started nodes additionally wire uBridge.
""" """
added = 0 # Group entries by node so different nodes' uBridge processes are wired
# in parallel (each node has its own AF_UNIX socket). Within a node
# entries are serial to respect the per-node uBridge command lock.
# This is what makes builtin L2 nodes (ethernet_switch/hub/cloud/nat)
# start their uBridge concurrently during project open instead of one
# at a time (~0.5s each for fork + socket connect).
per_node = {}
for entry in batch.nios: for entry in batch.nios:
node = project.get_node(entry.node_id) per_node.setdefault(entry.node_id, []).append(entry)
nio_settings = jsonable_encoder(entry.nio, exclude_unset=True)
# Dynamips.create_nio(self, node, nio_settings) is async and takes an async def _create_one_node(node_id, entries):
# extra positional 'node'. Detect via bound-method parameter count: node = project.get_node(node_id)
# standard == 1, Dynamips == 2. Await the async variant. # Dynamips.create_nio(self, node, nio_settings) is async and takes
# an extra positional 'node'; other managers' create_nio(nio_settings)
# is synchronous. Detect once per node via bound-method parameter
# count (standard == 1, Dynamips == 2) and await the async variant.
sig = inspect.signature(node.manager.create_nio) sig = inspect.signature(node.manager.create_nio)
if len(sig.parameters) >= 2: dynamips_style = len(sig.parameters) >= 2
nio = await node.manager.create_nio(node, nio_settings) for entry in entries:
else: nio_settings = jsonable_encoder(entry.nio, exclude_unset=True)
nio = node.manager.create_nio(nio_settings) if dynamips_style:
await _add_nio_binding(node, entry.adapter_number, entry.port_number, nio) nio = await node.manager.create_nio(node, nio_settings)
added += 1 else:
return {"added": added} nio = node.manager.create_nio(nio_settings)
await _add_nio_binding(node, entry.adapter_number, entry.port_number, nio)
await asyncio.gather(
*[_create_one_node(nid, ents) for nid, ents in per_node.items()]
)
return {"added": len(batch.nios)}
@router.put( @router.put(