Guides

    A batch of eight is not eight single runs

    Batch size changes the noise each sample gets. A batched call and eight parallel calls diverge, even with the same seeds. Know when that variance is harmless.

    Versely Team9 min read

    Eight images from one ComfyUI graph at batch size 8 are not eight images from eight single-sample runs. The batch size is part of the noise each sample receives, so the two procedures start from different random fields even when you pass the same eight seeds.

    That is a local-sampler fact. It does not automatically transfer to a hosted API. Versely's generate_images treats num_images as independent jobs (fan-out), capped at four on the agent tool, not as one kernel launch with a batch dimension. Eight parallel HTTP calls are also independent jobs. Mixing those dispatch shapes in one run log is how people convince themselves a seed is broken.

    The working rule: when a result must match, generate it as a single sample — batch size 1 on one device locally, num_images: 1 on Versely — and keep the file. When the variance is just options to pick from, stop worrying about it.

    A wall of monitors showing several images at once

    Batch size is part of the noise

    A seed indexes a pseudo-random generator. That generator is a stream. The stream is consumed in an order that depends on how many latents you draw in one kernel launch.

    Draw one latent, and the generator walks a particular sequence of numbers into that latent. Draw eight at once, and the same generator (even initialised with the "same" seed, even with per-image seed offsets) packs the work differently: fused kernels, different reduction order, a different layout of random values across the batch dimension. The result is not "the same eight pictures in a different order." It is eight different pictures.

    Precision makes this louder. Low-precision dtypes add more run-to-run variance than FP32, and even then PyTorch will not promise a match across devices. A batched low-precision call compared with eight sequential calls of the same dtype is a common worst case. Hardware and dtype are the other half of the same story, covered in seeds and reproducibility. This article is the remaining half: how many samples shared the launch.

    GPU count does the same kind of work. Split a batch across two cards and you have split the RNG state. A two-GPU batch of eight is not a one-GPU batch of eight, and neither is eight single-GPU singles. "One device" in the rule below is load-bearing.

    None of this is about prompt quality. The prompt can be identical. The model name can be identical. The seeds can be the list you wrote down. The numeric path is still different, so the walk through the prompt's landscape is different.

    Parallel calls versus one batched call

    Three dispatch shapes get collapsed into "I asked for eight images":

    A numeric batch. A local graph with batch size 8. One kernel launch. The generator is consumed as a batch. You get eight pictures that are internally consistent with that launch, and they will not match eight singles with the same seeds.

    Eight singles. Eight requests, one image each, maybe in parallel, maybe in a loop. Eight launches. Each launch initialises (or continues) the generator in the single-sample regime. Parallel HTTP calls add a further split: they do not share a batch dimension, they do not share an RNG stream, and on a hosted API they may not even share a GPU.

    A fan-out of singles. One request that asks for several images, implemented as independent jobs. This is what Versely's generate_images does with num_images. The agent tool documents 1–4 images and caps there. Each job is a single-sample draw. A seed forwarded on that call is the same single-sample recipe sent to several independent jobs, not an index into a batched kernel.

    Call shape What it is What the seed means
    Local graph, batch size 8, one seed A numeric batch Indexes that batch launch, not eight independent singles
    generate_images, num_images: 4, one model, one seed Four independent jobs Same seed on four singles, not a batched kernel
    Four requests, num_images: 1, same seed Four singles Each call is a single-sample draw; this is the same kind of work as the fan-out row
    Four requests, num_images: 1, four different seeds Four singles, four starting points Useful exploration
    One request, several named models A model comparison Each model interprets the seed in its own noise space

    Do not assume a Versely num_images call is a ComfyUI batch. It is not. Do not assume a ComfyUI batch of eight is eight independent batch-1 draws. It is not. If a specific take must come back later, generate that take alone.

    This is a different idea from batch generation as a testing workflow, which is about running a matrix of briefs and scoring winners. That kind of batch is "several jobs in one sitting." This article is about the numeric batch size inside a single job, and about not projecting that numeric meaning onto a fan-out API. You can do the marketing kind of batch entirely as singles (and often should, when a winner has to be reproducible). You cannot do the numeric kind of batch and then recreate image 3 as a single without expecting drift.

    A shell loop that fires eight HTTP calls is eight singles. A ComfyUI graph at batch size 8 is a numeric batch. A Versely generate_images call with num_images: 4 is four singles in one request. Write the dispatch so you know which one you used, and put that in the run log next to the seed.

    When the variance is harmless

    Most of the time you wanted eight options, not eight archival plates. Exploring a prompt, ranking hooks, comparing models, or picking a file you will edit anyway: the mismatch is not a bug. Use the cheaper dispatch. Spend attention on scoring, not on whether image 5 would have looked like this at batch size 1.

    The variance is also harmless when you pin nothing else. If dtype, library version and device are already floating, batch size is a rounding error. Get the rest of the bundle under control first. The seeds guide is the iteration pattern; this article is the dispatch shape that pattern still has to survive.

    When to force a single request on one device

    Force batch size 1, one device, one request, when the output has to match something that already exists.

    • A client approved image 3. The deliverable is that file. If you still have the file, do not regenerate it. If you lost the file and only have the seed, a single call on one device with the rest of the bundle pinned is the least-bad attempt, and it still may not match a take that was originally part of a batch of eight. Say that out loud before you promise a recreate.
    • You are debugging a prompt change. Hold batch size 1 so the only variable is the wording. A batched launch plus a wording change is two variables.
    • You are comparing two endpoints. Local versus API, or two model versions. Singles, one device each, same bundle otherwise. Otherwise you will attribute a batch-size artefact to the endpoint.
    • You are about to spend a finishing pass on one take. Upscale, inpaint, print. Finish from the file. If you must regenerate before finishing (wrong resolution, wrong colour space), regenerate that one take as a single.

    The recipe, written as a checklist:

    1. Batch size 1.
    2. One GPU, no data-parallel split.
    3. One request, not a parallel fan-out of the same seed.
    4. Dtype pinned (FP32 if you need the least variance).
    5. Library versions pinned.
    6. Seed, prompt, sampler, steps, guidance, resolution recorded.
    7. The output file stored, because the recipe can still fail.

    On a hosted API you can do 1, 3, 6 and 7. You cannot do 2, 4 and 5. That is why "reproduce last Tuesday's take from the seed" is a weak promise on any vendor endpoint, Versely included — and why it is even weaker if Tuesday's take was a local numeric batch. generate_images with num_images: 1 and a seed is the closest you get on Versely. Use it. Then keep the PNG.

    If you are dispatching from a script, the CLI provisions the key and the loop should make the call shape obvious: one POST per keeper when keepers must be reproducible, a multi-image POST when you are fishing. The text-to-image studio is the same choice in a UI. Ask for one image when it matters.

    Video is worse: less seed support, more numeric sensitivity. Generate the still as a single, then animate that still.

    FAQ

    Why don't eight parallel jobs match a batch of eight?

    When the batch is a numeric one — ComfyUI batch size 8, one kernel launch — batch size changes how the generator is consumed. Eight parallel jobs are eight single-sample launches, possibly on eight different GPUs. Same seeds, different noise, different pictures. A Versely num_images call is not that kind of batch.

    When does the difference actually matter?

    When you need a specific take to come back: a client-approved still, a fair A/B, a debug of one prompt word. When you are picking from options, it does not matter. Rank the files, not the recipes.

    Does asking for several images in one Versely call count as a numeric batch?

    No. generate_images fans num_images out as independent jobs (1–4 on the agent tool). That is not a ComfyUI batch-size launch. Do not expect it to behave like one, and do not expect a local batch of eight to match it. If a take is the one you need again, you should already have the file.

    Can I make a batched run match yesterday's singles?

    Not reliably. You would need the same batch size, device count, dtype and library pin, and even then PyTorch will not guarantee it across hardware. Recreate keepers as singles going forward, and treat yesterday's contact sheet as a set of files, not as a set of seeds you can replay.