From b155980402ce7a19ea1f845953d93eb2e58efe2c Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Mon, 10 Aug 2026 23:11:23 +0800 Subject: [PATCH] perf: dedicated 500-worker thread pool for ubridge batch I/O MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- gns3server/compute/docker/docker_vm.py | 3 ++- gns3server/compute/ubridge/ubridge_hypervisor.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 6f2c425ba..ee387e76f 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -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, ) diff --git a/gns3server/compute/ubridge/ubridge_hypervisor.py b/gns3server/compute/ubridge/ubridge_hypervisor.py index 740c22378..68a84e477 100644 --- a/gns3server/compute/ubridge/ubridge_hypervisor.py +++ b/gns3server/compute/ubridge/ubridge_hypervisor.py @@ -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: