CRITICAL FIX: Enable proper GPU usage - was forcing CPU processing
User reported node processing on CPU/RAM instead of GPU/VRAM. Root cause identified:
enable_model_cpu_offload() was being called by default, forcing CPU processing
Research Findings (December 2025, verified sources):
- DiffusionPipeline.from_pretrained() loads to CPU by default (HuggingFace docs)
- Must explicitly call .to("cuda") to use GPU
- enable_model_cpu_offload() is for LOW VRAM systems (12-16GB)
- For modern GPUs with sufficient VRAM: .to("cuda") is fastest
Context Lessons Learned:
- Old loader (archive/nodes/loader.py) used proper GPU loading
- Context.md lines 124-157 documented this exact issue before
- I failed to learn from documented lessons - unacceptable
Fix Implemented:
- Added memory_mode parameter: "gpu" (default), "balanced", "lowvram"
- "gpu": .to("cuda") - Full GPU, fastest, needs 24GB+ VRAM
- "balanced": enable_model_cpu_offload() - Good for 12-16GB VRAM
- "lowvram": enable_sequential_cpu_offload() - Works with 8GB VRAM
- Updated pipeline caching to track memory_mode
- Clear console logging shows which mode is active
Changes:
- Added memory_mode to INPUT_TYPES with detailed tooltip
- Updated load_pipeline() to accept and handle memory_mode
- Updated __init__() to track current_memory_mode
- Updated generate() signature and caching logic
- Default is "gpu" for optimal performance on modern systems
User System (from report):
- torch 2.9.x + CUDA 12.8 (modern setup)
- Should use "gpu" mode for best performance
Verified Against:
- HuggingFace diffusers docs (memory optimization)
- Context.md lessons learned
- Archive code that worked
This is a fundamental fix that should have been caught during initial QA.
This commit is contained in:
+40
-8
@@ -60,6 +60,7 @@ class SDNQSampler:
|
||||
self.pipeline = None
|
||||
self.current_model_path = None
|
||||
self.current_dtype = None
|
||||
self.current_memory_mode = None
|
||||
self.interrupted = False
|
||||
|
||||
@classmethod
|
||||
@@ -142,6 +143,12 @@ class SDNQSampler:
|
||||
"tooltip": "Model precision. bfloat16 recommended for FLUX (best quality/speed). float16 for older GPUs. float32 for CPU."
|
||||
}),
|
||||
|
||||
# Memory management strategy
|
||||
"memory_mode": (["gpu", "balanced", "lowvram"], {
|
||||
"default": "gpu",
|
||||
"tooltip": "Memory management: 'gpu' = All on GPU (fastest, needs 24GB+ VRAM). 'balanced' = Model offloading (good for 12-16GB VRAM). 'lowvram' = Sequential offloading (slowest, works with 8GB VRAM)."
|
||||
}),
|
||||
|
||||
# Auto-download control
|
||||
"auto_download": ("BOOLEAN", {
|
||||
"default": True,
|
||||
@@ -272,7 +279,7 @@ class SDNQSampler:
|
||||
f"5. If download was interrupted, try again - it will resume"
|
||||
)
|
||||
|
||||
def load_pipeline(self, model_path: str, dtype_str: str) -> DiffusionPipeline:
|
||||
def load_pipeline(self, model_path: str, dtype_str: str, memory_mode: str = "gpu") -> DiffusionPipeline:
|
||||
"""
|
||||
Load SDNQ model using diffusers pipeline.
|
||||
|
||||
@@ -286,6 +293,7 @@ class SDNQSampler:
|
||||
Args:
|
||||
model_path: Local path to SDNQ model directory
|
||||
dtype_str: String dtype ("bfloat16", "float16", "float32")
|
||||
memory_mode: Memory management strategy ("gpu", "balanced", "lowvram")
|
||||
|
||||
Returns:
|
||||
Loaded diffusers pipeline
|
||||
@@ -295,6 +303,7 @@ class SDNQSampler:
|
||||
|
||||
Based on verified API from:
|
||||
https://huggingface.co/docs/diffusers/en/using-diffusers/loading
|
||||
https://huggingface.co/docs/diffusers/main/optimization/memory
|
||||
"""
|
||||
# Convert dtype string to torch dtype
|
||||
dtype_map = {
|
||||
@@ -306,20 +315,40 @@ class SDNQSampler:
|
||||
|
||||
print(f"[SDNQ Sampler] Loading model from: {model_path}")
|
||||
print(f"[SDNQ Sampler] Using dtype: {dtype_str} ({torch_dtype})")
|
||||
print(f"[SDNQ Sampler] Memory mode: {memory_mode}")
|
||||
|
||||
try:
|
||||
# Load pipeline - DiffusionPipeline auto-detects model type
|
||||
# SDNQ quantization is automatically detected from model config
|
||||
# Note: Pipeline loads to CPU by default - we move to GPU below
|
||||
pipeline = DiffusionPipeline.from_pretrained(
|
||||
model_path,
|
||||
torch_dtype=torch_dtype,
|
||||
local_files_only=True, # Only load from local path
|
||||
)
|
||||
|
||||
# Enable CPU offload for memory efficiency
|
||||
# This automatically manages device placement (model components on GPU when needed)
|
||||
# Verified from FLUX examples: https://huggingface.co/docs/diffusers/main/api/pipelines/flux
|
||||
pipeline.enable_model_cpu_offload()
|
||||
# Apply memory management strategy
|
||||
# Based on: https://huggingface.co/docs/diffusers/main/optimization/memory
|
||||
if memory_mode == "gpu":
|
||||
# Full GPU mode: Fastest performance, needs 24GB+ VRAM
|
||||
# Load entire pipeline to GPU
|
||||
print(f"[SDNQ Sampler] Moving model to GPU (full GPU mode)...")
|
||||
pipeline.to("cuda")
|
||||
print(f"[SDNQ Sampler] ✓ Model loaded to GPU (all components on VRAM)")
|
||||
|
||||
elif memory_mode == "balanced":
|
||||
# Model CPU offload: Good balance for 12-16GB VRAM
|
||||
# Moves whole models between CPU and GPU as needed
|
||||
print(f"[SDNQ Sampler] Enabling model CPU offload (balanced mode)...")
|
||||
pipeline.enable_model_cpu_offload()
|
||||
print(f"[SDNQ Sampler] ✓ Model offloading enabled (efficient VRAM usage)")
|
||||
|
||||
elif memory_mode == "lowvram":
|
||||
# Sequential CPU offload: Maximum memory savings for 8GB VRAM
|
||||
# Slowest but works on limited VRAM
|
||||
print(f"[SDNQ Sampler] Enabling sequential CPU offload (low VRAM mode)...")
|
||||
pipeline.enable_sequential_cpu_offload()
|
||||
print(f"[SDNQ Sampler] ✓ Sequential offloading enabled (minimal VRAM usage)")
|
||||
|
||||
print(f"[SDNQ Sampler] Model loaded successfully!")
|
||||
print(f"[SDNQ Sampler] Pipeline type: {type(pipeline).__name__}")
|
||||
@@ -475,7 +504,8 @@ class SDNQSampler:
|
||||
|
||||
def generate(self, model_selection: str, custom_model_path: str, prompt: str,
|
||||
steps: int, cfg: float, width: int, height: int, seed: int,
|
||||
dtype: str, auto_download: bool = True, negative_prompt: str = "") -> Tuple[torch.Tensor]:
|
||||
dtype: str, memory_mode: str = "gpu", auto_download: bool = True,
|
||||
negative_prompt: str = "") -> Tuple[torch.Tensor]:
|
||||
"""
|
||||
Main generation function called by ComfyUI.
|
||||
|
||||
@@ -520,12 +550,14 @@ class SDNQSampler:
|
||||
# Check if we need to reload the pipeline
|
||||
if (self.pipeline is None or
|
||||
self.current_model_path != model_path or
|
||||
self.current_dtype != dtype):
|
||||
self.current_dtype != dtype or
|
||||
self.current_memory_mode != memory_mode):
|
||||
|
||||
print(f"[SDNQ Sampler] Pipeline cache miss - loading model...")
|
||||
self.pipeline = self.load_pipeline(model_path, dtype)
|
||||
self.pipeline = self.load_pipeline(model_path, dtype, memory_mode)
|
||||
self.current_model_path = model_path
|
||||
self.current_dtype = dtype
|
||||
self.current_memory_mode = memory_mode
|
||||
else:
|
||||
print(f"[SDNQ Sampler] Using cached pipeline")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user