Skip to content

Use vpermd for AVX2 dynamic 32/64-bit swizzle - #1446

Merged
DiamonDinoia merged 1 commit into
xtensor-stack:masterfrom
mapleFU:avx2-dynamic-swizzle-vpermd
Nov 12, 2026
Merged

DiamonDinoia merged 1 commit into
xtensor-stack:masterfrom
mapleFU:avx2-dynamic-swizzle-vpermd

Conversation

@mapleFU

@mapleFU mapleFU commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

#1141 routed the AVX2 dynamic 32/64-bit swizzle to the AVX fallback (vperm2f128 + 2x vpermilps + blendv), on the assumption that vpermps does not accept repeated indices. Both the Intel SDM and the AMD APM state that it does, and the constant-mask variant already went back to vpermd in #1201. Restore the single-instruction lowering for the dynamic mask too:

  • 32-bit: one vpermps / vpermd instead of 7 instructions
  • 64-bit: widen index i to the pair (2i, 2i + 1) with moveldup + add,
    then vpermd: 4 instructions instead of 11

Out-of-range indices keep wrapping modulo the batch size, as before.

Add a swizzle test with runtime index vectors that repeat lanes: every single-lane broadcast, all N^N patterns when N <= 4, and pseudo-random patterns otherwise.

Fixes #1445

Comment thread include/xsimd/arch/xsimd_avx2.hpp Resolved
@DiamonDinoia

Copy link
Copy Markdown
Contributor

Can you remove Claude as co author? I am okay with Assisted by AI. As a marker at least it does not pollute the stats :)

@mapleFU
mapleFU force-pushed the avx2-dynamic-swizzle-vpermd branch from eca94e5 to 63c540c Compare November 7, 2026 22:12
Comment thread test/test_batch_manip.cpp Resolved
Comment thread test/test_batch_manip.cpp
XSIMD_SWIZZLE_PATTERN_CASE(OddThenEven);
XSIMD_SWIZZLE_PATTERN_CASE(InterleavePairs);
// Runtime index vectors with duplicates:
SUBCASE("dynamic_duplicates") { swizzle_test<B>().dynamic_duplicates(); }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use the macro above for consistency

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Emm this is a subcase and didn't calls xsimd::fill_pattern 🤔, so should we just leave subcase? This just verify the case for duplicate, which is mentioned eariler

Comment thread test/test_batch_manip.cpp Outdated
{
std::size_t count = 1;
for (std::size_t i = 0; i < N; ++i)
count *= N;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that's std::accumulate, but you could also use the direct formula

@mapleFU

mapleFU commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

I've a naive question, I just use mask here, but some arch naming it indices. I may think indices is better in this scenerio ( I'm freshman in SIMD syntax), would you mind give some advice?

@mapleFU
mapleFU force-pushed the avx2-dynamic-swizzle-vpermd branch from 63c540c to a12dbdd Compare November 10, 2026 17:27
@DiamonDinoia

DiamonDinoia commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

can we add a test for out of range?

Comment thread include/xsimd/arch/xsimd_avx2.hpp Outdated
XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch<uint64_t, A> const& mask, requires_arch<avx2>) noexcept
{
// A 64 bits index i selects the 32 bits pair (2i, 2i + 1).
__m256i lo = _mm256_castps_si256(_mm256_moveldup_ps(_mm256_castsi256_ps(mask)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am not a fan of this triple cast + sum:
how about: _mm256_shuffle_epi32(mask, _MM_SHUFFLE(2, 2, 0, 0)) + _mm256_setr_epi32(0, 1, 0, 1, 0, 1, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

--- a/include/xsimd/arch/xsimd_avx2.hpp
+++ b/include/xsimd/arch/xsimd_avx2.hpp
@@ -1314,8 +1314,8 @@ namespace xsimd
         XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch<uint64_t, A> const& mask, requires_arch<avx2>) noexcept
         {
             // A 64 bits index i selects the 32 bits pair (2i, 2i + 1).
-            __m256i lo = _mm256_castps_si256(_mm256_moveldup_ps(_mm256_castsi256_ps(mask)));
-            __m256i idx = _mm256_add_epi32(_mm256_add_epi32(lo, lo), _mm256_set1_epi64x(int64_t(1) << 32));
+            __m256i lo = _mm256_shuffle_epi32(mask, _MM_SHUFFLE(2, 2, 0, 0));
+            __m256i idx = _mm256_add_epi32(_mm256_add_epi32(lo, lo), _mm256_setr_epi32(0, 1, 0, 1, 0, 1, 0, 1));
             return _mm256_permutevar8x32_epi32(self, idx);
         }

@DiamonDinoia

Copy link
Copy Markdown
Contributor

I've a naive question, I just use mask here, but some arch naming it indices. I may think indices is better in this scenerio ( I'm freshman in SIMD syntax), would you mind give some advice?

I am not sure about what is the general rule. I personally think of masking as a boolean. Wheter is a simple simd register or boolean or the single bits in an integer. I think of indices when I am doing a swizzle type operation where the numbers are integer not just booleans.

But, it is not a general rule. As long as it is clear anything is fine. The golden rule of naming I guess is answering the questions What is that?!

@mapleFU
mapleFU force-pushed the avx2-dynamic-swizzle-vpermd branch 2 times, most recently from 56011b2 to ba6533d Compare November 11, 2026 14:45

@DiamonDinoia DiamonDinoia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One simplification of the tests then good to go.

Comment thread test/test_batch_manip.cpp Resolved
PR xtensor-stack#1141 routed the AVX2 dynamic 32/64-bit swizzle to the AVX fallback
(vperm2f128 + 2x vpermilps + blendv), on the assumption that vpermps
does not accept repeated indices. Both the Intel SDM and the AMD APM
state that it does, and the constant-mask variant already went back to
vpermd in xtensor-stack#1201. Restore the single-instruction lowering for the
dynamic indices too:

- 32-bit: one vpermps / vpermd instead of 7 instructions
- 64-bit: widen index i to the pair (2i, 2i + 1) with vpshufd + add,
  then vpermd: 4 instructions instead of 11

Out-of-range indices keep wrapping modulo the batch size, as before.

Add a swizzle test with runtime index vectors that repeat lanes: every
single-lane broadcast and deterministic mt19937 patterns.

Fixes xtensor-stack#1445
@mapleFU
mapleFU force-pushed the avx2-dynamic-swizzle-vpermd branch from ba6533d to eb3869d Compare November 12, 2026 17:11
@DiamonDinoia
DiamonDinoia merged commit 0298d37 into xtensor-stack:master Nov 12, 2026
98 checks passed
@mapleFU
mapleFU deleted the avx2-dynamic-swizzle-vpermd branch November 13, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve AVX2 32/64B swizzle and revert part of pr1141

3 participants