Keep the polynomial and RBF Gram epilogues in the input precision - #2536
Open
maxwbuckley wants to merge 1 commit into
Open
Keep the polynomial and RBF Gram epilogues in the input precision#2536maxwbuckley wants to merge 1 commit into
maxwbuckley wants to merge 1 commit into
Conversation
`polynomial_kernel`, `polynomial_kernel_nopad` and `rbf_kernel_expanded` all
evaluate their epilogue in fp64 even when instantiated on `float`:
- `pow(gain * x + offset, exponent)` has `exp_t == int`, so overload
resolution picks `double pow(double, double)` and promotes the whole
expression.
- `exp(-1.0 * gain * (...))` promotes on the `-1.0` literal, and then
resolves to the fp64 `exp`.
`pow` and `exp` are not single instructions; both expand to a polynomial
evaluation, so the promotion multiplies out. Before this change the `float`
instantiations contained 92 and 19 fp64 instructions respectively; after, they
contain none. The `double` instantiations are unchanged (98 and 22).
Casting the exponent to `math_t` and dropping the `-1.0` selects the float
overloads without changing the `double` path.
## Cost of the epilogue
Measured with `KernelFactory::create(...)->evaluate()` on random column-major
inputs, RTX 5090 (sm_120a), CUDA 13.2, median of 5. `linear` is the same GEMM
with no epilogue; `tanh` has the same epilogue structure but no `double`
literal, so both act as controls.
float:
shape kernel before after speedup
8192^2, d=128 linear 0.316 ms 0.315 ms 1.00x (control)
8192^2, d=128 poly 8.669 ms 0.652 ms 13.3x
8192^2, d=128 rbf 2.189 ms 0.746 ms 2.93x
8192^2, d=128 tanh 0.662 ms 0.664 ms 1.00x (control)
16384^2, d=32 linear 0.696 ms 0.695 ms 1.00x (control)
16384^2, d=32 poly 33.952 ms 2.112 ms 16.1x
16384^2, d=32 rbf 7.831 ms 2.198 ms 3.56x
16384^2, d=32 tanh 2.113 ms 2.112 ms 1.00x (control)
4096^2, d=1024 poly 2.725 ms 0.703 ms 3.88x
4096^2, d=1024 rbf 1.160 ms 0.752 ms 1.54x
double is unchanged to within 0.05% on every shape.
Before this change the polynomial epilogue cost 24-43x the GEMM it decorates,
and the `float` path (33.95 ms) was within 1.4x of the `double` path
(47.56 ms). Afterwards poly, tanh and linear+epilogue all land within a few
percent of each other, i.e. the epilogue is memory-bound as it should be.
## Effect on cuML's SVM
cuML's SVC calls these through its kernel cache. Built cuML 26.10 against this
branch (`CPM_cuvs_SOURCE`), blobs data as in cuML's own `bench/sg/svc.cu`,
median of 3, run to convergence. Solver iteration counts are reported so that a
changed convergence path is not mistaken for a speedup.
float, converged before after speedup n_iter
50000x1000 poly 9.43 ms 6.26 ms 1.51x 500 -> 500
50000x1000 rbf 12.04 ms 10.44 ms 1.15x 1319 -> 1319
50000x2 poly 808.60 ms 686.71 ms 1.18x 338727 -> 323526
50000x2 rbf 89.42 ms 62.36 ms 1.43x 10436 -> 9219
50000x1000 linear 4.50 ms 4.38 ms 1.03x 192 -> 192 (control)
50000x1000 tanh 12.89 ms 12.59 ms 1.02x 771 -> 771 (control)
The two 50000x1000 rows converge in an identical number of iterations, so
1.51x and 1.15x are like-for-like. The 50000x2 rows changed iteration count;
per iteration they are 1.12x and 1.27x. The 2048x100000 shape is ~1.01x, since
at d=100000 the GEMM dominates. All `double` SVM configurations are unchanged.
## Numerics
The `float` path now rounds once instead of going through fp64. Measured
against the previous fp64-then-round result over the value range these kernels
actually see (4M samples):
polynomial degree=2 max relative error 1.19e-07 (1 ulp)
polynomial degree=3 max relative error 1.19e-07 (1 ulp)
polynomial degree=4 max relative error 1.19e-07 (1 ulp)
rbf max relative error 1.67e-07 (2 ulp)
`double` results are bit-identical.
Contributor
Author
|
Making SVMs great again @lowener :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
polynomial_kernel,polynomial_kernel_nopadandrbf_kernel_expandedallevaluate their epilogue in fp64 even when instantiated on
float:pow(gain * x + offset, exponent)hasexp_t == int, so overloadresolution picks
double pow(double, double)and promotes the wholeexpression.
exp(-1.0 * gain * (...))promotes on the-1.0literal, and thenresolves to the fp64
exp.powandexpare not single instructions; both expand to a polynomialevaluation, so the promotion multiplies out. Before this change the
floatinstantiations contained 92 and 19 fp64 instructions respectively; after, they
contain none. The
doubleinstantiations are unchanged (98 and 22).Casting the exponent to
math_tand dropping the-1.0selects the floatoverloads without changing the
doublepath.Cost of the epilogue
Measured with
KernelFactory::create(...)->evaluate()on random column-majorinputs, RTX 5090 (sm_120a), CUDA 13.2, median of 5.
linearis the same GEMMwith no epilogue;
tanhhas the same epilogue structure but nodoubleliteral, so both act as controls.
float:
shape kernel before after speedup
8192^2, d=128 linear 0.316 ms 0.315 ms 1.00x (control)
8192^2, d=128 poly 8.669 ms 0.652 ms 13.3x
8192^2, d=128 rbf 2.189 ms 0.746 ms 2.93x
8192^2, d=128 tanh 0.662 ms 0.664 ms 1.00x (control)
16384^2, d=32 linear 0.696 ms 0.695 ms 1.00x (control)
16384^2, d=32 poly 33.952 ms 2.112 ms 16.1x
16384^2, d=32 rbf 7.831 ms 2.198 ms 3.56x
16384^2, d=32 tanh 2.113 ms 2.112 ms 1.00x (control)
4096^2, d=1024 poly 2.725 ms 0.703 ms 3.88x
4096^2, d=1024 rbf 1.160 ms 0.752 ms 1.54x
double is unchanged to within 0.05% on every shape.
Before this change the polynomial epilogue cost 24-43x the GEMM it decorates,
and the
floatpath (33.95 ms) was within 1.4x of thedoublepath(47.56 ms). Afterwards poly, tanh and linear+epilogue all land within a few
percent of each other, i.e. the epilogue is memory-bound as it should be.
Effect on cuML's SVM
cuML's SVC calls these through its kernel cache. Built cuML 26.10 against this
branch (
CPM_cuvs_SOURCE), blobs data as in cuML's ownbench/sg/svc.cu,median of 3, run to convergence. Solver iteration counts are reported so that a
changed convergence path is not mistaken for a speedup.
float, converged before after speedup n_iter
50000x1000 poly 9.43 ms 6.26 ms 1.51x 500 -> 500
50000x1000 rbf 12.04 ms 10.44 ms 1.15x 1319 -> 1319
50000x2 poly 808.60 ms 686.71 ms 1.18x 338727 -> 323526
50000x2 rbf 89.42 ms 62.36 ms 1.43x 10436 -> 9219
50000x1000 linear 4.50 ms 4.38 ms 1.03x 192 -> 192 (control)
50000x1000 tanh 12.89 ms 12.59 ms 1.02x 771 -> 771 (control)
The two 50000x1000 rows converge in an identical number of iterations, so
1.51x and 1.15x are like-for-like. The 50000x2 rows changed iteration count;
per iteration they are 1.12x and 1.27x. The 2048x100000 shape is ~1.01x, since
at d=100000 the GEMM dominates. All
doubleSVM configurations are unchanged.Numerics
The
floatpath now rounds once instead of going through fp64. Measuredagainst the previous fp64-then-round result over the value range these kernels
actually see (4M samples):
polynomial degree=2 max relative error 1.19e-07 (1 ulp)
polynomial degree=3 max relative error 1.19e-07 (1 ulp)
polynomial degree=4 max relative error 1.19e-07 (1 ulp)
rbf max relative error 1.67e-07 (2 ulp)
doubleresults are bit-identical.