Skip to content

Commit

Permalink
Implement general new MPI communicator API. Note: MPI isn't used, i.e…
Browse files Browse the repository at this point in the history
…., each MPI rank does the classification all for itself!
  • Loading branch information
breyerml committed Dec 10, 2024
1 parent a7b0202 commit 27058a9
Show file tree
Hide file tree
Showing 17 changed files with 646 additions and 149 deletions.
51 changes: 49 additions & 2 deletions include/plssvm/backends/CUDA/csvm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "plssvm/csvm.hpp" // plssvm::detail::csvm_backend_exists
#include "plssvm/detail/memory_size.hpp" // plssvm::detail::memory_size
#include "plssvm/detail/type_traits.hpp" // PLSSVM_REQUIRES
#include "plssvm/mpi/communicator.hpp" // plssvm::mpi::communicator
#include "plssvm/parameter.hpp" // plssvm::parameter
#include "plssvm/target_platforms.hpp" // plssvm::target_platform

Expand Down Expand Up @@ -59,6 +60,16 @@ class csvm : public ::plssvm::detail::gpu_csvm<detail::device_ptr, int, detail::
* @throws plssvm::cuda::backend_exception if no CUDA capable devices could be found
*/
explicit csvm(parameter params = {});
/**
* @brief Construct a new C-SVM using the CUDA backend with the default parameters.
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] params struct encapsulating all possible parameters
* @throws plssvm::exception all exceptions thrown in the base class constructor
* @throws plssvm::cuda::backend_exception if the target platform isn't plssvm::target_platform::automatic or plssvm::target_platform::gpu_nvidia
* @throws plssvm::cuda::backend_exception if the plssvm::target_platform::gpu_nvidia target isn't available
* @throws plssvm::cuda::backend_exception if no CUDA capable devices could be found
*/
explicit csvm(mpi::communicator comm, parameter params = {});
/**
* @brief Construct a new C-SVM using the CUDA backend on the @p target platform with the parameters given through @p params.
* @param[in] target the target platform used for this C-SVM
Expand All @@ -69,6 +80,17 @@ class csvm : public ::plssvm::detail::gpu_csvm<detail::device_ptr, int, detail::
* @throws plssvm::cuda::backend_exception if no CUDA capable devices could be found
*/
explicit csvm(target_platform target, parameter params = {});
/**
* @brief Construct a new C-SVM using the CUDA backend on the @p target platform with the parameters given through @p params.
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] target the target platform used for this C-SVM
* @param[in] params struct encapsulating all possible SVM parameters
* @throws plssvm::exception all exceptions thrown in the base class constructor
* @throws plssvm::cuda::backend_exception if the target platform isn't plssvm::target_platform::automatic or plssvm::target_platform::gpu_nvidia
* @throws plssvm::cuda::backend_exception if the plssvm::target_platform::gpu_nvidia target isn't available
* @throws plssvm::cuda::backend_exception if no CUDA capable devices could be found
*/
explicit csvm(mpi::communicator comm, target_platform target, parameter params = {});

/**
* @brief Construct a new C-SVM using the CUDA backend and the optionally provided @p named_args.
Expand All @@ -80,7 +102,19 @@ class csvm : public ::plssvm::detail::gpu_csvm<detail::device_ptr, int, detail::
*/
template <typename... Args, PLSSVM_REQUIRES(::plssvm::detail::has_only_parameter_named_args_v<Args...>)>
explicit csvm(Args &&...named_args) :
csvm{ plssvm::target_platform::automatic, std::forward<Args>(named_args)... } { }
csvm{ mpi::communicator{}, std::forward<Args>(named_args)... } { }
/**
* @brief Construct a new C-SVM using the CUDA backend and the optionally provided @p named_args.
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] named_args the additional optional named arguments
* @throws plssvm::exception all exceptions thrown in the base class constructor
* @throws plssvm::cuda::backend_exception if the target platform isn't plssvm::target_platform::automatic or plssvm::target_platform::gpu_nvidia
* @throws plssvm::cuda::backend_exception if the plssvm::target_platform::gpu_nvidia target isn't available
* @throws plssvm::cuda::backend_exception if no CUDA capable devices could be found
*/
template <typename... Args, PLSSVM_REQUIRES(::plssvm::detail::has_only_parameter_named_args_v<Args...>)>
explicit csvm(mpi::communicator comm, Args &&...named_args) :
csvm{ std::move(comm), plssvm::target_platform::automatic, std::forward<Args>(named_args)... } { }

