From d8e1c9345171a04a4206b8fe58f7e373b38c926d Mon Sep 17 00:00:00 2001 From: ccndcn <2301835860@qq.com> Date: Mon, 6 Jul 2026 16:16:12 +0800 Subject: [PATCH 01/16] fix --- nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes.py b/nodes.py index 028e58c77ff..d18691c387a 100644 --- a/nodes.py +++ b/nodes.py @@ -2310,7 +2310,7 @@ async def init_external_custom_nodes(): node_paths = folder_paths.get_folder_paths("custom_nodes") node_import_times = [] for custom_node_path in node_paths: - possible_modules = os.listdir(os.path.realpath(custom_node_path)) + possible_modules = sorted(os.listdir(os.path.realpath(custom_node_path))) if "__pycache__" in possible_modules: possible_modules.remove("__pycache__") From f7d05c57b4a087d4c5f341921c637467c644fc46 Mon Sep 17 00:00:00 2001 From: ccndcn <2301835860@qq.com> Date: Tue, 7 Jul 2026 11:16:35 +0800 Subject: [PATCH 02/16] fix --- main.py | 3 ++- nodes.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index aa4ee2adbff..b05ae4df8b9 100644 --- a/main.py +++ b/main.py @@ -173,7 +173,8 @@ def execute_script(script_path): node_paths = folder_paths.get_folder_paths("custom_nodes") for custom_node_path in node_paths: - possible_modules = os.listdir(custom_node_path) + possible_modules = sorted(os.listdir(custom_node_path)) + print("!!!possible_modules1!!!:\n" + "\n".join(possible_modules)) node_prestartup_times = [] for possible_module in possible_modules: diff --git a/nodes.py b/nodes.py index d18691c387a..49cace462c5 100644 --- a/nodes.py +++ b/nodes.py @@ -2311,6 +2311,7 @@ async def init_external_custom_nodes(): node_import_times = [] for custom_node_path in node_paths: possible_modules = sorted(os.listdir(os.path.realpath(custom_node_path))) + print("!!!possible_modules2!!!:\n" + "\n".join(possible_modules)) if "__pycache__" in possible_modules: possible_modules.remove("__pycache__") From ecb4026550bbaa76921f3f365199b5999bc84c36 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Wed, 15 Apr 2026 16:45:48 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20partially=5Funload?= =?UTF-8?q?=20=E4=B8=8E=20need=5Fmmap=20=E5=8F=8A=20model=5Fto=5Fmmap=20?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- comfy/model_management.py | 2 +- comfy/model_patcher.py | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/comfy/model_management.py b/comfy/model_management.py index 1ada985881a..9366b55d722 100644 --- a/comfy/model_management.py +++ b/comfy/model_management.py @@ -829,7 +829,7 @@ def model_unload(self, memory_to_free=None, unpatch_weights=True): logging.debug(f"before unload, available_memory of offload device {self.model.offload_device}: {available_memory/(1024*1024*1024)} GB") mmap_mem_threshold = get_mmap_mem_threshold_gb() * 1024 * 1024 * 1024 # this is reserved memory for other system usage - if min(memory_to_free, model_loaded_size) > available_memory - mmap_mem_threshold or memory_to_free < model_loaded_size: + if memory_to_free < model_loaded_size: partially_unload = True else: partially_unload = False diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 3a37c5549f4..c8a6bee4e53 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -47,10 +47,10 @@ from comfy.patcher_extension import CallbacksMP, PatcherInjection, WrappersMP from comfy.model_management import get_free_memory, get_mmap_mem_threshold_gb, get_free_disk -def need_mmap() -> bool: +def need_mmap(offload_size: int = 0) -> bool: free_cpu_mem = get_free_memory(torch.device("cpu")) mmap_mem_threshold_gb = get_mmap_mem_threshold_gb() - if free_cpu_mem < mmap_mem_threshold_gb * 1024 * 1024 * 1024: + if free_cpu_mem - offload_size < mmap_mem_threshold_gb * 1024 * 1024 * 1024: logging.debug(f"Enabling mmap, current free cpu memory {free_cpu_mem/(1024*1024*1024)} GB < {mmap_mem_threshold_gb} GB") return True return False @@ -109,6 +109,14 @@ def model_to_mmap(model: torch.nn.Module): The same model with all tensors converted to memory-mapped format """ free_cpu_mem = get_free_memory(torch.device("cpu")) + free_disk_mem = get_free_disk() + model_mem = comfy.model_management.module_size(model) + if model_mem > free_cpu_mem: + logging.error(f"Not enough free CPU memory to convert model to mmap. Model size: {model_mem/(1024*1024*1024)} GB, free CPU memory: {free_cpu_mem/(1024*1024*1024)} GB") + raise ValueError("Not enough free CPU memory to convert model to mmap") + if model_mem > free_disk_mem: + logging.error(f"Not enough free disk memory to convert model to mmap. Model size: {model_mem/(1024*1024*1024)} GB, free disk memory: {free_disk_mem/(1024*1024*1024)} GB") + raise ValueError("Not enough free disk memory to convert model to mmap") logging.debug(f"Converting model {model.__class__.__name__} to mmap, current free cpu memory: {free_cpu_mem/(1024*1024*1024)} GB") def convert_fn(t): @@ -1240,9 +1248,12 @@ def unpatch_model(self, device_to=None, unpatch_weights=True): if device_to is not None: - if need_mmap(): + if need_mmap(offload_size=self.model_size()): # offload to mmap - model_to_mmap(self.model) + try: + model_to_mmap(self.model) + except Exception as e: + pass # todo else: self.model.to(device_to) self.model.device = device_to @@ -1305,12 +1316,13 @@ def partially_unload(self, device_to, memory_to_free=0, force_patch_weights=Fals bias_key = "{}.bias".format(n) if move_weight: cast_weight = self.force_cast_weights - if need_mmap(): - if get_free_disk() < module_mem: - logging.warning(f"Not enough disk space to offload {n} to mmap, current free disk space {get_free_disk()/(1024*1024*1024)} GB < {module_mem/(1024*1024*1024)} GB") + if need_mmap(offload_size=module_mem): + try: + # offload to mmap + model_to_mmap(m) + except Exception as e: + logging.error(f"Error occurred while offloading {n} to mmap: {e}") break - # offload to mmap - model_to_mmap(m) else: m.to(device_to) module_mem += move_weight_functions(m, device_to) From ca38678b591fe72459724c5f037c57a88d227e4b Mon Sep 17 00:00:00 2001 From: sfiisf Date: Thu, 23 Apr 2026 17:07:59 +0800 Subject: [PATCH 04/16] use gds to mmap offload --- comfy/model_patcher.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index c8a6bee4e53..bac6fd4f565 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -32,6 +32,7 @@ import weakref import gc import tqdm +import mmap import comfy.float import comfy.hooks @@ -61,21 +62,21 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: """ # Create temporary file if filename is None: - temp_file = tempfile.mkstemp(suffix='.pt', prefix='comfy_mmap_')[1] + temp_file = tempfile.mkstemp(suffix='.bin', prefix='comfy_mmap_')[1] else: temp_file = filename - # Save tensor to file - cpu_tensor = t.cpu() - torch.save(cpu_tensor, temp_file) - - # If we created a CPU copy from other device, delete it to free memory - if not t.device.type == 'cpu': - del cpu_tensor - gc.collect() - - # Load with mmap - this doesn't load all data into RAM - mmap_tensor = torch.load(temp_file, map_location='cpu', mmap=True, weights_only=False) + file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) + file.save_storage(t.untyped_storage(), offset=0) + t_type = t.dtype + t_shape = t.shape + num = t.numel() * t.element_size() + del t + gc.collect() + + fo = open(temp_file, "rb") + mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_READ) + mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() # Register cleanup callback - will be called when tensor is garbage collected def _cleanup(): @@ -111,9 +112,6 @@ def model_to_mmap(model: torch.nn.Module): free_cpu_mem = get_free_memory(torch.device("cpu")) free_disk_mem = get_free_disk() model_mem = comfy.model_management.module_size(model) - if model_mem > free_cpu_mem: - logging.error(f"Not enough free CPU memory to convert model to mmap. Model size: {model_mem/(1024*1024*1024)} GB, free CPU memory: {free_cpu_mem/(1024*1024*1024)} GB") - raise ValueError("Not enough free CPU memory to convert model to mmap") if model_mem > free_disk_mem: logging.error(f"Not enough free disk memory to convert model to mmap. Model size: {model_mem/(1024*1024*1024)} GB, free disk memory: {free_disk_mem/(1024*1024*1024)} GB") raise ValueError("Not enough free disk memory to convert model to mmap") @@ -1253,7 +1251,8 @@ def unpatch_model(self, device_to=None, unpatch_weights=True): try: model_to_mmap(self.model) except Exception as e: - pass # todo + logging.warning(f"Error occurred while offloading model to mmap: {e}") + # todo: 回退 然后 partially_unload else: self.model.to(device_to) self.model.device = device_to From 06539d583252b82240dbdc823fca432fd982c966 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Fri, 24 Apr 2026 14:29:33 +0800 Subject: [PATCH 05/16] =?UTF-8?q?mmap=20=E5=A4=B1=E8=B4=A5=E6=97=B6?= =?UTF-8?q?=E5=9B=9E=E9=80=80=E5=88=B0=20normal=20offload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- comfy/model_patcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index bac6fd4f565..817cfdd7993 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -1251,8 +1251,8 @@ def unpatch_model(self, device_to=None, unpatch_weights=True): try: model_to_mmap(self.model) except Exception as e: - logging.warning(f"Error occurred while offloading model to mmap: {e}") - # todo: 回退 然后 partially_unload + logging.warning(f"Error occurred while offloading model to mmap: {e}, fall back to normal offload") + self.model.to(device_to) else: self.model.to(device_to) self.model.device = device_to @@ -1320,8 +1320,8 @@ def partially_unload(self, device_to, memory_to_free=0, force_patch_weights=Fals # offload to mmap model_to_mmap(m) except Exception as e: - logging.error(f"Error occurred while offloading {n} to mmap: {e}") - break + logging.warning(f"Error occurred while offloading {n} to mmap: {e}, fall back to normal offload") + m.to(device_to) else: m.to(device_to) module_mem += move_weight_functions(m, device_to) From 0e61721525c675aca3052f5cde9e1fdb00413adb Mon Sep 17 00:00:00 2001 From: sfiisf Date: Tue, 28 Apr 2026 14:48:59 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=89=8D=E7=BD=AE?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=A3=80=E6=9F=A5&=E5=85=B3=E9=97=ADfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- comfy/model_patcher.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 817cfdd7993..3dea8310059 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -48,6 +48,8 @@ from comfy.patcher_extension import CallbacksMP, PatcherInjection, WrappersMP from comfy.model_management import get_free_memory, get_mmap_mem_threshold_gb, get_free_disk +_CUDA_GDS_AVAILABLE = hasattr(torch, "cuda") and hasattr(torch.cuda, "gds") and hasattr(torch.cuda.gds, "GdsFile") + def need_mmap(offload_size: int = 0) -> bool: free_cpu_mem = get_free_memory(torch.device("cpu")) mmap_mem_threshold_gb = get_mmap_mem_threshold_gb() @@ -66,17 +68,29 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: else: temp_file = filename - file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) - file.save_storage(t.untyped_storage(), offset=0) - t_type = t.dtype - t_shape = t.shape - num = t.numel() * t.element_size() - del t - gc.collect() - - fo = open(temp_file, "rb") - mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_READ) - mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() + if _CUDA_GDS_AVAILABLE: + file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) + file.save_storage(t.untyped_storage(), offset=0) + t_type = t.dtype + t_shape = t.shape + num = t.numel() * t.element_size() + del t + gc.collect() + + with open(temp_file, "rb") as fo: + mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_READ) + mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() + else: + cpu_tensor = t.cpu() + torch.save(cpu_tensor, temp_file) + + # If we created a CPU copy from other device, delete it to free memory + if not t.device.type == 'cpu': + del cpu_tensor + gc.collect() + + # Load with mmap - this doesn't load all data into RAM + mmap_tensor = torch.load(temp_file, map_location='cpu', mmap=True, weights_only=False) # Register cleanup callback - will be called when tensor is garbage collected def _cleanup(): From 222045f38ad5ea09a99490dad523108cb5bb2a79 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Tue, 16 Jun 2026 12:24:23 +0800 Subject: [PATCH 07/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=5FUSE=5FGDS=5FOFFLO?= =?UTF-8?q?AD=20=E6=8E=A7=E5=88=B6=E6=98=AF=E5=90=A6=E5=90=AF=E7=94=A8=20G?= =?UTF-8?q?DS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- comfy/model_patcher.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 3dea8310059..756f4f6c472 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -48,6 +48,7 @@ from comfy.patcher_extension import CallbacksMP, PatcherInjection, WrappersMP from comfy.model_management import get_free_memory, get_mmap_mem_threshold_gb, get_free_disk +_USE_GDS_OFFLOAD = bool(os.environ.get("USE_GDS_OFFLOAD", "False").lower() in ("true", "1", "yes")) _CUDA_GDS_AVAILABLE = hasattr(torch, "cuda") and hasattr(torch.cuda, "gds") and hasattr(torch.cuda.gds, "GdsFile") def need_mmap(offload_size: int = 0) -> bool: @@ -68,7 +69,7 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: else: temp_file = filename - if _CUDA_GDS_AVAILABLE: + if _USE_GDS_OFFLOAD and _CUDA_GDS_AVAILABLE: file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) file.save_storage(t.untyped_storage(), offset=0) t_type = t.dtype From 59ca53bb2a88fa2539f3b921da24f451b12aa133 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Tue, 16 Jun 2026 17:31:14 +0800 Subject: [PATCH 08/16] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=AC=A6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- comfy/model_patcher.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 756f4f6c472..fc8f060e760 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -76,11 +76,13 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: t_shape = t.shape num = t.numel() * t.element_size() del t + del file gc.collect() with open(temp_file, "rb") as fo: mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_READ) mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() + mmap_tensor._mmap = mm else: cpu_tensor = t.cpu() torch.save(cpu_tensor, temp_file) @@ -99,6 +101,8 @@ def _cleanup(): if os.path.exists(temp_file): os.remove(temp_file) logging.debug(f"Cleaned up mmap file: {temp_file}") + if hasattr(mmap_tensor, "_mmap"): + mmap_tensor._mmap.close() except Exception: pass From 65711d47ca23ced9e96deb39f0f258e68740ec6f Mon Sep 17 00:00:00 2001 From: ccndcn <2301835860@qq.com> Date: Mon, 6 Jul 2026 16:16:12 +0800 Subject: [PATCH 09/16] fix --- nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes.py b/nodes.py index 1a3dd3f484f..b6ce893a571 100644 --- a/nodes.py +++ b/nodes.py @@ -2353,7 +2353,7 @@ async def init_external_custom_nodes(): node_paths = folder_paths.get_folder_paths("custom_nodes") node_import_times = [] for custom_node_path in node_paths: - possible_modules = os.listdir(os.path.realpath(custom_node_path)) + possible_modules = sorted(os.listdir(os.path.realpath(custom_node_path))) if "__pycache__" in possible_modules: possible_modules.remove("__pycache__") From 977bf6eb442ef962e5c4d25ef8062f9a82783bb0 Mon Sep 17 00:00:00 2001 From: ccndcn <2301835860@qq.com> Date: Tue, 7 Jul 2026 11:16:35 +0800 Subject: [PATCH 10/16] fix --- main.py | 3 ++- nodes.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index eb64726ea4e..8c4212637b9 100644 --- a/main.py +++ b/main.py @@ -185,7 +185,8 @@ def execute_script(script_path): node_paths = folder_paths.get_folder_paths("custom_nodes") for custom_node_path in node_paths: - possible_modules = os.listdir(custom_node_path) + possible_modules = sorted(os.listdir(custom_node_path)) + print("!!!possible_modules1!!!:\n" + "\n".join(possible_modules)) node_prestartup_times = [] for possible_module in possible_modules: diff --git a/nodes.py b/nodes.py index b6ce893a571..10a5e0d2ca5 100644 --- a/nodes.py +++ b/nodes.py @@ -2354,6 +2354,7 @@ async def init_external_custom_nodes(): node_import_times = [] for custom_node_path in node_paths: possible_modules = sorted(os.listdir(os.path.realpath(custom_node_path))) + print("!!!possible_modules2!!!:\n" + "\n".join(possible_modules)) if "__pycache__" in possible_modules: possible_modules.remove("__pycache__") From ab02e26fa9ca8005ea6c8a2f1096d10e9f2ed37b Mon Sep 17 00:00:00 2001 From: sfiisf Date: Fri, 24 Jul 2026 14:25:01 +0800 Subject: [PATCH 11/16] refine --- comfy/model_patcher.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index fc8f060e760..1357c545c19 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -65,11 +65,19 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: """ # Create temporary file if filename is None: - temp_file = tempfile.mkstemp(suffix='.bin', prefix='comfy_mmap_')[1] + fd, temp_file = tempfile.mkstemp(suffix='.bin', prefix='comfy_mmap_') + os.close(fd) else: temp_file = filename - if _USE_GDS_OFFLOAD and _CUDA_GDS_AVAILABLE: + use_gds = ( + _USE_GDS_OFFLOAD + and _CUDA_GDS_AVAILABLE + and t.is_contiguous() + and t.storage_offset() == 0 + and t.untyped_storage().nbytes() == t.numel() * t.element_size() + ) + if use_gds: file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) file.save_storage(t.untyped_storage(), offset=0) t_type = t.dtype @@ -96,17 +104,18 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: mmap_tensor = torch.load(temp_file, map_location='cpu', mmap=True, weights_only=False) # Register cleanup callback - will be called when tensor is garbage collected - def _cleanup(): + def _cleanup(temp_file, mmap_file): try: + if mmap_file is not None: + mmap_file.close() if os.path.exists(temp_file): os.remove(temp_file) logging.debug(f"Cleaned up mmap file: {temp_file}") - if hasattr(mmap_tensor, "_mmap"): - mmap_tensor._mmap.close() except Exception: pass - weakref.finalize(mmap_tensor, _cleanup) + mmap_file = getattr(mmap_tensor, "_mmap", None) + weakref.finalize(mmap_tensor, _cleanup, temp_file, mmap_file) return mmap_tensor From 97ecec37118a36d5a7d707c54acd675b7bc5e57e Mon Sep 17 00:00:00 2001 From: sfiisf Date: Tue, 18 Aug 2026 17:23:23 +0800 Subject: [PATCH 12/16] Fix mmap offload safety and startup logging - keep mmap resources alive for shared tensor storage - use copy-on-write mappings and restrict GDS to CUDA tensors - base mmap pressure on loaded model weights - move custom-node directory dumps to debug logging --- comfy/model_patcher.py | 9 ++++----- main.py | 2 +- nodes.py | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 1357c545c19..47e7002e298 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -76,6 +76,7 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: and t.is_contiguous() and t.storage_offset() == 0 and t.untyped_storage().nbytes() == t.numel() * t.element_size() + and t.is_cuda ) if use_gds: file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) @@ -83,12 +84,10 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: t_type = t.dtype t_shape = t.shape num = t.numel() * t.element_size() - del t del file - gc.collect() with open(temp_file, "rb") as fo: - mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_READ) + mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_COPY) mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() mmap_tensor._mmap = mm else: @@ -115,7 +114,7 @@ def _cleanup(temp_file, mmap_file): pass mmap_file = getattr(mmap_tensor, "_mmap", None) - weakref.finalize(mmap_tensor, _cleanup, temp_file, mmap_file) + weakref.finalize(mmap_tensor.untyped_storage(), _cleanup, temp_file, mmap_file) return mmap_tensor @@ -1274,7 +1273,7 @@ def unpatch_model(self, device_to=None, unpatch_weights=True): if device_to is not None: - if need_mmap(offload_size=self.model_size()): + if need_mmap(offload_size=self.loaded_size()): # offload to mmap try: model_to_mmap(self.model) diff --git a/main.py b/main.py index 8c4212637b9..977a33e2da2 100644 --- a/main.py +++ b/main.py @@ -186,7 +186,7 @@ def execute_script(script_path): node_paths = folder_paths.get_folder_paths("custom_nodes") for custom_node_path in node_paths: possible_modules = sorted(os.listdir(custom_node_path)) - print("!!!possible_modules1!!!:\n" + "\n".join(possible_modules)) + logging.debug("!!!possible_modules1!!!:\n" + "\n".join(possible_modules)) node_prestartup_times = [] for possible_module in possible_modules: diff --git a/nodes.py b/nodes.py index 10a5e0d2ca5..e3ca3feedca 100644 --- a/nodes.py +++ b/nodes.py @@ -2354,7 +2354,7 @@ async def init_external_custom_nodes(): node_import_times = [] for custom_node_path in node_paths: possible_modules = sorted(os.listdir(os.path.realpath(custom_node_path))) - print("!!!possible_modules2!!!:\n" + "\n".join(possible_modules)) + logging.debug("!!!possible_modules2!!!:\n" + "\n".join(possible_modules)) if "__pycache__" in possible_modules: possible_modules.remove("__pycache__") From 0c3e66ee5eaecbdf69f92f9ce5fadb639c6b66c6 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Wed, 19 Aug 2026 15:32:18 +0800 Subject: [PATCH 13/16] support QuantizedTensor offload to mmap --- comfy/model_patcher.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 47e7002e298..ef42ae836b0 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -146,8 +146,9 @@ def model_to_mmap(model: torch.nn.Module): def convert_fn(t): if isinstance(t, QuantizedTensor): - logging.debug(f"QuantizedTensor detected, mmap skipped, tensor meta info: size {t.size()}, dtype {t.dtype}, device {t.device}, is_contiguous {t.is_contiguous()}") - return t + inner_tensor_names, quant_ctx = t.__tensor_flatten__() + inner_tensors = {name: to_mmap(getattr(t, name)) for name in inner_tensor_names} + return QuantizedTensor.__tensor_unflatten__(inner_tensors, quant_ctx, t.size(), t.stride()) elif isinstance(t, torch.nn.Parameter): new_tensor = to_mmap(t.detach()) return torch.nn.Parameter(new_tensor, requires_grad=t.requires_grad) From f35becbdedc6e6aa5e358e9c698b5dc93cc3a6e3 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Wed, 19 Aug 2026 16:16:14 +0800 Subject: [PATCH 14/16] refine get_free_disk --- comfy/model_management.py | 4 ++-- comfy/model_patcher.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/comfy/model_management.py b/comfy/model_management.py index 9366b55d722..fe1e32c3347 100644 --- a/comfy/model_management.py +++ b/comfy/model_management.py @@ -49,8 +49,8 @@ def get_mmap_mem_threshold_gb(): logging.debug(f"MMAP_MEM_THRESHOLD_GB: {mmap_mem_threshold_gb}") return mmap_mem_threshold_gb -def get_free_disk(): - return psutil.disk_usage("/").free +def get_free_disk(dir: str = "/"): + return psutil.disk_usage(dir).free class VRAMState(Enum): DISABLED = 0 #No vram present: no need to move models to vram diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index ef42ae836b0..25cbbe01552 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -137,7 +137,7 @@ def model_to_mmap(model: torch.nn.Module): The same model with all tensors converted to memory-mapped format """ free_cpu_mem = get_free_memory(torch.device("cpu")) - free_disk_mem = get_free_disk() + free_disk_mem = get_free_disk(dir=tempfile.gettempdir()) model_mem = comfy.model_management.module_size(model) if model_mem > free_disk_mem: logging.error(f"Not enough free disk memory to convert model to mmap. Model size: {model_mem/(1024*1024*1024)} GB, free disk memory: {free_disk_mem/(1024*1024*1024)} GB") From 4a64eea334e9d3fdf0d13c2187d7ec5b3b9b35cf Mon Sep 17 00:00:00 2001 From: sfiisf Date: Wed, 19 Aug 2026 16:52:26 +0800 Subject: [PATCH 15/16] refine QuantizedTensor offload to mmap behavior --- comfy/ops.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/comfy/ops.py b/comfy/ops.py index ff64aad5924..699f77d9c47 100644 --- a/comfy/ops.py +++ b/comfy/ops.py @@ -1101,7 +1101,11 @@ def _quantized_apply(module, fn, recurse=True): p = fn(param) if (not torch.is_inference_mode_enabled()) and p.is_inference(): p = p.clone() - module.register_parameter(key, torch.nn.Parameter(p, requires_grad=False)) + new_param = torch.nn.Parameter(p, requires_grad=False) + if isinstance(p, QuantizedTensor): + new_param._qdata = p._qdata + new_param._params = p._params + module.register_parameter(key, new_param) for key, buf in module._buffers.items(): if buf is not None: module._buffers[key] = fn(buf) From a24bc15e6f9b74fe2ffdc07ecec6a9330ce767e4 Mon Sep 17 00:00:00 2001 From: sfiisf Date: Tue, 25 Aug 2026 14:35:17 +0800 Subject: [PATCH 16/16] refine gds behavior&adjust node check logging level --- comfy/model_patcher.py | 30 ++++++++++++++++++------------ main.py | 2 +- nodes.py | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index 25cbbe01552..a54de236881 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -79,18 +79,24 @@ def to_mmap(t: torch.Tensor, filename: Optional[str] = None) -> torch.Tensor: and t.is_cuda ) if use_gds: - file = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) - file.save_storage(t.untyped_storage(), offset=0) - t_type = t.dtype - t_shape = t.shape - num = t.numel() * t.element_size() - del file - - with open(temp_file, "rb") as fo: - mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_COPY) - mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() - mmap_tensor._mmap = mm - else: + gds = None + try: + gds = torch.cuda.gds.GdsFile(temp_file, os.O_CREAT | os.O_RDWR) + gds.save_storage(t.untyped_storage(), offset=0) + t_type = t.dtype + t_shape = t.shape + num = t.numel() * t.element_size() + + with open(temp_file, "rb") as fo: + mm = mmap.mmap(fo.fileno(), length=num, access=mmap.ACCESS_COPY) + mmap_tensor = torch.frombuffer(mm, dtype=t_type).reshape(t_shape).cpu() + mmap_tensor._mmap = mm + except Exception as e: + logging.warning(f"GDS offload failed, falling back to CPU mmap: {e}") + use_gds = False + finally: + del gds + if not use_gds: cpu_tensor = t.cpu() torch.save(cpu_tensor, temp_file) diff --git a/main.py b/main.py index 511d750e8b3..883dd6ca38c 100644 --- a/main.py +++ b/main.py @@ -186,7 +186,7 @@ def execute_script(script_path): node_paths = folder_paths.get_folder_paths("custom_nodes") for custom_node_path in node_paths: possible_modules = sorted(os.listdir(custom_node_path)) - logging.debug("!!!possible_modules1!!!:\n" + "\n".join(possible_modules)) + logging.info("!!!possible_modules1!!!:\n" + "\n".join(possible_modules)) node_prestartup_times = [] for possible_module in possible_modules: diff --git a/nodes.py b/nodes.py index e840fa51715..c37236b8691 100644 --- a/nodes.py +++ b/nodes.py @@ -2354,7 +2354,7 @@ async def init_external_custom_nodes(): node_import_times = [] for custom_node_path in node_paths: possible_modules = sorted(os.listdir(os.path.realpath(custom_node_path))) - logging.debug("!!!possible_modules2!!!:\n" + "\n".join(possible_modules)) + logging.info("!!!possible_modules2!!!:\n" + "\n".join(possible_modules)) if "__pycache__" in possible_modules: possible_modules.remove("__pycache__")