Execution Model, Queue & Cache — why the graph does not run left to right
ComfyUI executes a dependency graph, not a drawing on the canvas. Screen position does not define execution order: runtime resolves topological dependencies from requested outputs, reuses valid cache entries, and recomputes only what became dirty or no longer matches its input signature.
Canvas layout helps people read the graph, but it does not define execution order
A node on the right can run only after all of its actual upstream dependencies are ready. A node on the left does not have to participate in the current execution simply because it appears “earlier” on screen if the selected output route does not require it.
Professional layout matters for human readability, but runtime truth lives in the links and dependency graph.
ExecutionList resolves dependencies, not node coordinates
In current ComfyUI, ExecutionList is built on top of a topological sort. Before a node can run, runtime must have the values produced by the upstream nodes it depends on.
This is why a large canvas can be arranged freely into groups and columns: visual order is for the person; dependency order is for the engine.
ComfyUI implementation
comfy_execution/graph.py describes ExecutionList as a topological dissolve of the graph and tracks the staged node, dependencies and cached values separately.
The engine starts from the required result and resolves its upstream requirements
The execution engine builds a list of output targets and adds them to the execution list. It then expands the dependencies required to produce those outputs. This is a useful debugging model: if you want to know why a branch executes, find the output that depends on it.
This matters especially in production workflows with previews, saves and alternative branches: being present in the JSON does not prove that a node is required by the current result path.
Queueing again does not necessarily mean recomputing the whole graph
ComfyUI stores intermediate outputs and can reuse a cached result on the next run when the node’s input signature and dependency context remain valid.
execution.py collects cached nodes separately and reports them to the client through the execution_cached event. This is not “skipped work”; it is normal graph optimization.
| What changed | Expected behavior |
|---|---|
| Nothing relevant changed | Most of the route may be served from cache |
| An upstream parameter changed | That node and its dependent downstream route require recomputation |
| An unrelated branch changed | An independent output route may keep its valid cache |
| A node reports its own IS_CHANGED / fingerprint | Cache validity incorporates that signal |
Cache identity depends on more than the node ID — inputs and ancestry matter
The current caching implementation builds a signature from class type, change fingerprint and inputs. For linked inputs, the signature includes the ancestor and socket, and ancestry is traversed deterministically.
A key production consequence is that changing a master control upstream can invalidate several downstream stages even when you edited only a small control node.
CacheKeySetInputSignature
ComfyUI caching.py includes the immediate node signature and ordered ancestry in the cache key for input-signature caching.
Evaluate a change by its downstream impact radius
If a shared seed, working resolution or selector high in the control plane changes, the impact radius can be large. If you change a local Color Match after a finished cutout, recomputation is usually limited to a later portion of the route.
This is another reason to design workflows modularly: good boundaries reduce the recalculation area and make debugging easier to reason about.
Queue Prompt requests a graph state; it does not mean “execute every node”
Queue sends runtime a description of the required graph state. The engine then validates dependencies, identifies cached nodes and executes only the missing part of the route.
Two runs of the same workflow can therefore take very different amounts of time: the first may build heavy intermediates, while the next can reuse a substantial part of the previous work.
When behavior looks strange, ask three questions: selected? required? cached?
These three questions separate routing problems from execution problems. Only after that is it useful to investigate model loading, sampler, masks or VRAM.
| Question | What to verify |
|---|---|
| SELECTED? | Does the selector / switch actually route through this branch? |
| REQUIRED? | Is there a current output that depends on this branch? |
| CACHED? | Did the node execute again, or did runtime reuse a stored output? |
How this changes the way you read the large Hansen workflow
- Do not read 252 nodes as a left-to-right list — choose a specific output/checkpoint first.
- Walk upstream from that output and record only the required route.
- At every selector, identify the selected branch.
- Mark bypassed modules separately so they do not enter the effective runtime map.
- After Queue, observe which checkpoints updated and which remained cached.
- When testing, change one variable and evaluate its downstream impact radius.
Practice: prove cache and dependency execution on a small graph
- Run a simple route to Preview/Save and record the time of the first run.
- Change nothing and Queue again; note which nodes runtime considers cached.
- Change one late parameter and observe how short the recalculated tail becomes.
- Change one early shared control and compare the impact radius.
- Switch a selector to an alternative branch and observe how the required ancestry changes.
- Explain in words which node became dirty first and why downstream had to be recomputed.