Not explicitly SIMD for a single evaluation (because the idea is still that you’d SIMD across multiple evaluations), but SLEEFPirates.estrin uses the estrin method, scaling much better for multiple evaluations:
julia> coef = ntuple(_ -> randn(), Val(30));
julia> packed = packpoly1x4r(coef);
julia> @btime evalpoly1x4r($(Ref(1.2))[], $packed)
4.644 ns (0 allocations: 0 bytes)
-204.5524827611573
julia> @btime SLEEFPirates.estrin($(Ref(1.2))[], $coef)
5.437 ns (0 allocations: 0 bytes)
-204.5524827611573
julia> @btime evalpoly($(Ref(1.2))[], $coef)
12.540 ns (0 allocations: 0 bytes)
-204.5524827611572
But SIMDPoly won for all the numbers of coefficients I tried when it comes to speeding up a single evaluation.
Types are known at compile time, so in that example what was known is evalpoly(::Int, ::NTuple{3,Int}).
If we define a function f such that everything is known:
julia> f() = evalpoly(1.0, (1,2,3))
f (generic function with 1 method)
julia> @code_typed f()
CodeInfo(
1 ─ return 6.0
) => Float64
If we make the coefficients known:
julia> f(x) = evalpoly(x, (1,2,3))
f (generic function with 2 methods)
julia> @code_typed f(2)
CodeInfo(
1 ─ %1 = Base.mul_int(x, 3)::Int64
│ %2 = Base.add_int(%1, 2)::Int64
│ %3 = Base.mul_int(x, %2)::Int64
│ %4 = Base.add_int(%3, 1)::Int64
└── return %4
) => Int64
Etc.
If the number of coefficients is unknown but large, the compiler would unroll by some constant amount (dependent on the particular machine it’s compiling for) to make good use of the CPU’s execution resources.