From fd90854b69d619c8fe5eca3b666e4e2a3b3c968f Mon Sep 17 00:00:00 2001 From: Oscar Amoros Huguet Date: Sun, 2 Aug 2026 21:52:47 +0200 Subject: [PATCH 1/4] Adding divergent batch test and circular batch test, passing both for CPU and GPU. Pending to add circular tensor test --- .../execution_model/data_parallel_patterns.h | 86 +++++++++++---- .../core/execution_model/executors.h | 50 +++++++++ tests/data/test_circular_tensor.h | 19 ++++ .../test_divergent_batch.h | 104 ++++++++++++++++++ tests/operation/test_cricular_batch.h | 90 +++++++++++++++ 5 files changed, 329 insertions(+), 20 deletions(-) create mode 100644 tests/data/test_circular_tensor.h create mode 100644 tests/data_parallel_patterns/test_divergent_batch.h create mode 100644 tests/operation/test_cricular_batch.h diff --git a/include/fused_kernel/core/execution_model/data_parallel_patterns.h b/include/fused_kernel/core/execution_model/data_parallel_patterns.h index 6c39c3b7..55241a4f 100644 --- a/include/fused_kernel/core/execution_model/data_parallel_patterns.h +++ b/include/fused_kernel/core/execution_model/data_parallel_patterns.h @@ -217,6 +217,18 @@ namespace fk { // namespace FusedKernel return Parent::getActiveThreads(details, iOp); } + // Executes the work of a single thread. The caller is responsible for providing + // the thread coordinates, which allows other DPPs (like DivergentBatchTransformDPP) + // to reuse this implementation while owning the index generation. + template + FK_DEVICE_FUSE void exec_thread(const Point& thread, const Details& details, const IOps&... iOps) { + const ActiveThreads activeThreads = getActiveThreads(details, get_arg<0>(iOps...)); + + if (thread.x < activeThreads.x && thread.y < activeThreads.y) { + Parent::execute_thread(thread, activeThreads, iOps...); + } + } + template FK_DEVICE_FUSE void exec(const Details& details, const IOps&... iOps) { const cg::thread_block g = cg::this_thread_block(); @@ -226,11 +238,7 @@ namespace fk { // namespace FusedKernel const int z = g.group_index().z; // So far we only consider the option of using the z dimension to specify n (x*y) thread planes const Point thread{ x, y, z }; - const ActiveThreads activeThreads = getActiveThreads(details, get_arg<0>(iOps...)); - - if (x < activeThreads.x && y < activeThreads.y) { - Parent::execute_thread(thread, activeThreads, iOps...); - } + exec_thread(thread, details, iOps...); } }; #endif // defined(__NVCC__) @@ -248,16 +256,27 @@ namespace fk { // namespace FusedKernel return Parent::getActiveThreads(details, iOp); } + // Executes the work of a single thread. The caller is responsible for providing + // the thread coordinates, which allows other DPPs (like DivergentBatchTransformDPP) + // to reuse this implementation while owning the index generation. + template + FK_HOST_FUSE void exec_thread(const Point& thread, const Details& details, const IOps&... iOps) { + const ActiveThreads activeThreads = getActiveThreads(details, get_arg<0>(iOps...)); + + if (thread.x < activeThreads.x && thread.y < activeThreads.y) { + Parent::execute_thread(thread, activeThreads, iOps...); + } + } + template FK_HOST_FUSE void exec(const Details& details, const IOps&... iOps) { - using TFI = typename Details::TFI; const ActiveThreads activeThreads = getActiveThreads(details, get_arg<0>(iOps...)); for (int z = 0; z < activeThreads.z; ++z) { for (int y = 0; y < activeThreads.y; ++y) { for (int x = 0; x < activeThreads.x; ++x) { const Point thread{ x, y, z }; - Parent::execute_thread(thread, activeThreads, iOps...); + exec_thread(thread, details, iOps...); } } } @@ -267,25 +286,47 @@ namespace fk { // namespace FusedKernel template struct DivergentBatchTransformDPP; - template + template struct DivergentBatchTransformDPPBase { friend struct DivergentBatchTransformDPP; // Allow DivergentBatchTransformDPP to access private members friend struct DivergentBatchTransformDPP; // Allow DivergentBatchTransformDPPBase to access private members private: template - FK_HOST_DEVICE_FUSE void launchTransformDPP(const IOps&... iOps) { + FK_HOST_DEVICE_FUSE void launchTransformDPP(const Point& thread, const IOps&... iOps) { using Details = TransformDPPDetails; - TransformDPP::exec(Details{}, iOps...); + using TDPP = TransformDPP; + if constexpr (PA == ParArch::CPU) { + // On CPU there is no thread grid: the x and y indices are generated here, + // from the geometry of this sequence, while the plane index comes from the caller. + const ActiveThreads activeThreads = TDPP::getActiveThreads(Details{}, get_arg<0>(iOps...)); + for (int y = 0; y < static_cast(activeThreads.y); ++y) { + for (int x = 0; x < static_cast(activeThreads.x); ++x) { + TDPP::exec_thread(Point{ x, y, thread.z }, Details{}, iOps...); + } + } + } else { + TDPP::exec_thread(thread, Details{}, iOps...); + } } + // Functor used to expand the IOp tuple of an operation sequence while carrying + // the thread coordinates, which are not part of the tuple. + template + struct LaunchTransformDPPForThread { + Point thread; + FK_HOST_DEVICE_CNST void operator()(const IOps&... iOps) const { + launchTransformDPP(thread, iOps...); + } + }; + template - FK_HOST_DEVICE_FUSE void divergent_operate(const uint z, + FK_HOST_DEVICE_FUSE void divergent_operate(const Point& thread, const InstantiableOperationSequence& iOpSequence, const IOpSequenceTypes&... iOpSequences) { - if (OpSequenceNumber == SequenceSelector::at(z)) { - apply_d(launchTransformDPP, iOpSequence.iOps); + if (OpSequenceNumber == SequenceSelector::at(thread.z)) { + apply_d(LaunchTransformDPPForThread{ thread }, iOpSequence.iOps); } else if constexpr (sizeof...(iOpSequences) > 0) { - divergent_operate(z, iOpSequences...); + divergent_operate(thread, iOpSequences...); } } }; @@ -305,7 +346,7 @@ namespace fk { // namespace FusedKernel template struct DivergentBatchTransformDPP { private: - using Parent = DivergentBatchTransformDPPBase; + using Parent = DivergentBatchTransformDPPBase; public: using DPPDetails = DivergentBatchTransformDPPDetails; static constexpr ParArch PAR_ARCH = ParArch::GPU_NVIDIA; @@ -313,23 +354,28 @@ namespace fk { // namespace FusedKernel FK_DEVICE_FUSE void exec(const DPPDetails&, const IOpSequenceTypes&... iOpSequences) { const cg::thread_block g = cg::this_thread_block(); - const uint z = g.group_index().z; - Parent::template divergent_operate<0>(z, iOpSequences...); + const int x = (g.dim_threads().x * g.group_index().x) + g.thread_index().x; + const int y = (g.dim_threads().y * g.group_index().y) + g.thread_index().y; + const int z = g.group_index().z; + const Point thread{ x, y, z }; + + Parent::template divergent_operate<0>(thread, iOpSequences...); } }; #endif // defined(__NVCC__) template struct DivergentBatchTransformDPP { private: - using Parent = DivergentBatchTransformDPPBase; + using Parent = DivergentBatchTransformDPPBase; public: using DPPDetails = DivergentBatchTransformDPPDetails; static constexpr ParArch PAR_ARCH = ParArch::CPU; template FK_DEVICE_FUSE void exec(const DPPDetails& details, const IOpSequenceTypes&... iOpSequences) { - for (uint z = 0; z < details.numPlanes; ++z) { - Parent::template divergent_operate<0>(z, iOpSequences...); + for (int z = 0; z < static_cast(details.numPlanes); ++z) { + const Point thread{ 0, 0, z }; + Parent::template divergent_operate<0>(thread, iOpSequences...); } } }; diff --git a/include/fused_kernel/core/execution_model/executors.h b/include/fused_kernel/core/execution_model/executors.h index d33375e5..d0f93839 100644 --- a/include/fused_kernel/core/execution_model/executors.h +++ b/include/fused_kernel/core/execution_model/executors.h @@ -188,6 +188,56 @@ FK_HOST_FUSE void executeOperations(const std::array, Batch>& input, co DECLARE_EXECUTOR_PARENT_IMPL }; + template + struct Executor> { + private: + using DPPType = DivergentBatchTransformDPP; + using DPPDetails = typename DPPType::DPPDetails; + using SelfType = Executor; + + template + FK_HOST_FUSE ActiveThreads getActiveThreads(const IOpSequenceTypes &...iOpSequences) { + const uint x = cxp::max::f(get<0>(iOpSequences.iOps).getActiveThreads().x...); + const uint y = cxp::max::f(get<0>(iOpSequences.iOps).getActiveThreads().y...); + const uint z = cxp::sum::f(get<0>(iOpSequences.iOps).getActiveThreads().z...); + return ActiveThreads{x, y, z}; + } + + template + FK_HOST_FUSE auto fuseBackSequence(const IOpSequence &iOpSeq) { + return buildOperationSequence_tup(apply( + [](auto &&...args) { + // Now fuse_back deduces the types naturally and preserves value categories via perfect forwarding + return BackFuser::fuse_back(std::forward(args)...); + }, + iOpSeq.iOps)); + } + + template + FK_HOST_FUSE void executeOperationsFused(Stream_ &stream, + const IOpSequenceTypes &...iOpSequences) { + const ActiveThreads activeThreads = getActiveThreads(iOpSequences...); + const DPPDetails details{ .numPlanes = activeThreads.z }; + + DivergentBatchTransformDPP::exec(details, iOpSequences...); + } + + template + FK_HOST_FUSE void executeOperations_helper(Stream_ &stream, + const IOpSequenceTypes &...iOpSequences) { + executeOperationsFused(stream, fuseBackSequence(iOpSequences)...); + } + + public: + FK_STATIC_STRUCT(Executor, SelfType) + FK_HOST_FUSE ParArch parArch() { return ParArch::CPU; } + template + FK_HOST_FUSE void executeOperations(Stream_ &stream, + const IOpSequenceTypes &...iOpSequences) { + executeOperations_helper(stream, iOpSequences...); + } + }; + #if defined(__NVCC__) struct ComputeBestSolutionBase { FK_HOST_FUSE uint computeDiscardedThreads(const uint width, const uint height, const uint blockDimx, const uint blockDimy) { diff --git a/tests/data/test_circular_tensor.h b/tests/data/test_circular_tensor.h new file mode 100644 index 00000000..1d8331cd --- /dev/null +++ b/tests/data/test_circular_tensor.h @@ -0,0 +1,19 @@ +/* Copyright 2026 Oscar Amoros Huguet + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "tests/main.h" + +int launch() { + return 0; +} \ No newline at end of file diff --git a/tests/data_parallel_patterns/test_divergent_batch.h b/tests/data_parallel_patterns/test_divergent_batch.h new file mode 100644 index 00000000..a781a459 --- /dev/null +++ b/tests/data_parallel_patterns/test_divergent_batch.h @@ -0,0 +1,104 @@ +/* Copyright 2023 Mediaproduccion S.L.U. (Oscar Amoros Huguet) + Copyright 2025-2026 Oscar Amoros Huguet + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "tests/main.h" + +#include +#include +#include +#include +#include + +struct OneToOne { + FK_DEVICE_FUSE uint at(const uint &zIdx) { return zIdx; } +}; + +bool testDivergentBatch() { + constexpr uint WIDTH = 32; + constexpr uint HEIGHT = 32; + constexpr uint BATCH = 2; + constexpr uint VAL_SUM = 3; + + fk::Stream stream; + + std::vector> inputAllocations; + std::array, BATCH> input; + fk::Tensor output; + fk::Tensor h_groundTruth; + output.allocTensor(WIDTH, HEIGHT, BATCH); + h_groundTruth.allocTensor(WIDTH, HEIGHT, BATCH, 1, fk::MemType::Host); + + for (uint i = 0; i < BATCH; i++) { + fk::Ptr2D temp(WIDTH, HEIGHT); + fk::setTo(i, temp, stream); + inputAllocations.push_back(temp); + input[i] = temp; + } + + for (int z = 0; z < BATCH; z++) { + if (z == 0) { + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < HEIGHT; x++) { + const fk::Point p{x, y, z}; + *fk::PtrAccessor::point(p, h_groundTruth.ptr()) = VAL_SUM; + } + } + } else { + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < HEIGHT; x++) { + const fk::Point p{x, y, z}; + *fk::PtrAccessor::point(p, h_groundTruth.ptr()) = z; + } + } + } + } + + auto opSeq1 = fk::buildOperationSequence(fk::Read>{input[0]}, + fk::Binary>{VAL_SUM}, + fk::Write>{output.ptr()}); + auto opSeq2 = fk::buildOperationSequence(fk::Read>{input[1]}, + fk::Write>{output.ptr()}); + + fk::executeOperations>(stream, opSeq1, opSeq2); + + output.download(stream); + stream.sync(); + + bool correct = true; + for (int z = 0; z < BATCH; z++) { + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + const fk::Point p{x, y, z}; + const uint gt = *fk::PtrAccessor::point(p, h_groundTruth.ptr()); + const uint res = *fk::PtrAccessor::point(p, output.ptrPinned()); + correct &= gt == res; + } + } + } + + return correct; +} + +int launch() { + int returnValue = 0; + if (testDivergentBatch()) { + std::cout << "testDivergentBatch OK" << std::endl; + } else { + std::cout << "testDivergentBatch Failed!" << std::endl; + throw std::runtime_error("Test failed!"); + returnValue = -1; + } + return returnValue; +} \ No newline at end of file diff --git a/tests/operation/test_cricular_batch.h b/tests/operation/test_cricular_batch.h new file mode 100644 index 00000000..65b008ec --- /dev/null +++ b/tests/operation/test_cricular_batch.h @@ -0,0 +1,90 @@ +/* Copyright 2023 Mediaproduccion S.L.U. (Oscar Amoros Huguet) + Copyright 2025-2026 Oscar Amoros Huguet + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "tests/main.h" + +#include +#include + +#include + +bool testCircularBatchRead() { + constexpr uint WIDTH = 32; + constexpr uint HEIGHT = 32; + constexpr uint BATCH = 15; + constexpr uint FIRST = 4; + + fk::Stream stream; + fk::Stream fk_stream(stream); + + std::vector> inputAllocations; + std::array, BATCH> input; + fk::Tensor output; + + for (int i = 0; i < BATCH; i++) { + fk::Ptr2D temp(WIDTH, HEIGHT, 0); + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + const fk::Point p{x, y, 0}; + *fk::PtrAccessor::point(p, temp.ptrPinned()) = fk::make_(i, i, i); + } + } + temp.upload(stream); + inputAllocations.push_back(temp); + input[i] = temp; + } + output.allocTensor(WIDTH, HEIGHT, BATCH); + + fk::Read, BATCH>> + circularBatchRead; + circularBatchRead.params.first = FIRST; + for (int i = 0; i < BATCH; i++) { + circularBatchRead.params.opData[i].params = input[i]; + } + fk::Write> write3D{{output}}; + + fk::executeOperations>(fk_stream, circularBatchRead, write3D); + + output.download(stream); + stream.sync(); + + bool correct = true; + for (int z = 0; z < BATCH; z++) { + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + fk::Point p{x, y, z}; + uchar3 res = *fk::PtrAccessor::point(p, output.ptrPinned()); + uchar newZ = (z + FIRST); + uchar3 gt = newZ >= BATCH ? fk::make_set(newZ - BATCH) : fk::make_set(newZ); + correct &= res.x == gt.x; + correct &= res.y == gt.y; + correct &= res.z == gt.z; + } + } + } + + return correct; +} + +int launch() { + int returnValue = 0; + if (testCircularBatchRead()) { + std::cout << "testCircularBatchRead OK" << std::endl; + } else { + std::cout << "testCircularBatchRead Failed!" << std::endl; + returnValue = -1; + } + return returnValue; +} \ No newline at end of file From 1838493233178900229304c657fb7fb0ff79cbf6 Mon Sep 17 00:00:00 2001 From: Oscar Amoros Huguet Date: Tue, 4 Aug 2026 00:41:33 +0200 Subject: [PATCH 2/4] Fixed issue with CircularTensor. Not a definitive solution, but works --- .../algorithms/basic_ops/memory_operations.h | 9 ++- .../fused_kernel/core/data/circular_tensor.h | 19 ++++- tests/data/test_circular_tensor.h | 72 ++++++++++++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/include/fused_kernel/algorithms/basic_ops/memory_operations.h b/include/fused_kernel/algorithms/basic_ops/memory_operations.h index 82a330c8..919801fc 100644 --- a/include/fused_kernel/algorithms/basic_ops/memory_operations.h +++ b/include/fused_kernel/algorithms/basic_ops/memory_operations.h @@ -532,7 +532,14 @@ namespace fk { } FK_HOST_DEVICE_FUSE uint num_elems_z(const Point thread, const OperationDataType& opData) { - return BATCH; + // Report the planes of the view we were actually given, not the compile-time BATCH. + // BATCH stays the modulus used by computeCircularThreadIdx for the circular wrap-around, + // but it is not necessarily the number of planes this read is responsible for: + // CircularTensor::update narrows the copy sequence to BATCH - 1 planes so that the plane + // space of the DivergentBatchTransformDPP (the sum of the planes declared by each + // sequence) adds up to BATCH. Returning BATCH here made that total BATCH + 1 and let a + // thread address a plane past the end of the Tensor. + return Operation::num_elems_z(thread, opData.params.opData); } FK_HOST_DEVICE_FUSE uint pitch(const Point thread, const OperationDataType& opData) { diff --git a/include/fused_kernel/core/data/circular_tensor.h b/include/fused_kernel/core/data/circular_tensor.h index 50feb046..e6ecba17 100644 --- a/include/fused_kernel/core/data/circular_tensor.h +++ b/include/fused_kernel/core/data/circular_tensor.h @@ -120,6 +120,15 @@ namespace fk { } using equivalentReadDFType = EquivalentType_t; + // The DivergentBatchTransformDPP defines a global plane space whose size is the sum of the + // planes declared by each sequence. Both sequences write into the same output Tensor, but + // each one owns a different subset of its planes: the update sequence produces a single + // plane (the newly inserted image) and the copy sequence produces the remaining BATCH - 1. + // Therefore each sequence must be given a view of the data that reports its own number of + // planes, so that the total adds up to BATCH and no thread addresses a plane past the end. + // The update sequence already declares a single plane through its 2D read. + constexpr uint COPY_PLANES = static_cast(BATCH) - 1u; + MidWrite> updateWriteToTemp; updateWriteToTemp.params.first = m_nextUpdateIdx; updateWriteToTemp.params.opData.params = m_tempTensor.ptr(); @@ -130,8 +139,16 @@ namespace fk { equivalentReadDFType nonUpdateRead; nonUpdateRead.params.first = m_nextUpdateIdx; nonUpdateRead.params.opData.params = m_tempTensor.ptr(); + nonUpdateRead.params.opData.params.dims.planes = COPY_PLANES; + + // The copy sequence writes into the same output Tensor as the update sequence, using the + // global plane index, so it needs the full view: narrowing dims.planes here would be + // misleading. Write operations take no part in the thread space (the DPP derives it from + // the first IOp of the sequence, i.e. the read) and TensorSplit/TensorWrite address the + // data through the pitch/plane_pitch strides, never through dims.planes. + const auto nonUpdateWrite = writeInstantiableOperation; - const auto copyOps = buildOperationSequence(nonUpdateRead, writeInstantiableOperation); + const auto copyOps = buildOperationSequence(nonUpdateRead, nonUpdateWrite); if (PA == ParArch::GPU_NVIDIA && !(this->type == MemType::Device || this->type == MemType::DeviceAndPinned)) { throw std::runtime_error("CircularTensor operations on Device memory only supported \ diff --git a/tests/data/test_circular_tensor.h b/tests/data/test_circular_tensor.h index 1d8331cd..42422f64 100644 --- a/tests/data/test_circular_tensor.h +++ b/tests/data/test_circular_tensor.h @@ -14,6 +14,76 @@ #include "tests/main.h" +#include +#include +#include +#include +#include + +#include + +template +bool testCircularTensor() { + using TensorOT = typename fk::VectorTraits::base; + constexpr uint COLOR_PLANES = fk::cn; + + fk::CircularTensor + myTensor(WIDTH, HEIGHT); + fk::Ptr2D input(WIDTH, HEIGHT); + + fk::Stream fk_stream; + fk::setTo(10.0f, myTensor, fk_stream); + + for (int i = 0; i < ITERS; i++) { + fk::setTo(fk::make_(i + 1, i + 1, i + 1), input, fk_stream); + myTensor.update(fk_stream, fk::Read>{input.ptr()}, + fk::Unary>{}, fk::Write>{myTensor.ptr()}); + fk_stream.sync(); + } + + myTensor.download(fk_stream); + fk_stream.sync(); + + bool correct = true; + for (int z = 0; z < BATCH; z++) { + const TensorOT value = (TensorOT)(ITERS - z); + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + const fk::Point p{x, y, z}; + const TensorOT res = *fk::PtrAccessor::point(p, myTensor.ptrPinned()); + correct &= value == res; + } + } + } + + return correct; +} + +template +void launchTest() { + if (testCircularTensor()) { + std::cout << "testCircularTensor<" << BATCH << ", " << WIDTH << ", " << HEIGHT << ", " << ITERS << ", " << typeid(IT).name() << ", " << typeid(OT).name() << "> OK" << std::endl; + } else { + std::cout << "testCircularTensor<" << BATCH << ", " << WIDTH << ", " << HEIGHT << ", " << ITERS << ", " << typeid(IT).name() << ", " << typeid(OT).name() << "> Failed!" + << std::endl; + } +} + int launch() { - return 0; + int returnValue = 0; + launchTest<2, 128, 128, 100, uchar3, float3>(); + launchTest<3, 128, 128, 100, uchar3, float3>(); + launchTest<4, 128, 128, 100, uchar3, float3>(); + launchTest<5, 128, 128, 100, uchar3, float3>(); + launchTest<6, 128, 128, 100, uchar3, float3>(); + launchTest<7, 128, 128, 100, uchar3, float3>(); + launchTest<8, 128, 128, 100, uchar3, float3>(); + launchTest<9, 128, 128, 100, uchar3, float3>(); + launchTest<10, 128, 128, 100, uchar3, float3>(); + launchTest<11, 128, 128, 100, uchar3, float3>(); + launchTest<12, 128, 128, 100, uchar3, float3>(); + launchTest<13, 128, 128, 100, uchar3, float3>(); + launchTest<14, 128, 128, 100, uchar3, float3>(); + launchTest<15, 128, 128, 100, uchar3, float3>(); + return returnValue; } \ No newline at end of file From f75602b424f07b50ba47ea1f24b09314c8fcf92b Mon Sep 17 00:00:00 2001 From: Oscar Amoros Huguet Date: Tue, 4 Aug 2026 00:50:22 +0200 Subject: [PATCH 3/4] Improved tests --- tests/data/test_circular_tensor.h | 36 ++++++++++++----------- tests/operation/test_cricular_batch.h | 41 +++++++++++++++++++-------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/tests/data/test_circular_tensor.h b/tests/data/test_circular_tensor.h index 42422f64..a840e32e 100644 --- a/tests/data/test_circular_tensor.h +++ b/tests/data/test_circular_tensor.h @@ -60,30 +60,32 @@ bool testCircularTensor() { } template -void launchTest() { +bool launchTest() { if (testCircularTensor()) { std::cout << "testCircularTensor<" << BATCH << ", " << WIDTH << ", " << HEIGHT << ", " << ITERS << ", " << typeid(IT).name() << ", " << typeid(OT).name() << "> OK" << std::endl; + return true; } else { std::cout << "testCircularTensor<" << BATCH << ", " << WIDTH << ", " << HEIGHT << ", " << ITERS << ", " << typeid(IT).name() << ", " << typeid(OT).name() << "> Failed!" << std::endl; + return false; } } int launch() { - int returnValue = 0; - launchTest<2, 128, 128, 100, uchar3, float3>(); - launchTest<3, 128, 128, 100, uchar3, float3>(); - launchTest<4, 128, 128, 100, uchar3, float3>(); - launchTest<5, 128, 128, 100, uchar3, float3>(); - launchTest<6, 128, 128, 100, uchar3, float3>(); - launchTest<7, 128, 128, 100, uchar3, float3>(); - launchTest<8, 128, 128, 100, uchar3, float3>(); - launchTest<9, 128, 128, 100, uchar3, float3>(); - launchTest<10, 128, 128, 100, uchar3, float3>(); - launchTest<11, 128, 128, 100, uchar3, float3>(); - launchTest<12, 128, 128, 100, uchar3, float3>(); - launchTest<13, 128, 128, 100, uchar3, float3>(); - launchTest<14, 128, 128, 100, uchar3, float3>(); - launchTest<15, 128, 128, 100, uchar3, float3>(); - return returnValue; + bool correct = true; + correct &= launchTest<2, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<3, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<4, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<5, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<6, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<7, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<8, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<9, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<10, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<11, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<12, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<13, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<14, 128, 128, 100, uchar3, float3>(); + correct &= launchTest<15, 128, 128, 100, uchar3, float3>(); + return correct ? 0 : -1; } \ No newline at end of file diff --git a/tests/operation/test_cricular_batch.h b/tests/operation/test_cricular_batch.h index 65b008ec..f055f03c 100644 --- a/tests/operation/test_cricular_batch.h +++ b/tests/operation/test_cricular_batch.h @@ -20,12 +20,8 @@ #include +template bool testCircularBatchRead() { - constexpr uint WIDTH = 32; - constexpr uint HEIGHT = 32; - constexpr uint BATCH = 15; - constexpr uint FIRST = 4; - fk::Stream stream; fk::Stream fk_stream(stream); @@ -78,13 +74,34 @@ bool testCircularBatchRead() { return correct; } -int launch() { - int returnValue = 0; - if (testCircularBatchRead()) { - std::cout << "testCircularBatchRead OK" << std::endl; +template +bool launchTestCircularBatchRead() { + if (testCircularBatchRead()) { + std::cout << "testCircularBatchRead<" << WIDTH << ", " << HEIGHT << ", " << BATCH << ", " << FIRST << "> OK" + << std::endl; + return true; } else { - std::cout << "testCircularBatchRead Failed!" << std::endl; - returnValue = -1; + std::cout << "testCircularBatchRead<" << WIDTH << ", " << HEIGHT << ", " << BATCH << ", " << FIRST + << "> Failed!" << std::endl; + return false; } - return returnValue; +} + +int launch() { + bool correct = true; + correct &= launchTestCircularBatchRead<32, 32, 2, 0>(); + correct &= launchTestCircularBatchRead<32, 32, 3, 2>(); + correct &= launchTestCircularBatchRead<32, 32, 4, 2>(); + correct &= launchTestCircularBatchRead<32, 32, 5, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 6, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 7, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 8, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 9, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 10, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 11, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 12, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 13, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 14, 4>(); + correct &= launchTestCircularBatchRead<32, 32, 15, 4>(); + return correct ? 0 : -1; } \ No newline at end of file From 17d58b58d0855214ee02a5db5f72a8e6542e8913 Mon Sep 17 00:00:00 2001 From: Oscar Amoros Huguet Date: Tue, 4 Aug 2026 01:00:17 +0200 Subject: [PATCH 4/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/data_parallel_patterns/test_divergent_batch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data_parallel_patterns/test_divergent_batch.h b/tests/data_parallel_patterns/test_divergent_batch.h index a781a459..8f17b8a7 100644 --- a/tests/data_parallel_patterns/test_divergent_batch.h +++ b/tests/data_parallel_patterns/test_divergent_batch.h @@ -50,7 +50,7 @@ bool testDivergentBatch() { for (int z = 0; z < BATCH; z++) { if (z == 0) { for (int y = 0; y < HEIGHT; y++) { - for (int x = 0; x < HEIGHT; x++) { + for (int x = 0; x < WIDTH; x++) { const fk::Point p{x, y, z}; *fk::PtrAccessor::point(p, h_groundTruth.ptr()) = VAL_SUM; }