Skip to content

Improve generic version of complex masked store - #1399

Open
serge-sans-paille wants to merge 1 commit into
masterfrom
bug/1391x
Open

Improve generic version of complex masked store#1399
serge-sans-paille wants to merge 1 commit into
masterfrom
bug/1391x

Conversation

@serge-sans-paille

Copy link
Copy Markdown
Contributor

Use the usual kernel mechanism which allows for specialization. Leverage existing masked store mechanism instead of implementing a new one.

Follow-up to #1391

@serge-sans-paille

Copy link
Copy Markdown
Contributor Author

@DiamonDinoia this is the generic implementation I mentioned in #1391

@serge-sans-paille
serge-sans-paille force-pushed the bug/1391x branch 18 times, most recently from bc5c258 to fb39929 Compare August 6, 2026 16:24
@serge-sans-paille

serge-sans-paille commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@DiamonDinoia : (edit: not) ready for review!

@serge-sans-paille
serge-sans-paille force-pushed the bug/1391x branch 3 times, most recently from d862c74 to c17f5af Compare August 7, 2026 07:28
@DiamonDinoia

Copy link
Copy Markdown
Contributor

I did not implement it like this in one go because it is not that trivial as you can see :)

@serge-sans-paille
serge-sans-paille force-pushed the bug/1391x branch 5 times, most recently from 71a0eec to 8f5123e Compare August 14, 2026 17:46
@serge-sans-paille
serge-sans-paille force-pushed the bug/1391x branch 9 times, most recently from 3da3b78 to 3af0f7d Compare August 18, 2026 06:57
@serge-sans-paille

Copy link
Copy Markdown
Contributor Author

@DiamonDinoia should be good for review now

Use the usual kernel mechanism which allows for specialization.
Implement specialization for avx and avx512.

Follow-up to #1391

@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.

These changes have better codegen on my machine for the generic and sse case.

--- a/include/xsimd/arch/common/xsimd_common_memory.hpp
+++ b/include/xsimd/arch/common/xsimd_common_memory.hpp
@@ -457,20 +457,10 @@ namespace xsimd
             // Scalar fallback: only active lanes are touched. Arches with
             // hardware predicated loads should override this.
             constexpr std::size_t size = batch<T, A>::size;
-            alignas(A::alignment()) std::array<T, size> buffer_real;
-            alignas(A::alignment()) std::array<T, size> buffer_imag;
+            alignas(A::alignment()) std::array<std::complex<T>, size> buffer;
             for (std::size_t i = 0; i < size; ++i)
-                if (mask.get(i))
-                {
-                    buffer_real[i] = mem[i].real();
-                    buffer_imag[i] = mem[i].imag();
-                }
-                else
-                {
-                    buffer_real[i] = T(0);
-                    buffer_imag[i] = T(0);
-                }
-            return batch<std::complex<T>, A>::load_aligned(buffer_real.data(), buffer_imag.data());
+                buffer[i] = mask.get(i) ? mem[i] : std::complex<T>(0);
+            return batch<std::complex<T>, A>::load_aligned(buffer.data());
         }
 
         template <class A, class T_in, class T_out, bool... Values, class alignment>
@@ -829,6 +819,40 @@ namespace xsimd
             {
                 static_assert(std::is_same_v<T, void>, "complex_low not implemented for the required architecture");
             }
+
+            // Split a complex mask into the two real masks covering the first and
+            // the second half of memory, by duplicating each lane. A bitmask arch
+            // spreads the lane bits instead;
+            template <class A, class T>
+            XSIMD_INLINE std::array<batch_bool<T, A>, 2> zip_complex_mask_vector(batch_bool<T, A> mask) noexcept
+            {
+                batch<T, A> nmask(mask.to_native());
+                return { batch_bool<T, A>(zip_lo(nmask, nmask).to_native()),
+                         batch_bool<T, A>(zip_hi(nmask, nmask).to_native()) };
+            }
+
+            // A complex batch occupies two real registers, so one masked complex
+            // access is two masked real accesses over the two halves of memory.
+            template <class A, class T, class Mode>
+            XSIMD_INLINE batch<std::complex<T>, A>
+            load_complex_masked_halves(std::complex<T> const* mem, std::array<batch_bool<T, A>, 2> masks, Mode mode) noexcept
+            {
+                batch<T, A> res_lo = batch<T, A>::load(reinterpret_cast<T const*>(mem), masks[0], mode);
+                batch<T, A> res_hi = batch<T, A>::load(reinterpret_cast<T const*>(mem) + batch<T, A>::size, masks[1], mode);
+                return load_complex(res_lo, res_hi, A {});
+            }
+
+            template <class A, class T, class Mode>
+            XSIMD_INLINE void
+            store_complex_masked_halves(std::complex<T>* mem, batch<std::complex<T>, A> const& src, std::array<batch_bool<T, A>, 2> masks, Mode mode) noexcept
+            {
+                batch<T, A> src_lo = complex_low(src, A {});
+                batch<T, A> src_hi = complex_high(src, A {});
+                // Qualified: kernel::detail has its own store_masked overloads, which
+                // would hide the arch dispatchers in kernel.

namespace detail
{
template <class A, class T>
std::array<batch_bool<T, A>, 2> zip_complex_mask(batch_bool<T, A> 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.

this needs XSIMD_INLINE sometimes does not get inlined

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.

2 participants