perf: dedicated 500-worker thread pool for ubridge batch I/O

Replace the default asyncio executor (capped at ~32 threads) with a
dedicated ThreadPoolExecutor sized for large-topology parallelism.
When 500 nodes each call _connect_nio, up to 500 OS threads can now
send blocking ubridge commands in parallel — no longer serialised by
either the event loop or a small thread pool.

- ubridge_hypervisor: module-level _ubridge_sync_pool (max_workers=500)
- docker_vm._connect_nio: dispatches to the dedicated pool instead of
  the default executor
This commit is contained in:
YueGuobin 2026-08-10 23:11:23 +08:00
parent 05934fa8e8
commit b155980402
No known key found for this signature in database
2 changed files with 13 additions and 1 deletions

View File

@ -38,6 +38,7 @@ from gns3server.utils.hostname import is_rfc1123_hostname_valid
from gns3server.utils import macaddress_to_int, int_to_macaddress
from gns3server.compute.ubridge.ubridge_error import UbridgeError, UbridgeNamespaceError
from gns3server.compute.ubridge.ubridge_hypervisor import _ubridge_sync_pool
from ..base_node import BaseNode
from ..adapters.ethernet_adapter import EthernetAdapter
@ -1242,7 +1243,7 @@ class DockerVM(BaseNode):
async with getattr(self, lock_name):
loop = asyncio.get_running_loop()
await loop.run_in_executor(
None, # default thread-pool executor
_ubridge_sync_pool, # dedicated 500-worker thread pool
self._ubridge_hypervisor.send_batch_sync,
commands,
)

View File

@ -19,12 +19,23 @@ import time
import logging
import asyncio
import threading
import concurrent.futures
from gns3server.utils.asyncio import locking
from .ubridge_error import UbridgeError
log = logging.getLogger(__name__)
# Dedicated thread pool for blocking ubridge socket I/O. Every node gets
# its own ubridge process + socket, so N nodes can send commands in true
# OS-thread parallelism. The default asyncio executor caps at ~32 threads;
# sizing for the large-topology case (2500+ links → thousands of NIO add
# calls across hundreds of nodes).
_ubridge_sync_pool = concurrent.futures.ThreadPoolExecutor(
max_workers=500,
thread_name_prefix="ubridge-sync",
)
class UBridgeHypervisor: