Skip to content

← Memory, Copies and In-Place step 3 of 6

Easy Primitives

Fill the caller's buffer

def fill(buf, values):
    buf = values          # rebinds the local name. The caller sees nothing.

This is ordinary Python, not a PyTorch quirk: assigning to a parameter rebinds the local name and leaves the object alone. It is worth its own problem here because tensors are exactly the kind of object people expect to behave otherwise, and because PyTorch gives you a real way to do what was meant.

buf.copy_(values)         # writes values into buf's buffer
buf[:] = values           # the same thing

Both write elementwise into the existing storage. The caller’s tensor changes, because it is the same tensor.

copy_ converts

copy_ casts and moves as needed:

cpu_buf.copy_(gpu_tensor)          # device transfer
float_buf.copy_(int_tensor)        # dtype conversion

It broadcasts the source to the destination’s shape too, with the same asymmetry as any in-place op: the destination’s shape is fixed and the source stretches. That is the rule from the broadcasting track, applying again.

Where this shows up

Filling a pinned staging buffer before a transfer. Writing into a slice of a preallocated output. Updating a parameter under no_grad during a custom optimiser step, where rebinding p would leave the optimiser’s registered parameter untouched and the model unchanged.

That last one is a real and frustrating bug: the loss goes down in your arithmetic and the model never moves.

Your task

def fill(buf: torch.Tensor, values: torch.Tensor) -> None

Write values into buf, in place. Return nothing; the caller keeps their own reference and expects it to have changed.