Jonathan Wakely [Thursday, 30 July 2026, 18:25:53 CEST]:
> bool is_leap_neri(short y)
> {
> return (y & (y % 25 == 0 ? 15 : 3)) == 0;
> }
This can be vectorized as 16-bit integers => 32 is_leap evaluations in
parallel with AVX2.
> bool is_leap_hueffner(short y)
> {
> const auto y32 = static_cast<unsigned int>(y) + 32800u;
> return ((y32 * 1073750999u) & 3221352463u) <= 126976u;
> }
This requires 32-bit integers => 16 is_leap evaluations in parallel with AVX2.
Consequently, vectorization provides a 2x *throughput* advantage for neri over
hueffner. But throughput is only one performance aspect. Latency is often more
important. And if I understand your benchmark results correctly (and simply
because of the `% 25` in neri), hueffner wins the latency benchmark.
With std::simd we could have both:
template <typename Abi>
auto is_leap(simd::basic_vec<short, Abi> y)
{
if constexpr (y.size() <= simd::vec<int>::size())
{
simd::rebind_t<unsigned int, simd::basic_vec<short, Abi>> y32 = y;
y += 32800u;
return ((y32 * 1073750999u) & 3221352463u) <= 126976u;
}
else
{
return (y & (y % cw<25> == cw<0> ? cw<15> : cw<3>)) == cw<0>;
}
}
(cw<n> is used because we don't have short literals; alternative: short(n))
Actually, the first branch might be more efficient in throughput even for up
to simd::vec<int>::size() * 2 or more, because of ILP.
Anyway, I'm derailing the topic :-). I believe the proposed change makes sense
if it improves latency. It might be a good idea to add a comment that
throughput is better with Neri's algorithm but that we optimize for latency.
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Center for Heavy Ion Research https://gsi.de
std::simd
──────────────────────────────────────────────────────────────────────────