Skip to content

← Shapes and Strides step 8 of 9

Medium Primitives

Windows that overlap and cost nothing

Split a sequence into overlapping windows:

[0 1 2 3 4 5 6 7]   window 3, step 2

[0 1 2]
    [2 3 4]
        [4 5 6]
            [6 7 ?]   dropped, incomplete

The obvious implementation stacks slices and allocates a tensor roughly window / step times the size of the input. For a long signal that is the dominant cost of the whole pipeline.

Nothing says a stride cannot revisit memory

A view is a shape and a stride over a buffer. There is no rule that two elements of the view must be different elements of the buffer. So:

buffer   0 1 2 3 4 5 6 7
shape    (4, 3)
stride   (2, 1)

Row i, column j reads element 2i + j. Row 0 reads 0,1,2. Row 1 reads 2,3,4. Element 2 appears in both rows, stored once.

unfold builds exactly this:

x.unfold(0, size=3, step=2)     # shape (4, 3), stride (2, 1), same buffer

The catch worth knowing

Because entries alias, writing to the result writes through to several places at once, and reductions that assume independence are wrong. Read from it freely; treat it as read-only unless you have thought hard.

This is the mechanism behind convolution’s im2col, every rolling statistic, and every chunked-attention implementation you will read.

Your task

def windows(x: torch.Tensor, size: int, step: int) -> torch.Tensor

Return the overlapping windows of a 1-D tensor without copying the buffer. Drop any trailing incomplete window.

The starter stacks slices, which is correct and allocates.