Skip to content

Add IndexOfMin and IndexOfMax to hwy/contrib/algo - #3253

Merged
copybara-service[bot] merged 1 commit into
google:masterfrom
Wint3rNight:index-of-min-max
Aug 10, 2026
Merged

Add IndexOfMin and IndexOfMax to hwy/contrib/algo#3253
copybara-service[bot] merged 1 commit into
google:masterfrom
Wint3rNight:index-of-min-max

Conversation

@Wint3rNight

Copy link
Copy Markdown
Contributor

From the op wishlist. minmax-inl.h already has MinValue/MaxValue which return the value, so these return where it is. Ties go to the lowest index, matching std::min_element.

Each lane keeps its best value plus the block it came from, then a final pass picks the smallest value, earliest block, lowest lane. Lanes hold the block number rather than the element index so it fits in the lane type — for 8-bit lanes that only reaches 255 blocks, so the loop works in segments and carries the base across. Wider lanes run as a single segment.

Roughly 10x std::min_element on 4M floats (1.46 ms vs 14.31 ms), and about 2x the cost of MinValue — the extra compare and two selects per vector, plus this isn't unrolled the way MinValue is. Happy to add the 4x unroll if you'd like it in the same PR.

Tests follow the existing minmax_value_test.cc pattern against a scalar reference, plus one that walks a single extreme across a span long enough to cross a segment boundary on 8-bit lanes.

@jan-wassenberg jan-wassenberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! We can land as-is, I've left two minor suggestions which might help a tiny bit. More important would be to 2x unroll. Could be done in a follow-up if you like.

IfThenElse(RebindMask(du, is_min), blocks, Set(du, LimitsMax<TU>()));
const TU min_block = ReduceMin(du, cand);
const Mask<D> winners =
And(is_min, RebindMask(d, Eq(blocks, Set(du, min_block))));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: could be RebindMask(d, MaskedEq(RebindMask(du, is_min), blocks, Set(du, min_block))).

const T seg_min = ReduceMin(d, acc);
const Mask<D> is_min = Eq(acc, Set(d, seg_min));
const VFromD<decltype(du)> cand =
IfThenElse(RebindMask(du, is_min), blocks, Set(du, LimitsMax<TU>()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could also be IfThenElseZero, then we use ReduceMax.

@copybara-service
copybara-service Bot merged commit ddbba34 into google:master Aug 10, 2026
25 of 52 checks passed
@Wint3rNight

Copy link
Copy Markdown
Contributor Author

Thanks! I'll do a follow-up with both suggestions and the 2x unroll.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants