Skip to content

Commit

Permalink
Non-const processOne cannot be called in a SIMD-loop
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Kretz <[email protected]>
  • Loading branch information
mattkretz committed Sep 25, 2024
1 parent fbfdfcb commit acf66e3
Showing 1 changed file with 37 additions and 22 deletions.
59 changes: 37 additions & 22 deletions core/include/gnuradio-4.0/Block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1585,29 +1585,44 @@ class Block : public lifecycle::StateMachine<Derived>, public std::tuple<Argumen
} else {
constexpr bool kIsSourceBlock = TInputTypes::size() == 0;
constexpr std::size_t kMaxWidth = stdx::simd_abi::max_fixed_size<double>;
// A block determines it's simd::size() via its input types. However, a source block doesn't have any
// input types and therefore wouldn't be able to produce simd output on processOne calls. To overcome
// this limitation, a source block can implement `processOne_simd(vir::constexpr_value auto width)`
// instead of `processOne()` and then return simd objects with simd::size() == width.
constexpr bool kIsSimdSourceBlock = kIsSourceBlock and requires(Derived& d) { d.processOne_simd(vir::cw<kMaxWidth>); };
if constexpr (kIsSimdSourceBlock or traits::block::can_processOne_simd<Derived>) {
// SIMD loop
constexpr auto kWidth = [&] {
if constexpr (kIsSourceBlock)
return vir::cw<kMaxWidth>;
else
return vir::cw<std::min(kMaxWidth, vir::simdize<typename TInputTypes::template apply<std::tuple>>::size() * std::size_t(4))>;
}();
invokeUserProvidedFunction("invokeProcessOneSimd", [&userReturnStatus, &inputSpans, &outputSpans, &kWidth, &processedIn, this] noexcept(HasNoexceptProcessOneFunction<Derived>) { userReturnStatus = invokeProcessOneSimd(inputSpans, outputSpans, kWidth, processedIn); });
} else { // Non-SIMD loop
if constexpr (HasConstProcessOneFunction<Derived>) { // processOne is const -> can process whole batch similar to SIMD-ised call
invokeUserProvidedFunction("invokeProcessOnePure", [&userReturnStatus, &inputSpans, &outputSpans, &processedIn, this] noexcept(HasNoexceptProcessOneFunction<Derived>) { userReturnStatus = invokeProcessOnePure(inputSpans, outputSpans, processedIn); });
} else { // processOne isn't const i.e. not a pure function w/o side effects -> need to evaluate state after each sample
const auto result = invokeProcessOneNonConst(inputSpans, outputSpans, processedIn);
userReturnStatus = result.status;
processedIn = result.processedIn;
processedOut = result.processedOut;
// A block determines it's simd::size() via its input types. However, a source block doesn't have
// any input types and therefore wouldn't be able to produce simd output on processOne calls. To
// overcome this limitation, a source block can implement `processOne_simd(vir::constexpr_value auto
// width)` instead of `processOne()` and then return simd objects with simd::size() == width.
constexpr bool kIsSimdSourceBlock =
kIsSourceBlock and requires(Derived& d) { d.processOne_simd(vir::cw<kMaxWidth>); };
if constexpr (HasConstProcessOneFunction<Derived>) { // processOne is const -> can process whole batch
// similar to SIMD-ised call
if constexpr (kIsSimdSourceBlock or traits::block::can_processOne_simd<Derived>) {
// SIMD loop
constexpr auto kWidth = [&] {
if constexpr (kIsSourceBlock)
return vir::cw<kMaxWidth>;
else
return vir::cw<std::min(kMaxWidth, vir::simdize<typename TInputTypes::template apply<std::tuple>>::size() * std::size_t(4))>;
}();
invokeUserProvidedFunction("invokeProcessOneSimd",
[&userReturnStatus, &inputSpans, &outputSpans, &kWidth, &processedIn, this] noexcept(
HasNoexceptProcessOneFunction<Derived>) {
userReturnStatus = invokeProcessOneSimd(inputSpans, outputSpans, kWidth, processedIn);
});
} else { // Non-SIMD loop
invokeUserProvidedFunction("invokeProcessOnePure",
[&userReturnStatus, &inputSpans, &outputSpans, &processedIn, this] noexcept(
HasNoexceptProcessOneFunction<Derived>) {
userReturnStatus = invokeProcessOnePure(inputSpans, outputSpans, processedIn);
});
}
} else { // processOne isn't const i.e. not a pure function w/o side effects -> need to evaluate state
// after each sample
static_assert(not kIsSimdSourceBlock and not traits::block::can_processOne_simd<Derived>,
"A non-const processOne function cannot be called with SIMD arguments. This function signature "
"is contradicting itself. Either mark the function 'const' or have it not accept SIMD argument "
"types.");
const auto result = invokeProcessOneNonConst(inputSpans, outputSpans, processedIn);
userReturnStatus = result.status;
processedIn = result.processedIn;
processedOut = result.processedOut;
}
}
} else { // block does not define any valid processing function
Expand Down

0 comments on commit acf66e3

Please sign in to comment.