Skip to content

Modernize Algorithms

Daniel Campora Perez requested to merge dcampora_modernize_algorithms into master

This MR changes substantially how Algorithms are declared in Allen.

  • The cuda and x86 folders have been refactored into device and host.
  • Parameters must now be defined under a macro DEFINE_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 and operator() must now be defined in a source file.
  • As a consequence, parameters have virtual functions to access and set the size, data and name.
  • 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 and device_function act as closures, with the added functionality that an instance of type const ArgumentReference<Parameters>& gets automatically converted onto its proper Parameters 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:

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

Edited by Daniel Campora Perez

Merge request reports