Kwker

Train faster on the CPU

install() also speeds up the parts of a training loop that run outside the model's forward pass. The optimizer step is the largest: torch runs about eight small kernels per parameter tensor from a Python loop, and Kwker runs the whole step in one pass. On 4 cores, an AdamW step over 1.3 million parameters took 0.63 ms instead of 3.3 ms (5.2x), and an SGD step over 11 million parameters 2.0 ms instead of 6.3 ms (3.2x). The parameters come out bit for bit the same.

Prerequisites

Step 1: a training loop

A small model, a fixed seed and twenty steps:

PythonNeeds torch: runs on your machine.
import torch
import kwker.torch_ops as sto

def train(seed=0, steps=20):
    torch.manual_seed(seed)
    model = torch.nn.Sequential(torch.nn.Linear(64, 128), torch.nn.ReLU(), torch.nn.Linear(128, 10))
    opt = torch.optim.AdamW(model.parameters(), lr=1e-3)
    x, y = torch.randn(256, 64), torch.randint(0, 10, (256,))
    for _ in range(steps):
        opt.zero_grad()
        torch.nn.functional.cross_entropy(model(x), y).backward()
        opt.step()
    return model

reference = train()                    # torch's own optimizer step
print(type(reference[0]).__name__)
Output
Linear

Step 2: the same loop with Kwker

PythonRuns on your machine.
sto.install()
fast = train()                         # the same code, Kwker's fused optimizer step
sto.uninstall()
print(all(torch.equal(a, b) for a, b in zip(reference.parameters(), fast.parameters())))
Output
True

Every parameter is equal to the bit. The fused step follows torch's own rounding, including its square root.

What else gets faster

Step 3: measure your own training script

ShellOn your machine.
python -m kwker audit -- train.py --epochs 1

It runs the script several times with and without Kwker, alternating, and reports the wall time, the CPU time and whether the outputs agree.

Next steps