Modernize Algorithms
This MR changes substantially how Algorithms are declared in Allen.
- The
cuda
andx86
folders have been refactored intodevice
andhost
. -
Parameters
must now be defined under a macroDEFINE_PARAMETERS
. - Each of the input/output host/device parameters and properties inside
Parameters
must have exactly one single instance. - Algorithms are not templated anymore.
set_arguments_size
andoperator()
must now be defined in a source file. - As a consequence, parameters have virtual functions to access and set the
size
,data
andname
. - Algorithm names are set at runtime with a pregenerated function.
- The parser has been reworked to support the new syntax, do a more robust parsing and generate the required additional configuration.
-
host_function
anddevice_function
act as closures, with the added functionality that an instance of typeconst ArgumentReference<Parameters>&
gets automatically converted onto its properParameters
struct.
Explanation
Some of the above are hard to explain without an example, so here is one based off the Search by triplet algorithm. The algorithm consists in header and source files:
- https://gitlab.cern.ch/lhcb/Allen/-/blob/dcampora_modernize_algorithms/device/velo/search_by_triplet/include/SearchByTriplet.cuh
- https://gitlab.cern.ch/lhcb/Allen/-/blob/dcampora_modernize_algorithms/device/velo/search_by_triplet/src/SearchByTriplet.cu
The Parameters
macro uses BOOST_HANA
to achieve the capacity of declaring a struct
with attributes that can be converted onto a tuple, with C++14 and compatibility with nvcc 10
. Note this would be achievable with a C++17 compiler (such as the announced https://devblogs.nvidia.com/cuda-11-features-revealed/ by using structured binding.
For instance, a simplified block with HOST_INPUT
actually is:
// Defines some parameters
DEFINE_PARAMETERS(
Parameters,
(HOST_INPUT(host_number_of_selected_events_t, uint), host_number_of_selected_events))
// By hand, and with C++17, one could write without macros:
// struct Parameters {
// struct host_number_of_selected_events_t : host_datatype, input_datatype<uint> {} host_number_of_selected_events;
// };
// Ie. One can now get a plain tuple
decltype(boost::hana::to<boost::hana::ext::std::tuple_tag>(boost::hana::members(std::declval<Parameters>())));
Simplified function calling:
Almost all algorithms invoke at least one kernel
, ie. a function where most of the functionality is written. For DeviceAlgorithms, it is definitely the case that a kernel "global function" must be invoked, which is executed on the device.
A function that wraps any host or global function already existed. Now, it has been extended with support for converting on the fly the ArgumentReference<Parameters>
(ie. the tuple of argument references) into the Parameters
(ie. the struct). This works by detecting whenever the type passed to the function is of type ArgumentReference<T>
, by converting all parameters
onto data<TYPE>(arguments)
statements, and all properties
into property<PROPERTY>()
statements.
For instance, the function call of Search by triplet becomes very simple:
-
device_function(velo_search_by_triplet)
returns a function that expects the grid size, block size and the cuda stream. - Invoking that function returns finally a function that accepts the parameters.
-
arguments
is converted on the fly (less error-prone).
device_function(velo_search_by_triplet)(
dim3(first<host_number_of_selected_events_t>(arguments)), dim3(property<block_dim_x_t>().get()), cuda_stream)(
arguments, constants.dev_velo_geometry);
A generated sequence:
An example of a generated sequence:
#pragma once
#include <tuple>
#include "ConfiguredLines.h"
#include "../../stream/gear/include/ArgumentManager.cuh"
#include "../../host/data_provider/include/HostDataProvider.h"
#include "../../host/data_provider/include/HostDataProvider.h"
#include "../../host/global_event_cut/include/HostGlobalEventCut.h"
struct host_ut_banks__host_raw_banks_t : host_data_provider::Parameters::host_raw_banks_t, host_global_event_cut::Parameters::host_ut_raw_banks_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "host_ut_banks__host_raw_banks_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct host_ut_banks__host_raw_offsets_t : host_data_provider::Parameters::host_raw_offsets_t, host_global_event_cut::Parameters::host_ut_raw_offsets_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "host_ut_banks__host_raw_offsets_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct host_scifi_banks__host_raw_banks_t : host_data_provider::Parameters::host_raw_banks_t, host_global_event_cut::Parameters::host_scifi_raw_banks_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "host_scifi_banks__host_raw_banks_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct host_scifi_banks__host_raw_offsets_t : host_data_provider::Parameters::host_raw_offsets_t, host_global_event_cut::Parameters::host_scifi_raw_offsets_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "host_scifi_banks__host_raw_offsets_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct initialize_lists__host_total_number_of_events_t : host_global_event_cut::Parameters::host_total_number_of_events_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "initialize_lists__host_total_number_of_events_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct initialize_lists__host_event_list_t : host_global_event_cut::Parameters::host_event_list_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "initialize_lists__host_event_list_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct initialize_lists__host_number_of_selected_events_t : host_global_event_cut::Parameters::host_number_of_selected_events_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "initialize_lists__host_number_of_selected_events_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
struct initialize_lists__dev_event_list_t : host_global_event_cut::Parameters::dev_event_list_t { void set_size(size_t size) override { m_size = size; } size_t size() const override { return m_size; } std::string name() const override { return "initialize_lists__dev_event_list_t"; } void set_offset(char* offset) override { m_offset = offset; } char* offset() const override { return m_offset; } private: size_t m_size = 0; char* m_offset = nullptr; };
using configured_arguments_t = std::tuple<
host_ut_banks__host_raw_banks_t,
host_ut_banks__host_raw_offsets_t,
host_scifi_banks__host_raw_banks_t,
host_scifi_banks__host_raw_offsets_t,
initialize_lists__host_total_number_of_events_t,
initialize_lists__host_event_list_t,
initialize_lists__host_number_of_selected_events_t,
initialize_lists__dev_event_list_t>;
using configured_sequence_t = std::tuple<
host_data_provider::host_data_provider_t,
host_data_provider::host_data_provider_t,
host_global_event_cut::host_global_event_cut_t>;
using configured_sequence_arguments_t = std::tuple<
std::tuple<host_ut_banks__host_raw_banks_t, host_ut_banks__host_raw_offsets_t>,
std::tuple<host_scifi_banks__host_raw_banks_t, host_scifi_banks__host_raw_offsets_t>,
std::tuple<host_ut_banks__host_raw_banks_t, host_ut_banks__host_raw_offsets_t, host_scifi_banks__host_raw_banks_t, host_scifi_banks__host_raw_offsets_t, initialize_lists__host_total_number_of_events_t, initialize_lists__host_event_list_t, initialize_lists__host_number_of_selected_events_t, initialize_lists__dev_event_list_t>>;
void inline populate_sequence_algorithm_names(configured_sequence_t& sequence) {
std::get<0>(sequence).set_name("host_ut_banks");
std::get<1>(sequence).set_name("host_scifi_banks");
std::get<2>(sequence).set_name("initialize_lists");
}
Builds on top of !363 (merged) and !383 (merged).