Start Timer
Find out which node is eating your generation time
- any_1
- any_2
- passthrough
- start_seconds
- start_time
ComfyUI tells you total runtime per image, but it won't tell you whether your sampler is the bottleneck or something upstream is. When you're chasing down why a workflow takes forever - or you just want to know what a video model actually costs per slice - you want a stopwatch on a specific stretch of the graph. Start Timer is the starting half of that stopwatch, and it does one clever thing to make it accurate.
How it works
ComfyUI runs nodes in dependency order, not left-to-right, so you can't just eyeball "the middle of the graph." The timer solves this with a data dependency: Start Timer passes any value through untouched, and if you wire the end of a slice into Stop Timer, the engine is forced to run Start → slice → Stop in exactly that order. The start timestamp travels to Stop as a plain number (start_seconds), and Stop just computes now - start.
The part most people miss is the any_1 input's tooltip: the timer starts only after ALL connected inputs are ready. So tap every branch feeding your sampler - noise, guider, sampler, sigmas, latent - and the clock starts right before the sampler runs, not the moment the first branch resolves. That's the difference between measuring the sampler and measuring your whole graph.
Both timer nodes return an always-dirty IS_CHANGED (NaN), which is the same trick the KB's node-plumbing doc describes: it forces the node to re-execute every run so the timestamp is real. The tradeoff is that the wrapped slice is recomputed on every run with no caching while the timers are active. Mute or bypass them when you're not measuring - otherwise you're paying for a stopwatch on every queue, forever.
Inputs
any_1- the main line, passes through unchanged. This is what feeds your slice.time_format- a strftime pattern for thestart_timestring output. Default%Y-%m-%d %H:%M:%S; you'll rarely need to touch it.any_2(optional) - extra dependency taps. More slots appear as you connect. They only gate when the timer starts;any_1is the only thing passed through.
Outputs
passthrough- your value, untouched. Wire it (or a copy of the slice) onward.start_seconds- epoch seconds as a float. This is what feedsStop Timer'sstart_secondsinput.start_time- the formatted human string, if you want a timestamp in a log or report.
Install
It's part of the Kinburg-Nodes pack: install via ComfyUI Manager (search "Kinburg-Nodes") or cd ComfyUI/custom_nodes && git clone https://github.com/Kinburg/Kinburg-Nodes then restart. No extra dependencies - this node and its Stop Timer sibling are plain Python with nothing to download.
The gotcha worth knowing
Because the timers force re-execution, they quietly defeat ComfyUI's caching for everything between Start and Stop. Leave them wired in permanently and your workflow re-runs its whole slice every time even if nothing changed. The intended pattern is: add both timers while you're profiling, read the number, then mute them. It's a measuring tool, not a permanent citizen of your graph.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| any_1 | * | Main line — passes through unchanged. The timer starts only after ALL connected inputs are ready, so tap every branch feeding your sampler (noise / guider / sampler / sigmas / latent) to start right before it runs, not as soon as one branch is ready. | |
| time_format | STRING | %Y-%m-%d %H:%M:%S | strftime pattern for the 'start_time' string output. |
| any_2opt | * | Extra dependency taps — more slots appear as you connect. They only gate WHEN the timer starts; just any_1 is passed through. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| passthrough | * | — |
| start_seconds | FLOAT | — |
| start_time | STRING | — |