diff --git a/HeterogeneousTest/AlpakaDevice/BuildFile.xml b/HeterogeneousTest/AlpakaDevice/BuildFile.xml new file mode 100644 index 0000000000000..33da29f0df749 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/BuildFile.xml @@ -0,0 +1,2 @@ + + diff --git a/HeterogeneousTest/AlpakaDevice/README.md b/HeterogeneousTest/AlpakaDevice/README.md new file mode 100644 index 0000000000000..4815e1a1f0200 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/README.md @@ -0,0 +1,49 @@ +# Introduction + +The packages `HeterogeneousTest/AlpakaDevice`, `HeterogeneousTest/AlpakaKernel`, +`HeterogeneousTest/AlpakaWrapper` and `HeterogeneousTest/AlpakaOpaque` implement a set of libraries, +plugins and tests to exercise the build rules for Alpaka. +In particular, these tests show what is supported and what are the limitations implementing +Alpaka-based libraries, and using them from multiple plugins. + + +# `HeterogeneousTest/AlpakaDevice` + +The package `HeterogeneousTest/AlpakaDevice` implements a library that defines and exports Alpaka +device-side functions: +```c++ +namespace cms::alpakatest { + + template + ALPAKA_FN_ACC void add_vectors_f(TAcc const& acc, ...); + + template + ALPAKA_FN_ACC void add_vectors_d(TAcc const& acc, ...); + +} // namespace cms::alpakatest +``` + +The `plugins` directory implements the `AlpakaTestDeviceAdditionModule` `EDAnalyzer` that launches +an Alpaka kernel using the functions defined in ths library. As a byproduct this plugin also shows +how to split an `EDAnalyzer` or other framework plugin into a host-only part (in a `.cc` file) and +a device part (in a `.dev.cc` file). + +The `test` directory implements the `testAlpakaDeviceAddition` binary that launches a Alpaka kernel +using these functions. +It also contains the `testAlpakaTestDeviceAdditionModule.py` python configuration to exercise the +`AlpakaTestDeviceAdditionModule` plugin. + + +# Other packages + +For various ways in which this library and plugin can be tested, see also the other +`HeterogeneousTest/Alpaka...` packages: + - [`HeterogeneousTest/AlpakaKernel/README.md`](../../HeterogeneousTest/AlpakaKernel/README.md) + - [`HeterogeneousTest/AlpakaWrapper/README.md`](../../HeterogeneousTest/AlpakaWrapper/README.md) + - [`HeterogeneousTest/AlpakaOpaque/README.md`](../../HeterogeneousTest/AlpakaOpaque/README.md) + + +# Combining plugins + +`HeterogeneousTest/AlpakaOpaque/test` contains the `testAlpakaTestAdditionModules.py` python +configuration that exercise all four plugins in a single application. diff --git a/HeterogeneousTest/AlpakaDevice/interface/alpaka/DeviceAddition.h b/HeterogeneousTest/AlpakaDevice/interface/alpaka/DeviceAddition.h new file mode 100644 index 0000000000000..9a38952b2c2b1 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/interface/alpaka/DeviceAddition.h @@ -0,0 +1,36 @@ +#ifndef HeterogeneousTest_AlpakaDevice_interface_alpaka_DeviceAddition_h +#define HeterogeneousTest_AlpakaDevice_interface_alpaka_DeviceAddition_h + +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" + +namespace cms::alpakatest { + + template + ALPAKA_FN_ACC void add_vectors_f(TAcc const& acc, + float const* __restrict__ in1, + float const* __restrict__ in2, + float* __restrict__ out, + uint32_t size) { + for (auto i : cms::alpakatools::uniform_elements(acc, size)) { + out[i] = in1[i] + in2[i]; + } + } + + template + ALPAKA_FN_ACC void add_vectors_d(TAcc const& acc, + double const* __restrict__ in1, + double const* __restrict__ in2, + double* __restrict__ out, + uint32_t size) { + for (auto i : cms::alpakatools::uniform_elements(acc, size)) { + out[i] = in1[i] + in2[i]; + } + } + +} // namespace cms::alpakatest + +#endif // HeterogeneousTest_AlpakaDevice_interface_alpaka_DeviceAddition_h diff --git a/HeterogeneousTest/AlpakaDevice/plugins/BuildFile.xml b/HeterogeneousTest/AlpakaDevice/plugins/BuildFile.xml new file mode 100644 index 0000000000000..7601109f77e70 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/plugins/BuildFile.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionAlgo.dev.cc b/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionAlgo.dev.cc new file mode 100644 index 0000000000000..e8e899e354ac1 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionAlgo.dev.cc @@ -0,0 +1,32 @@ +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" +#include "HeterogeneousTest/AlpakaDevice/interface/alpaka/DeviceAddition.h" + +#include "AlpakaTestDeviceAdditionAlgo.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaDevicePlugins { + + struct KernelAddVectorsF { + template + ALPAKA_FN_ACC void operator()(TAcc const& acc, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size) const { + cms::alpakatest::add_vectors_f(acc, in1, in2, out, size); + } + }; + + void wrapper_add_vectors_f(Queue& queue, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size) { + alpaka::exec(queue, cms::alpakatools::make_workdiv(32, 32), KernelAddVectorsF{}, in1, in2, out, size); + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaDevicePlugins diff --git a/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionAlgo.h b/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionAlgo.h new file mode 100644 index 0000000000000..3bac24f61d5f8 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionAlgo.h @@ -0,0 +1,18 @@ +#ifndef HeterogeneousTest_AlpakaDevice_plugins_alpaka_AlpakaTestDeviceAdditionAlgo_h +#define HeterogeneousTest_AlpakaDevice_plugins_alpaka_AlpakaTestDeviceAdditionAlgo_h + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaDevicePlugins { + + void wrapper_add_vectors_f(Queue& queue, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size); + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaDevicePlugins + +#endif // HeterogeneousTest_AlpakaDevice_plugins_alpaka_AlpakaTestDeviceAdditionAlgo_h diff --git a/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionModule.cc b/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionModule.cc new file mode 100644 index 0000000000000..3ce3b451ef851 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/plugins/alpaka/AlpakaTestDeviceAdditionModule.cc @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +#include + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousCore/AlpakaServices/interface/alpaka/AlpakaService.h" + +#include "AlpakaTestDeviceAdditionAlgo.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE { + + class AlpakaTestDeviceAdditionModule : public edm::global::EDAnalyzer<> { + public: + explicit AlpakaTestDeviceAdditionModule(edm::ParameterSet const& config); + ~AlpakaTestDeviceAdditionModule() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + void analyze(edm::StreamID, edm::Event const& event, edm::EventSetup const& setup) const override; + + private: + const uint32_t size_; + }; + + AlpakaTestDeviceAdditionModule::AlpakaTestDeviceAdditionModule(edm::ParameterSet const& config) + : size_(config.getParameter("size")) {} + + void AlpakaTestDeviceAdditionModule::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("size", 1024 * 1024); + + // ignore the alpaka = cms.untracked.PSet(...) injected by the framework + edm::ParameterSetDescription alpaka; + alpaka.setAllowAnything(); + desc.addUntracked("alpaka", alpaka); + + descriptions.addWithDefaultLabel(desc); + } + + void AlpakaTestDeviceAdditionModule::analyze(edm::StreamID, + edm::Event const& event, + edm::EventSetup const& setup) const { + // require a valid Alpaka backend for running + edm::Service service; + if (not service or not service->enabled()) { + std::cout << "The " << ALPAKA_TYPE_ALIAS_NAME(AlpakaService) + << " is not available or disabled, the test will be skipped.\n"; + return; + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // allocate input and output host buffers + std::vector in1_h(size_); + std::vector in2_h(size_); + std::vector out_h(size_); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size_; ++i) { + in1_h[i] = dist(rand); + in2_h[i] = dist(rand); + out_h[i] = 0.; + } + + // run the test on all available devices + for (auto const& device : cms::alpakatools::devices()) { + Queue queue{device}; + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size_); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size_); + auto out_d = cms::alpakatools::make_device_buffer(queue, size_); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size_); + alpaka::memcpy(queue, in2_d, in2_h, size_); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + HeterogeneousTestAlpakaDevicePlugins::wrapper_add_vectors_f( + queue, in1_d.data(), in2_d.data(), out_d.data(), size_); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d); + + // wait for all the operations to complete + alpaka::wait(queue); + + // check the results + for (uint32_t i = 0; i < size_; ++i) { + float sum = in1_h[i] + in2_h[i]; + assert(out_h[i] < sum + epsilon); + assert(out_h[i] > sum - epsilon); + } + } + + std::cout << "All tests passed.\n"; + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE + +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/MakerMacros.h" +DEFINE_FWK_ALPAKA_MODULE(AlpakaTestDeviceAdditionModule); diff --git a/HeterogeneousTest/AlpakaDevice/test/BuildFile.xml b/HeterogeneousTest/AlpakaDevice/test/BuildFile.xml new file mode 100644 index 0000000000000..b8b03a57abc91 --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/test/BuildFile.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaDevice/test/alpaka/testDeviceAddition.dev.cc b/HeterogeneousTest/AlpakaDevice/test/alpaka/testDeviceAddition.dev.cc new file mode 100644 index 0000000000000..b73cd5b74279c --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/test/alpaka/testDeviceAddition.dev.cc @@ -0,0 +1,102 @@ +#include +#include +#include + +#define CATCH_CONFIG_MAIN +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/devices.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" +#include "HeterogeneousTest/AlpakaDevice/interface/alpaka/DeviceAddition.h" + +using namespace ALPAKA_ACCELERATOR_NAMESPACE; + +struct KernelAddVectorsF { + template + ALPAKA_FN_ACC void operator()(TAcc const& acc, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size) const { + cms::alpakatest::add_vectors_f(acc, in1, in2, out, size); + } +}; + +TEST_CASE("HeterogeneousTest/AlpakaDevice test", "[alpakaTestDeviceAddition]") { + auto const& devices = cms::alpakatools::devices(); + if (devices.empty()) { + FAIL("No devices available for the " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend, " + "the test will be skipped."); + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // buffer size + constexpr uint32_t size = 1024 * 1024; + + // allocate input and output host buffers + std::vector in1_h(size); + std::vector in2_h(size); + std::vector out_h(size); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size; ++i) { + in1_h[i] = dist(rand); + in2_h[i] = dist(rand); + out_h[i] = 0.; + } + + // run the test on all available devices + for (auto const& device : cms::alpakatools::devices()) { + SECTION("Test add_vectors_f on " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend") { + REQUIRE_NOTHROW([&]() { + Queue queue{device}; + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size); + auto out_d = cms::alpakatools::make_device_buffer(queue, size); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size); + alpaka::memcpy(queue, in2_d, in2_h, size); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + alpaka::exec(queue, + cms::alpakatools::make_workdiv(32, 32), + KernelAddVectorsF{}, + in1_d.data(), + in2_d.data(), + out_d.data(), + size); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d, size); + + // wait for all the operations to complete + alpaka::wait(queue); + }()); + + // check the results + for (uint32_t i = 0; i < size; ++i) { + float sum = in1_h[i] + in2_h[i]; + CHECK_THAT(out_h[i], Catch::Matchers::WithinAbs(sum, epsilon)); + } + } + } +} diff --git a/HeterogeneousTest/AlpakaDevice/test/testAlpakaTestDeviceAdditionModule.py b/HeterogeneousTest/AlpakaDevice/test/testAlpakaTestDeviceAdditionModule.py new file mode 100644 index 0000000000000..79c7b0e9c51db --- /dev/null +++ b/HeterogeneousTest/AlpakaDevice/test/testAlpakaTestDeviceAdditionModule.py @@ -0,0 +1,15 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process('TestAlpakaTestDeviceAdditionModule') +process.load('Configuration.StandardSequences.Accelerators_cff') +process.load('HeterogeneousCore.AlpakaCore.ProcessAcceleratorAlpaka_cfi') + +process.source = cms.Source('EmptySource') + +process.alpakaTestDeviceAdditionModule = cms.EDAnalyzer('AlpakaTestDeviceAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.path = cms.Path(process.alpakaTestDeviceAdditionModule) + +process.maxEvents.input = 1 diff --git a/HeterogeneousTest/AlpakaKernel/BuildFile.xml b/HeterogeneousTest/AlpakaKernel/BuildFile.xml new file mode 100644 index 0000000000000..dbd2e61ca61ea --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/BuildFile.xml @@ -0,0 +1,3 @@ + + + diff --git a/HeterogeneousTest/AlpakaKernel/README.md b/HeterogeneousTest/AlpakaKernel/README.md new file mode 100644 index 0000000000000..fd87ed2cf0d01 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/README.md @@ -0,0 +1,53 @@ +# Introduction + +The packages `HeterogeneousTest/AlpakaDevice`, `HeterogeneousTest/AlpakaKernel`, +`HeterogeneousTest/AlpakaWrapper` and `HeterogeneousTest/AlpakaOpaque` implement a set of libraries, +plugins and tests to exercise the build rules for Alpaka. +In particular, these tests show what is supported and what are the limitations implementing +Alpaka-based libraries, and using them from multiple plugins. + + +# `HeterogeneousTest/AlpakaKernel` + +The package `HeterogeneousTest/AlpakaKernel` implements a library that defines and exports Alpaka +kernels that call the device functions defined in the `HeterogeneousTest/AlpakaDevice` library: +```c++ +namespace cms::alpakatest { + + struct KernelAddVectorsF { + template + ALPAKA_FN_ACC void operator()(TAcc const& acc, ...) const; + }; + + struct KernelAddVectorsD { + template + ALPAKA_FN_ACC void operator()(TAcc const& acc, ...) const; + }; + +} // namespace cms::alpakatest +``` + +The `plugins` directory implements the `AlpakaTestKernelAdditionModule` `EDAnalyzer` that launches +the Alpaka kernels defined in this library. As a byproduct this plugin also shows how to split an +`EDAnalyzer` or other framework plugin into a host-only part (in a `.cc` file) and a device part (in +a `.dev.cc` file). + +The `test` directory implements the `testAlpakaKernelAddition` test binary that launches the Alpaka +kernel defined in this library. +It also contains the `testAlpakaTestKernelAdditionModule.py` python configuration to exercise the +`AlpakaTestKernelAdditionModule` module. + + +# Other packages + +For various ways in which this library and plugin can be tested, see also the other +`HeterogeneousTest/Alpaka...` packages: + - [`HeterogeneousTest/AlpakaDevice/README.md`](../../HeterogeneousTest/AlpakaDevice/README.md) + - [`HeterogeneousTest/AlpakaWrapper/README.md`](../../HeterogeneousTest/AlpakaWrapper/README.md) + - [`HeterogeneousTest/AlpakaOpaque/README.md`](../../HeterogeneousTest/AlpakaOpaque/README.md) + + +# Combining plugins + +`HeterogeneousTest/AlpakaOpaque/test` contains the `testAlpakaTestAdditionModules.py` python +configuration that exercise all four plugins in a single application. diff --git a/HeterogeneousTest/AlpakaKernel/interface/alpaka/DeviceAdditionKernel.h b/HeterogeneousTest/AlpakaKernel/interface/alpaka/DeviceAdditionKernel.h new file mode 100644 index 0000000000000..43d99270e32b8 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/interface/alpaka/DeviceAdditionKernel.h @@ -0,0 +1,36 @@ +#ifndef HeterogeneousTest_AlpakaKernel_interface_alpaka_DeviceAdditionKernel_h +#define HeterogeneousTest_AlpakaKernel_interface_alpaka_DeviceAdditionKernel_h + +#include + +#include + +#include "HeterogeneousTest/AlpakaDevice/interface/alpaka/DeviceAddition.h" + +namespace cms::alpakatest { + + struct KernelAddVectorsF { + template + ALPAKA_FN_ACC void operator()(TAcc const& acc, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size) const { + add_vectors_f(acc, in1, in2, out, size); + } + }; + + struct KernelAddVectorsD { + template + ALPAKA_FN_ACC void operator()(TAcc const& acc, + const double* __restrict__ in1, + const double* __restrict__ in2, + double* __restrict__ out, + uint32_t size) const { + add_vectors_d(acc, in1, in2, out, size); + } + }; + +} // namespace cms::alpakatest + +#endif // HeterogeneousTest_AlpakaKernel_interface_alpaka_DeviceAdditionKernel_h diff --git a/HeterogeneousTest/AlpakaKernel/plugins/BuildFile.xml b/HeterogeneousTest/AlpakaKernel/plugins/BuildFile.xml new file mode 100644 index 0000000000000..9afe990758c74 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/plugins/BuildFile.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionAlgo.dev.cc b/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionAlgo.dev.cc new file mode 100644 index 0000000000000..0cf8caa3769c9 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionAlgo.dev.cc @@ -0,0 +1,22 @@ +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" +#include "HeterogeneousTest/AlpakaKernel/interface/alpaka/DeviceAdditionKernel.h" + +#include "AlpakaTestKernelAdditionAlgo.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaKernelPlugins { + + void wrapper_add_vectors_f(Queue& queue, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size) { + alpaka::exec( + queue, cms::alpakatools::make_workdiv(32, 32), cms::alpakatest::KernelAddVectorsF{}, in1, in2, out, size); + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaKernelPlugins diff --git a/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionAlgo.h b/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionAlgo.h new file mode 100644 index 0000000000000..268c2117144f3 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionAlgo.h @@ -0,0 +1,18 @@ +#ifndef HeterogeneousTest_AlpakaKernel_plugins_alpaka_AlpakaTestKernelAdditionAlgo_h +#define HeterogeneousTest_AlpakaKernel_plugins_alpaka_AlpakaTestKernelAdditionAlgo_h + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaKernelPlugins { + + void wrapper_add_vectors_f(Queue& queue, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size); + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::HeterogeneousTestAlpakaKernelPlugins + +#endif // HeterogeneousTest_AlpakaKernel_plugins_alpaka_AlpakaTestKernelAdditionAlgo_h diff --git a/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionModule.cc b/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionModule.cc new file mode 100644 index 0000000000000..a58931c389985 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/plugins/alpaka/AlpakaTestKernelAdditionModule.cc @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +#include + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousCore/AlpakaServices/interface/alpaka/AlpakaService.h" + +#include "AlpakaTestKernelAdditionAlgo.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE { + + class AlpakaTestKernelAdditionModule : public edm::global::EDAnalyzer<> { + public: + explicit AlpakaTestKernelAdditionModule(edm::ParameterSet const& config); + ~AlpakaTestKernelAdditionModule() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + void analyze(edm::StreamID, edm::Event const& event, edm::EventSetup const& setup) const override; + + private: + const uint32_t size_; + }; + + AlpakaTestKernelAdditionModule::AlpakaTestKernelAdditionModule(edm::ParameterSet const& config) + : size_(config.getParameter("size")) {} + + void AlpakaTestKernelAdditionModule::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("size", 1024 * 1024); + + // ignore the alpaka = cms.untracked.PSet(...) injected by the framework + edm::ParameterSetDescription alpaka; + alpaka.setAllowAnything(); + desc.addUntracked("alpaka", alpaka); + + descriptions.addWithDefaultLabel(desc); + } + + void AlpakaTestKernelAdditionModule::analyze(edm::StreamID, + edm::Event const& event, + edm::EventSetup const& setup) const { + // require a valid Alpaka backend for running + edm::Service service; + if (not service or not service->enabled()) { + std::cout << "The " << ALPAKA_TYPE_ALIAS_NAME(AlpakaService) + << " is not available or disabled, the test will be skipped.\n"; + return; + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // allocate input and output host buffers + std::vector in1_h(size_); + std::vector in2_h(size_); + std::vector out_h(size_); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size_; ++i) { + in1_h[i] = dist(rand); + in2_h[i] = dist(rand); + out_h[i] = 0.; + } + + // run the test on all available devices + for (auto const& device : cms::alpakatools::devices()) { + Queue queue{device}; + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size_); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size_); + auto out_d = cms::alpakatools::make_device_buffer(queue, size_); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size_); + alpaka::memcpy(queue, in2_d, in2_h, size_); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + HeterogeneousTestAlpakaKernelPlugins::wrapper_add_vectors_f( + queue, in1_d.data(), in2_d.data(), out_d.data(), size_); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d); + + // wait for all the operations to complete + alpaka::wait(queue); + + // check the results + for (uint32_t i = 0; i < size_; ++i) { + float sum = in1_h[i] + in2_h[i]; + assert(out_h[i] < sum + epsilon); + assert(out_h[i] > sum - epsilon); + } + } + + std::cout << "All tests passed.\n"; + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE + +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/MakerMacros.h" +DEFINE_FWK_ALPAKA_MODULE(AlpakaTestKernelAdditionModule); diff --git a/HeterogeneousTest/AlpakaKernel/test/BuildFile.xml b/HeterogeneousTest/AlpakaKernel/test/BuildFile.xml new file mode 100644 index 0000000000000..75c73a6122b0b --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/test/BuildFile.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaKernel/test/alpaka/testDeviceAdditionKernel.dev.cc b/HeterogeneousTest/AlpakaKernel/test/alpaka/testDeviceAdditionKernel.dev.cc new file mode 100644 index 0000000000000..14b4f7f520640 --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/test/alpaka/testDeviceAdditionKernel.dev.cc @@ -0,0 +1,91 @@ +#include +#include +#include + +#define CATCH_CONFIG_MAIN +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/devices.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" +#include "HeterogeneousTest/AlpakaKernel/interface/alpaka/DeviceAdditionKernel.h" + +using namespace ALPAKA_ACCELERATOR_NAMESPACE; + +TEST_CASE("HeterogeneousTest/AlpakaKernel test", "[alpakaTestDeviceAdditionKernel]") { + auto const& devices = cms::alpakatools::devices(); + if (devices.empty()) { + FAIL("No devices available for the " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend, " + "the test will be skipped."); + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // buffer size + constexpr uint32_t size = 1024 * 1024; + + // allocate input and output host buffers + std::vector in1_h(size); + std::vector in2_h(size); + std::vector out_h(size); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size; ++i) { + in1_h[i] = dist(rand); + in2_h[i] = dist(rand); + out_h[i] = 0.; + } + + // run the test on all available devices + for (auto const& device : cms::alpakatools::devices()) { + SECTION("Test add_vectors_f on " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend") { + REQUIRE_NOTHROW([&]() { + Queue queue{device}; + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size); + auto out_d = cms::alpakatools::make_device_buffer(queue, size); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size); + alpaka::memcpy(queue, in2_d, in2_h, size); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + alpaka::exec(queue, + cms::alpakatools::make_workdiv(32, 32), + cms::alpakatest::KernelAddVectorsF{}, + in1_d.data(), + in2_d.data(), + out_d.data(), + size); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d, size); + + // wait for all the operations to complete + alpaka::wait(queue); + }()); + + // check the results + for (uint32_t i = 0; i < size; ++i) { + float sum = in1_h[i] + in2_h[i]; + CHECK_THAT(out_h[i], Catch::Matchers::WithinAbs(sum, epsilon)); + } + } + } +} diff --git a/HeterogeneousTest/AlpakaKernel/test/testAlpakaTestKernelAdditionModule.py b/HeterogeneousTest/AlpakaKernel/test/testAlpakaTestKernelAdditionModule.py new file mode 100644 index 0000000000000..bc7f9fae436ed --- /dev/null +++ b/HeterogeneousTest/AlpakaKernel/test/testAlpakaTestKernelAdditionModule.py @@ -0,0 +1,15 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process('TestAlpakaTestKernelAdditionModule') +process.load('Configuration.StandardSequences.Accelerators_cff') +process.load('HeterogeneousCore.AlpakaCore.ProcessAcceleratorAlpaka_cfi') + +process.source = cms.Source('EmptySource') + +process.alpakaTestKernelAdditionModule = cms.EDAnalyzer('AlpakaTestKernelAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.path = cms.Path(process.alpakaTestKernelAdditionModule) + +process.maxEvents.input = 1 diff --git a/HeterogeneousTest/AlpakaOpaque/BuildFile.xml b/HeterogeneousTest/AlpakaOpaque/BuildFile.xml new file mode 100644 index 0000000000000..03d0d171f2be2 --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/BuildFile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/HeterogeneousTest/AlpakaOpaque/README.md b/HeterogeneousTest/AlpakaOpaque/README.md new file mode 100644 index 0000000000000..3bd31fbd2bbd6 --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/README.md @@ -0,0 +1,46 @@ +# Introduction + +The packages `HeterogeneousTest/AlpakaDevice`, `HeterogeneousTest/AlpakaKernel`, +`HeterogeneousTest/AlpakaWrapper` and `HeterogeneousTest/AlpakaOpaque` implement a set of libraries, +plugins and tests to exercise the build rules for Alpaka. +In particular, these tests show what is supported and what are the limitations implementing +Alpaka-based libraries, and using them from multiple plugins. + + +# `HeterogeneousTest/AlpakaOpaque` + +The package `HeterogeneousTest/AlpakaOpaque` implements a non-Alpaka aware library, with functions +that call the wrappers defined in the `HeterogeneousTest/AlpakaWrapper` library: +```c++ +namespace ALPAKA_ACCELERATOR_NAMESPACE::test { + + void opaque_add_vectors_f(...); + void opaque_add_vectors_d(...); + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::test +``` + +The `plugins` directory implements the `AlpakaTestOpqaueAdditionModule` `EDAnalyzer` that calls the +function defined in this library. This plugin shows how the function can be used directly from a +host-only, non-Alpaka aware plugin. + +The `test` directory implements the `testAlpakaDeviceAdditionOpqaue` test binary that calls the +function defined in this library, and shows how they can be used directly from a host-only, +non-Alpaka aware application. +It also contains the `testAlpakaTestOpqaueAdditionModule.py` python configuration to exercise the +`AlpakaTestOpqaueAdditionModule` module. + + +# Other packages + +For various ways in which this library and plugin can be tested, see also the other +`HeterogeneousTest/Alpaka...` packages: + - [`HeterogeneousTest/AlpakaDevice/README.md`](../../HeterogeneousTest/AlpakaDevice/README.md) + - [`HeterogeneousTest/AlpakaKernel/README.md`](../../HeterogeneousTest/AlpakaKernel/README.md) + - [`HeterogeneousTest/AlpakaWrapper/README.md`](../../HeterogeneousTest/AlpakaWrapper/README.md) + + +# Combining plugins + +`HeterogeneousTest/AlpakaOpaque/test` contains also the `testAlpakaTestAdditionModules.py` python +configuration that exercise all four plugins in a single application. diff --git a/HeterogeneousTest/AlpakaOpaque/interface/alpaka/DeviceAdditionOpaque.h b/HeterogeneousTest/AlpakaOpaque/interface/alpaka/DeviceAdditionOpaque.h new file mode 100644 index 0000000000000..12eb45373ab1c --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/interface/alpaka/DeviceAdditionOpaque.h @@ -0,0 +1,16 @@ +#ifndef HeterogeneousTest_AlpakaOpaque_interface_alpaka_DeviceAdditionOpaque_h +#define HeterogeneousTest_AlpakaOpaque_interface_alpaka_DeviceAdditionOpaque_h + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::test { + + void opaque_add_vectors_f(const float* in1, const float* in2, float* out, uint32_t size); + + void opaque_add_vectors_d(const double* in1, const double* in2, double* out, uint32_t size); + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::test + +#endif // HeterogeneousTest_AlpakaOpaque_interface_alpaka_DeviceAdditionOpaque_h diff --git a/HeterogeneousTest/AlpakaOpaque/plugins/BuildFile.xml b/HeterogeneousTest/AlpakaOpaque/plugins/BuildFile.xml new file mode 100644 index 0000000000000..aad45d082d2e3 --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/plugins/BuildFile.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaOpaque/plugins/alpaka/AlpakaTestOpaqueAdditionModule.cc b/HeterogeneousTest/AlpakaOpaque/plugins/alpaka/AlpakaTestOpaqueAdditionModule.cc new file mode 100644 index 0000000000000..855856c9d7af3 --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/plugins/alpaka/AlpakaTestOpaqueAdditionModule.cc @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousTest/AlpakaOpaque/interface/alpaka/DeviceAdditionOpaque.h" +#include "HeterogeneousCore/AlpakaServices/interface/alpaka/AlpakaService.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE { + + class AlpakaTestOpaqueAdditionModule : public edm::global::EDAnalyzer<> { + public: + explicit AlpakaTestOpaqueAdditionModule(edm::ParameterSet const& config); + ~AlpakaTestOpaqueAdditionModule() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + void analyze(edm::StreamID, edm::Event const& event, edm::EventSetup const& setup) const override; + + private: + const uint32_t size_; + }; + + AlpakaTestOpaqueAdditionModule::AlpakaTestOpaqueAdditionModule(edm::ParameterSet const& config) + : size_(config.getParameter("size")) {} + + void AlpakaTestOpaqueAdditionModule::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("size", 1024 * 1024); + + // ignore the alpaka = cms.untracked.PSet(...) injected by the framework + edm::ParameterSetDescription alpaka; + alpaka.setAllowAnything(); + desc.addUntracked("alpaka", alpaka); + + descriptions.addWithDefaultLabel(desc); + } + + void AlpakaTestOpaqueAdditionModule::analyze(edm::StreamID, + edm::Event const& event, + edm::EventSetup const& setup) const { + // require a valid Alpaka backend for running + edm::Service service; + if (not service or not service->enabled()) { + std::cout << "The " << ALPAKA_TYPE_ALIAS_NAME(AlpakaService) + << " is not available or disabled, the test will be skipped.\n"; + return; + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // allocate input and output host buffers + std::vector in1(size_); + std::vector in2(size_); + std::vector out(size_); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size_; ++i) { + in1[i] = dist(rand); + in2[i] = dist(rand); + out[i] = 0.; + } + + // launch the 1-dimensional kernel for vector addition on the first available device + test::opaque_add_vectors_f(in1.data(), in2.data(), out.data(), size_); + + // check the results + for (uint32_t i = 0; i < size_; ++i) { + float sum = in1[i] + in2[i]; + assert(out[i] < sum + epsilon); + assert(out[i] > sum - epsilon); + } + + std::cout << "All tests passed.\n"; + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE + +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/MakerMacros.h" +DEFINE_FWK_ALPAKA_MODULE(AlpakaTestOpaqueAdditionModule); diff --git a/HeterogeneousTest/AlpakaOpaque/src/alpaka/DeviceAdditionOpaque.cc b/HeterogeneousTest/AlpakaOpaque/src/alpaka/DeviceAdditionOpaque.cc new file mode 100644 index 0000000000000..34c3370b677c9 --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/src/alpaka/DeviceAdditionOpaque.cc @@ -0,0 +1,85 @@ +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/devices.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousTest/AlpakaOpaque/interface/alpaka/DeviceAdditionOpaque.h" +#include "HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::test { + + void opaque_add_vectors_f(const float* in1, const float* in2, float* out, uint32_t size) { + // run on the first available devices + auto const& device = cms::alpakatools::devices()[0]; + Queue queue{device}; + + // wrap the input and output data in views + auto in1_h = cms::alpakatools::make_host_view(in1, size); + auto in2_h = cms::alpakatools::make_host_view(in2, size); + auto out_h = cms::alpakatools::make_host_view(out, size); + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size); + auto out_d = cms::alpakatools::make_device_buffer(queue, size); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size); + alpaka::memcpy(queue, in2_d, in2_h, size); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + test::wrapper_add_vectors_f(queue, in1_d.data(), in2_d.data(), out_d.data(), size); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d); + + // wait for all the operations to complete + alpaka::wait(queue); + + // the device buffers are freed automatically + } + + void opaque_add_vectors_d(const double* in1, const double* in2, double* out, uint32_t size) { + // run on the first available devices + auto const& device = cms::alpakatools::devices()[0]; + Queue queue{device}; + + // wrap the input and output data in views + auto in1_h = cms::alpakatools::make_host_view(in1, size); + auto in2_h = cms::alpakatools::make_host_view(in2, size); + auto out_h = cms::alpakatools::make_host_view(out, size); + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size); + auto out_d = cms::alpakatools::make_device_buffer(queue, size); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size); + alpaka::memcpy(queue, in2_d, in2_h, size); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + test::wrapper_add_vectors_d(queue, in1_d.data(), in2_d.data(), out_d.data(), size); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d); + + // wait for all the operations to complete + alpaka::wait(queue); + + // the device buffers are freed automatically + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::test diff --git a/HeterogeneousTest/AlpakaOpaque/test/BuildFile.xml b/HeterogeneousTest/AlpakaOpaque/test/BuildFile.xml new file mode 100644 index 0000000000000..51b598e396e9a --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/test/BuildFile.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaOpaque/test/alpaka/testDeviceAdditionOpaque.cc b/HeterogeneousTest/AlpakaOpaque/test/alpaka/testDeviceAdditionOpaque.cc new file mode 100644 index 0000000000000..aba728bd26218 --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/test/alpaka/testDeviceAdditionOpaque.cc @@ -0,0 +1,54 @@ +#include +#include +#include + +#define CATCH_CONFIG_MAIN +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/devices.h" +#include "HeterogeneousTest/AlpakaOpaque/interface/alpaka/DeviceAdditionOpaque.h" + +using namespace ALPAKA_ACCELERATOR_NAMESPACE; + +TEST_CASE("HeterogeneousTest/AlpakaOpaque test", "[alpakaTestOpaqueAdditionOpaque]") { + auto const& devices = cms::alpakatools::devices(); + if (devices.empty()) { + FAIL("No devices available for the " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend, " + "the test will be skipped."); + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // buffer size + constexpr uint32_t size = 1024 * 1024; + + // allocate input and output host buffers + std::vector in1(size); + std::vector in2(size); + std::vector out(size); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size; ++i) { + in1[i] = dist(rand); + in2[i] = dist(rand); + out[i] = 0.; + } + + SECTION("Test add_vectors_f on " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend") { + // launch the 1-dimensional kernel for vector addition + REQUIRE_NOTHROW(test::opaque_add_vectors_f(in1.data(), in2.data(), out.data(), size)); + + // check the results + for (uint32_t i = 0; i < size; ++i) { + float sum = in1[i] + in2[i]; + CHECK_THAT(out[i], Catch::Matchers::WithinAbs(sum, epsilon)); + } + } +} diff --git a/HeterogeneousTest/AlpakaOpaque/test/testAlpakaTestAdditionModules.py b/HeterogeneousTest/AlpakaOpaque/test/testAlpakaTestAdditionModules.py new file mode 100644 index 0000000000000..e1a2e44448a6c --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/test/testAlpakaTestAdditionModules.py @@ -0,0 +1,31 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process('TestAlpakaTestOpaqueAdditionModule') +process.load('Configuration.StandardSequences.Accelerators_cff') +process.load('HeterogeneousCore.AlpakaCore.ProcessAcceleratorAlpaka_cfi') + +process.source = cms.Source('EmptySource') + +process.alpakaTestDeviceAdditionModule = cms.EDAnalyzer('AlpakaTestDeviceAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.alpakaTestKernelAdditionModule = cms.EDAnalyzer('AlpakaTestKernelAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.alpakaTestWrapperAdditionModule = cms.EDAnalyzer('AlpakaTestWrapperAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.alpakaTestOpaqueAdditionModule = cms.EDAnalyzer('AlpakaTestOpaqueAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.path = cms.Path( + process.alpakaTestDeviceAdditionModule + + process.alpakaTestKernelAdditionModule + + process.alpakaTestWrapperAdditionModule + + process.alpakaTestOpaqueAdditionModule) + +process.maxEvents.input = 1 diff --git a/HeterogeneousTest/AlpakaOpaque/test/testAlpakaTestOpaqueAdditionModule.py b/HeterogeneousTest/AlpakaOpaque/test/testAlpakaTestOpaqueAdditionModule.py new file mode 100644 index 0000000000000..a23a22b8389ec --- /dev/null +++ b/HeterogeneousTest/AlpakaOpaque/test/testAlpakaTestOpaqueAdditionModule.py @@ -0,0 +1,15 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process('TestAlpakaTestOpaqueAdditionModule') +process.load('Configuration.StandardSequences.Accelerators_cff') +process.load('HeterogeneousCore.AlpakaCore.ProcessAcceleratorAlpaka_cfi') + +process.source = cms.Source('EmptySource') + +process.alpakaTestOpaqueAdditionModule = cms.EDAnalyzer('AlpakaTestOpaqueAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.path = cms.Path(process.alpakaTestOpaqueAdditionModule) + +process.maxEvents.input = 1 diff --git a/HeterogeneousTest/AlpakaWrapper/BuildFile.xml b/HeterogeneousTest/AlpakaWrapper/BuildFile.xml new file mode 100644 index 0000000000000..0418fc9ec2c38 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/BuildFile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/HeterogeneousTest/AlpakaWrapper/README.md b/HeterogeneousTest/AlpakaWrapper/README.md new file mode 100644 index 0000000000000..e9aef644c554a --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/README.md @@ -0,0 +1,48 @@ +# Introduction + +The packages `HeterogeneousTest/AlpakaDevice`, `HeterogeneousTest/AlpakaKernel`, +`HeterogeneousTest/AlpakaWrapper` and `HeterogeneousTest/AlpakaOpaque` implement a set of libraries, +plugins and tests to exercise the build rules for Alpaka. +In particular, these tests show what is supported and what are the limitations implementing +Alpaka-based libraries, and using them from multiple plugins. + + +# `HeterogeneousTest/AlpakaWrapper` + +The package `HeterogeneousTest/AlpakaWrapper` implements a library that defines and exports +host-side wrappers that launch the kernels defined in the `HeterogeneousTest/AlpakaKernel` library: +```c++ +namespace ALPAKA_ACCELERATOR_NAMESPACE::test { + + void wrapper_add_vectors_f(...); + void wrapper_add_vectors_d(...); + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::test +``` +These wrappers can be used from host-only, non-Alpaka aware libraries, plugins and applications. +They can be linked with the standard host linker. + +The `plugins` directory implements the `AlpakaTestWrapperAdditionModule` `EDAnalyzer` that calls the +wrappers defined in this library. This plugin shows how the wrappers can be used directly from a +host-only, non-Alpaka aware plugin. + +The `test` directory implements the `testAlpakaDeviceAdditionWrapper` test binary that calls the +wrappers defined in this library, and shows how they can be used directly from a host-only, +non-Alpaka aware application. +It also contains the `testAlpakaTestWrapperAdditionModule.py` python configuration to exercise the +`AlpakaTestWrapperAdditionModule` module. + + +# Other packages + +For various ways in which this library and plugin can be tested, see also the other +`HeterogeneousTest/Alpaka...` packages: + - [`HeterogeneousTest/AlpakaDevice/README.md`](../../HeterogeneousTest/AlpakaDevice/README.md) + - [`HeterogeneousTest/AlpakaKernel/README.md`](../../HeterogeneousTest/AlpakaKernel/README.md) + - [`HeterogeneousTest/AlpakaOpaque/README.md`](../../HeterogeneousTest/AlpakaOpaque/README.md) + + +# Combining plugins + +`HeterogeneousTest/AlpakaOpaque/test` contains the `testAlpakaTestAdditionModules.py` python +configuration that exercise all four plugins in a single application. diff --git a/HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h b/HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h new file mode 100644 index 0000000000000..a278911aebfd4 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h @@ -0,0 +1,24 @@ +#ifndef HeterogeneousTest_AlpakaWrapper_interface_alpaka_DeviceAdditionWrapper_h +#define HeterogeneousTest_AlpakaWrapper_interface_alpaka_DeviceAdditionWrapper_h + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::test { + + void wrapper_add_vectors_f(Queue& queue, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size); + + void wrapper_add_vectors_d(Queue& queue, + const double* __restrict__ in1, + const double* __restrict__ in2, + double* __restrict__ out, + uint32_t size); + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::test + +#endif // HeterogeneousTest_AlpakaWrapper_interface_alpaka_DeviceAdditionWrapper_h diff --git a/HeterogeneousTest/AlpakaWrapper/plugins/BuildFile.xml b/HeterogeneousTest/AlpakaWrapper/plugins/BuildFile.xml new file mode 100644 index 0000000000000..3ebdae5ffd581 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/plugins/BuildFile.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaWrapper/plugins/alpaka/AlpakaTestWrapperAdditionModule.cc b/HeterogeneousTest/AlpakaWrapper/plugins/alpaka/AlpakaTestWrapperAdditionModule.cc new file mode 100644 index 0000000000000..1a33e51c23348 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/plugins/alpaka/AlpakaTestWrapperAdditionModule.cc @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#include + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousCore/AlpakaServices/interface/alpaka/AlpakaService.h" +#include "HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE { + + class AlpakaTestWrapperAdditionModule : public edm::global::EDAnalyzer<> { + public: + explicit AlpakaTestWrapperAdditionModule(edm::ParameterSet const& config); + ~AlpakaTestWrapperAdditionModule() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + void analyze(edm::StreamID, edm::Event const& event, edm::EventSetup const& setup) const override; + + private: + const uint32_t size_; + }; + + AlpakaTestWrapperAdditionModule::AlpakaTestWrapperAdditionModule(edm::ParameterSet const& config) + : size_(config.getParameter("size")) {} + + void AlpakaTestWrapperAdditionModule::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("size", 1024 * 1024); + + // ignore the alpaka = cms.untracked.PSet(...) injected by the framework + edm::ParameterSetDescription alpaka; + alpaka.setAllowAnything(); + desc.addUntracked("alpaka", alpaka); + + descriptions.addWithDefaultLabel(desc); + } + + void AlpakaTestWrapperAdditionModule::analyze(edm::StreamID, + edm::Event const& event, + edm::EventSetup const& setup) const { + // require a valid Alpaka backend for running + edm::Service service; + if (not service or not service->enabled()) { + std::cout << "The " << ALPAKA_TYPE_ALIAS_NAME(AlpakaService) + << " is not available or disabled, the test will be skipped.\n"; + return; + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // allocate input and output host buffers + std::vector in1_h(size_); + std::vector in2_h(size_); + std::vector out_h(size_); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size_; ++i) { + in1_h[i] = dist(rand); + in2_h[i] = dist(rand); + out_h[i] = 0.; + } + + // run the test on all available devices + for (auto const& device : cms::alpakatools::devices()) { + Queue queue{device}; + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size_); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size_); + auto out_d = cms::alpakatools::make_device_buffer(queue, size_); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size_); + alpaka::memcpy(queue, in2_d, in2_h, size_); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + test::wrapper_add_vectors_f(queue, in1_d.data(), in2_d.data(), out_d.data(), size_); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d); + + // wait for all the operations to complete + alpaka::wait(queue); + + // check the results + for (uint32_t i = 0; i < size_; ++i) { + float sum = in1_h[i] + in2_h[i]; + assert(out_h[i] < sum + epsilon); + assert(out_h[i] > sum - epsilon); + } + } + + std::cout << "All tests passed.\n"; + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE + +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/MakerMacros.h" +DEFINE_FWK_ALPAKA_MODULE(AlpakaTestWrapperAdditionModule); diff --git a/HeterogeneousTest/AlpakaWrapper/src/alpaka/DeviceAdditionWrapper.dev.cc b/HeterogeneousTest/AlpakaWrapper/src/alpaka/DeviceAdditionWrapper.dev.cc new file mode 100644 index 0000000000000..b5f662fc930c9 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/src/alpaka/DeviceAdditionWrapper.dev.cc @@ -0,0 +1,30 @@ +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" +#include "HeterogeneousTest/AlpakaKernel/interface/alpaka/DeviceAdditionKernel.h" +#include "HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h" + +namespace ALPAKA_ACCELERATOR_NAMESPACE::test { + + void wrapper_add_vectors_f(Queue& queue, + const float* __restrict__ in1, + const float* __restrict__ in2, + float* __restrict__ out, + uint32_t size) { + alpaka::exec( + queue, cms::alpakatools::make_workdiv(32, 32), cms::alpakatest::KernelAddVectorsF{}, in1, in2, out, size); + } + + void wrapper_add_vectors_d(Queue& queue, + const double* __restrict__ in1, + const double* __restrict__ in2, + double* __restrict__ out, + uint32_t size) { + alpaka::exec( + queue, cms::alpakatools::make_workdiv(32, 32), cms::alpakatest::KernelAddVectorsD{}, in1, in2, out, size); + } + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE::test diff --git a/HeterogeneousTest/AlpakaWrapper/test/BuildFile.xml b/HeterogeneousTest/AlpakaWrapper/test/BuildFile.xml new file mode 100644 index 0000000000000..d1bbeb51e4ef5 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/test/BuildFile.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/HeterogeneousTest/AlpakaWrapper/test/alpaka/testDeviceAdditionWrapper.cc b/HeterogeneousTest/AlpakaWrapper/test/alpaka/testDeviceAdditionWrapper.cc new file mode 100644 index 0000000000000..00e2776f64948 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/test/alpaka/testDeviceAdditionWrapper.cc @@ -0,0 +1,85 @@ +#include +#include +#include + +#define CATCH_CONFIG_MAIN +#include + +#include + +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/devices.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" +#include "HeterogeneousCore/AlpakaInterface/interface/workdivision.h" +#include "HeterogeneousTest/AlpakaWrapper/interface/alpaka/DeviceAdditionWrapper.h" + +using namespace ALPAKA_ACCELERATOR_NAMESPACE; + +TEST_CASE("HeterogeneousTest/AlpakaWrapper test", "[alpakaTestDeviceAdditionWrapper]") { + auto const& devices = cms::alpakatools::devices(); + if (devices.empty()) { + FAIL("No devices available for the " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend, " + "the test will be skipped."); + } + + // random number generator with a gaussian distribution + std::random_device rd{}; + std::default_random_engine rand{rd()}; + std::normal_distribution dist{0., 1.}; + + // tolerance + constexpr float epsilon = 0.000001; + + // buffer size + constexpr uint32_t size = 1024 * 1024; + + // allocate input and output host buffers + std::vector in1_h(size); + std::vector in2_h(size); + std::vector out_h(size); + + // fill the input buffers with random data, and the output buffer with zeros + for (uint32_t i = 0; i < size; ++i) { + in1_h[i] = dist(rand); + in2_h[i] = dist(rand); + out_h[i] = 0.; + } + + // run the test on all available devices + for (auto const& device : cms::alpakatools::devices()) { + SECTION("Test add_vectors_f on " EDM_STRINGIZE(ALPAKA_ACCELERATOR_NAMESPACE) " backend") { + REQUIRE_NOTHROW([&]() { + Queue queue{device}; + + // allocate input and output buffers on the device + auto in1_d = cms::alpakatools::make_device_buffer(queue, size); + auto in2_d = cms::alpakatools::make_device_buffer(queue, size); + auto out_d = cms::alpakatools::make_device_buffer(queue, size); + + // copy the input data to the device + // FIXME: pass the explicit size of type uint32_t to avoid compilation error + // The destination view and the extent are required to have compatible index types! + alpaka::memcpy(queue, in1_d, in1_h, size); + alpaka::memcpy(queue, in2_d, in2_h, size); + + // fill the output buffer with zeros + alpaka::memset(queue, out_d, 0); + + // launch the 1-dimensional kernel for vector addition + test::wrapper_add_vectors_f(queue, in1_d.data(), in2_d.data(), out_d.data(), size); + + // copy the results from the device to the host + alpaka::memcpy(queue, out_h, out_d, size); + + // wait for all the operations to complete + alpaka::wait(queue); + }()); + + // check the results + for (uint32_t i = 0; i < size; ++i) { + float sum = in1_h[i] + in2_h[i]; + CHECK_THAT(out_h[i], Catch::Matchers::WithinAbs(sum, epsilon)); + } + } + } +} diff --git a/HeterogeneousTest/AlpakaWrapper/test/testAlpakaTestWrapperAdditionModule.py b/HeterogeneousTest/AlpakaWrapper/test/testAlpakaTestWrapperAdditionModule.py new file mode 100644 index 0000000000000..7de7cdf1f2451 --- /dev/null +++ b/HeterogeneousTest/AlpakaWrapper/test/testAlpakaTestWrapperAdditionModule.py @@ -0,0 +1,15 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process('TestAlpakaTestWrapperAdditionModule') +process.load('Configuration.StandardSequences.Accelerators_cff') +process.load('HeterogeneousCore.AlpakaCore.ProcessAcceleratorAlpaka_cfi') + +process.source = cms.Source('EmptySource') + +process.alpakaTestWrapperAdditionModule = cms.EDAnalyzer('AlpakaTestWrapperAdditionModule@alpaka', + size = cms.uint32( 1024*1024 ) +) + +process.path = cms.Path(process.alpakaTestWrapperAdditionModule) + +process.maxEvents.input = 1