ns = [32, 64, 128, 256, 512, 1024]
K = 100
budget_s = 5.0 # stop a sweep once one gradient takes longer than this
gpu_sync = _ -> CUDA.synchronize()
function sweep(variant, device, loss, grad; prep=identity, sync=identity, allocated)
records = []
for (k, n) in enumerate(ns)
θ, uobs = prep.(inputs(Float64, n))
compile_s = k == 1 ? @elapsed(sync(grad(θ, uobs, K))) : nothing
samples = n >= 512 ? 3 : 10
r = (; dtype="Float64", variant, N=n^2, n, K,
forward_s = time_median(() -> loss(θ, uobs, K); sync, samples),
grad_s = time_median(() -> grad(θ, uobs, K); sync, samples),
compile_s, peak_mem_bytes = allocated(() -> grad(θ, uobs, K)))
push!(records, r)
r.grad_s > budget_s && break
end
return records
end
# Memory allocated by one gradient call (host for CPU, device for GPU).
host_alloc(f) = @allocated f()
dev_alloc(f) = CUDA.@allocated f()
cpu = vcat(sweep("lexicographic", "cpu", gs_loss, gs_gradient; allocated=host_alloc),
sweep("red-black", "cpu", rb_loss, rb_gradient; allocated=host_alloc))
gpu = sweep("red-black", "gpu", rb_loss, rb_gradient; prep=CuArray, sync=gpu_sync,
allocated=dev_alloc)
# Gradient memory against the number of sweeps K, at n = 256.
θm, uobsm = inputs(Float64, 256)
for Km in (10, 100, 1000)
push!(cpu, (; dtype="Float64", variant="lexicographic", N=256^2, n=256, K=Km,
forward_s=nothing, grad_s=nothing, compile_s=nothing,
peak_mem_bytes=@allocated(gs_gradient(θm, uobsm, Km))))
end
write_results(EXAMPLE, "enzyme", "cpu", cpu)
write_results(EXAMPLE, "enzyme", "gpu", gpu)
for (dev, rs) in (("cpu", cpu), ("gpu", gpu)), r in rs
r.grad_s === nothing && continue
@printf "%s %-13s n=%-5d forward %9.3f ms gradient %9.3f ms memory %8.2f MiB\n" dev r.variant r.n 1e3r.forward_s 1e3r.grad_s r.peak_mem_bytes / 2^20
end