/**
* @brief Construct a new C-SVM using the CUDA backend on the @p target platform and the optionally provided @p named_args.
Expand All @@ -93,7 +127,20 @@ class csvm : public ::plssvm::detail::gpu_csvm<detail::device_ptr, int, detail::
*/
template <typename... Args, PLSSVM_REQUIRES(::plssvm::detail::has_only_parameter_named_args_v<Args...>)>
explicit csvm(const target_platform target, Args &&...named_args) :
base_type{ std::forward<Args>(named_args)... } {
csvm{ mpi::communicator{}, target, std::forward<Args>(named_args)... } { }
/**
* @brief Construct a new C-SVM using the CUDA backend on the @p target platform and the optionally provided @p named_args.
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] target the target platform used for this C-SVM
* @param[in] named_args the additional optional named-parameters
* @throws plssvm::exception all exceptions thrown in the base class constructor
* @throws plssvm::cuda::backend_exception if the target platform isn't plssvm::target_platform::automatic or plssvm::target_platform::gpu_nvidia
* @throws plssvm::cuda::backend_exception if the plssvm::target_platform::gpu_nvidia target isn't available
* @throws plssvm::cuda::backend_exception if no CUDA capable devices could be found
*/
template <typename... Args, PLSSVM_REQUIRES(::plssvm::detail::has_only_parameter_named_args_v<Args...>)>
explicit csvm(mpi::communicator comm, const target_platform target, Args &&...named_args) :
base_type{ std::move(comm), std::forward<Args>(named_args)... } {
this->init(target);
}

Expand Down
10 changes: 6 additions & 4 deletions include/plssvm/backends/gpu_csvm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "plssvm/detail/move_only_any.hpp" // plssvm::detail::{move_only_any, move_only_any_cast}
#include "plssvm/kernel_function_types.hpp" // plssvm::kernel_function_type
#include "plssvm/matrix.hpp" // plssvm::aos_matrix, plssvm::soa_matrix
#include "plssvm/mpi/communicator.hpp" // plssvm::mpi::communicator
#include "plssvm/parameter.hpp" // plssvm::parameter
#include "plssvm/shape.hpp" // plssvm::shape
#include "plssvm/solver_types.hpp" // plssvm::solver_type
Expand Down Expand Up @@ -56,17 +57,18 @@ class gpu_csvm : public ::plssvm::csvm {
/**
* @copydoc plssvm::csvm::csvm()
*/
explicit gpu_csvm(parameter params = {}) :
::plssvm::csvm{ params } { }
explicit gpu_csvm(mpi::communicator comm, parameter params = {}) :
::plssvm::csvm{ std::move(comm), params } { }

/**
* @brief Construct a C-SVM forwarding all parameters @p args to the plssvm::parameter constructor.
* @tparam Args the type of the (named-)parameters
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] args the parameters used to construct a plssvm::parameter
*/
template <typename... Args>
explicit gpu_csvm(Args &&...args) :
::plssvm::csvm{ std::forward<Args>(args)... } { }
explicit gpu_csvm(mpi::communicator comm, Args &&...args) :
::plssvm::csvm{ std::move(comm), std::forward<Args>(args)... } { }

/**
* @copydoc plssvm::csvm::csvm(const plssvm::csvm &)
Expand Down
31 changes: 26 additions & 5 deletions include/plssvm/csvm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "plssvm/kernel_function_types.hpp" // plssvm::kernel_function_type
#include "plssvm/matrix.hpp" // plssvm::aos_matrix
#include "plssvm/model.hpp" // plssvm::model
#include "plssvm/mpi/communicator.hpp" // plssvm::mpi::communicator
#include "plssvm/parameter.hpp" // plssvm::parameter
#include "plssvm/shape.hpp" // plssvm::shape
#include "plssvm/solver_types.hpp" // plssvm::solver_type
Expand Down Expand Up @@ -69,16 +70,18 @@ class csvm {
/**
* @brief Construct a C-SVM using the SVM parameter @p params.
* @details Uses the default SVM parameter if none are provided.
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] params the SVM parameter
*/
explicit csvm(parameter params = {});
explicit csvm(mpi::communicator comm, parameter params = {});
/**
* @brief Construct a C-SVM forwarding all parameters @p args to the plssvm::parameter constructor.
* @tparam Args the type of the (named-)parameters
* @param[in] comm the used MPI communicator (**note**: currently unused)
* @param[in] args the parameters used to construct a plssvm::parameter
*/
template <typename... Args>
explicit csvm(Args &&...args);
explicit csvm(mpi::communicator comm, Args &&...args);

/**
* @brief Delete copy-constructor since a CSVM is a move-only type.
Expand Down Expand Up @@ -255,6 +258,9 @@ class csvm {
/// The data distribution on the available devices.
mutable std::unique_ptr<detail::data_distribution> data_distribution_{};

/// The used MPI communicator.
mpi::communicator comm_{};

protected: // necessary for tests, would otherwise be private
/**
* @brief Perform some sanity checks on the passed SVM parameters.
Expand Down Expand Up @@ -311,13 +317,15 @@ class csvm {
parameter params_{};
};

inline csvm::csvm(parameter params) :
inline csvm::csvm(mpi::communicator comm, parameter params) :
comm_{ std::move(comm) },
params_{ params } {
this->sanity_check_parameter();
}

template <typename... Args>
csvm::csvm(Args &&...named_args) :
csvm::csvm(mpi::communicator comm, Args &&...named_args) :
comm_{ std::move(comm) },
params_{ std::forward<Args>(named_args)... } {
this->sanity_check_parameter();
}
Expand Down Expand Up @@ -376,6 +384,7 @@ model<label_type> csvm::fit(const data_set<label_type> &data, Args &&...named_ar
const std::chrono::time_point start_time = std::chrono::steady_clock::now();

detail::log(verbosity_level::full,
comm_,
"Using {} ({}) as multi-class classification strategy.\n",
used_classification,
classification_type_to_full_string(used_classification));
Expand Down Expand Up @@ -417,6 +426,7 @@ model<label_type> csvm::fit(const data_set<label_type> &data, Args &&...named_ar
if (num_classes == 2) {
// special optimization for binary case (no temporary copies necessary)
detail::log(verbosity_level::full,
comm_,
"\nClassifying 0 vs 1 ({} vs {}) (1/1):\n",
data.mapping_->get_label_by_mapped_index(0),
data.mapping_->get_label_by_mapped_index(1));
Expand Down Expand Up @@ -460,6 +470,7 @@ model<label_type> csvm::fit(const data_set<label_type> &data, Args &&...named_ar

// solve the minimization problem -> note that only a single rhs is present
detail::log(verbosity_level::full,
comm_,
"\nClassifying {} vs {} ({} vs {}) ({}/{}):\n",
i,
j,
Expand All @@ -486,6 +497,7 @@ model<label_type> csvm::fit(const data_set<label_type> &data, Args &&...named_ar

const std::chrono::time_point end_time = std::chrono::steady_clock::now();
detail::log(verbosity_level::full | verbosity_level::timing,
comm_,
"\nLearned the SVM classifier for {} multi-class classification in {}.\n\n",
classification_type_to_full_string(used_classification),
detail::tracking::tracking_entry{ "cg", "total_runtime", std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time) });
Expand Down Expand Up @@ -804,6 +816,7 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l

// output the necessary information on the console
detail::log(verbosity_level::full,
comm_,
"Determining the solver type based on the available memory:\n"
" - total system memory: {2}\n"
" - usable system memory (with safety margin of min({0} %, {1}): {3}\n"
Expand Down Expand Up @@ -842,7 +855,10 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l
// use the explicit solver type
used_solver = solver_type::cg_explicit;
} else {
detail::log(verbosity_level::full, "Cannot use cg_explicit due to memory constraints on device(s) {}!\n", format_vector(failed_cg_explicit_constraints));
detail::log(verbosity_level::full,
comm_,
"Cannot use cg_explicit due to memory constraints on device(s) {}!\n",
format_vector(failed_cg_explicit_constraints));

// check whether there is enough memory available for cg_implicit
if (const std::vector<std::size_t> failed_cg_implicit_constraints = check_sizes(total_memory_needed_implicit_per_device, usable_device_memory_per_device); failed_cg_implicit_constraints.empty()) {
Expand All @@ -865,6 +881,7 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l

// output the maximum memory allocation size per device
detail::log(verbosity_level::full,
comm_,
" - maximum supported single memory allocation size: {}\n"
" - maximum needed single memory allocation size (cg_explicit): {}\n"
" - maximum needed single memory allocation size (cg_implicit): {}\n",
Expand All @@ -881,6 +898,7 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l
used_solver == solver_type::cg_explicit && !failed_cg_explicit_constraints.empty()) {
// max mem alloc size constraints not fulfilled
detail::log(verbosity_level::full,
comm_,
"Cannot use cg_explicit due to maximum single memory allocation constraints on device(s) {}! Falling back to cg_implicit.\n",
format_vector(failed_cg_explicit_constraints));
// can't use cg_explicit
Expand All @@ -890,6 +908,7 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l
used_solver == solver_type::cg_implicit && !failed_cg_implicit_constraints.empty()) {
// can't fulfill maximum single memory allocation size even for cg_implicit
plssvm::detail::log(verbosity_level::full | verbosity_level::warning,
comm_,
"WARNING: if you are sure that the guaranteed maximum memory allocation size can be safely ignored on your device, "
"this check can be disabled via \"-DPLSSVM_ENFORCE_MAX_MEM_ALLOC_SIZE=OFF\" during the CMake configuration!\n");
throw kernel_launch_resources{ fmt::format("Can't fulfill maximum single memory allocation constraint for device(s) {} even for the cg_implicit solver!", format_vector(failed_cg_implicit_constraints)) };
Expand All @@ -898,6 +917,7 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l
}

detail::log(verbosity_level::full,
comm_,
"Using {} as solver for AX=B.\n\n",
detail::tracking::tracking_entry{ "solver", "solver_type", used_solver });

Expand Down Expand Up @@ -926,6 +946,7 @@ std::tuple<aos_matrix<real_type>, std::vector<real_type>, std::vector<unsigned l

if (used_solver != solver_type::cg_implicit) {
detail::log(verbosity_level::full | verbosity_level::timing,
comm_,
"Assembled the kernel matrix in {}.\n",
assembly_duration);
}
Expand Down
Loading

0 comments on commit 27058a9

Please sign in to comment.