Attention une mise à jour du service Gitlab va être effectuée le mardi 18 janvier (et non lundi 17 comme annoncé précédemment) entre 18h00 et 18h30. Cette mise à jour va générer une interruption du service dont nous ne maîtrisons pas complètement la durée mais qui ne devrait pas excéder quelques minutes.

Commit 639ed29f authored by Quentin Khan's avatar Quentin Khan
Browse files

Perftest: new implementation

parent 93bfbe21
#include "Utils/FParameters.hpp"
#include "Utils/FParameterNames.hpp"
#include "PerfTest/FPerfTestParams.hpp"
#include "PerfTest/DescriptorLookup.hpp"
#define HOST_NAME_MAX 64
using FReal = double;
namespace ParName {
const FParameterNames Algo = {{"--algo"},"Algorithm to run (basic, task, costzones, sectiontask, autobalance)."};
const FParameterNames Kern = {{"--kernel"},"Kernel to use (lagrange)."};
const FParameterNames Density = {{"--density"},"Maximum particle count per leaf in adaptive algorihtms."};
const FParameterNames Schedule = {{"--schedule"},"OpenMP scheduling policy (static, dynamic)."};
const FParameterNames ChunkSize = {{"--chunk-size"},"OpenMP chunk size for basic dynamic algorithm."};
}
int main (int argc, char** argv)
{
// Parameter handling //////////////
FHelpDescribeAndExit(
argc, argv,
"Performance test program for FMM balancing techniques. ",
FParameterDefinitions::InputFile,
FParameterDefinitions::OctreeHeight,
FParameterDefinitions::OctreeSubHeight,
FParameterDefinitions::NbThreads,
ParName::Algo,
ParName::Kern,
ParName::Density,
ParName::Schedule,
ParName::ChunkSize);
FPerfTestParams params;
{
using namespace FParameterDefinitions;
using namespace FParameters;
params.filename = getStr(argc,argv,InputFile.options,
"../Data/unitCubeXYZQ100.bfma");
params.treeHeight = getValue(argc, argv, OctreeHeight.options, 5);
params.subTreeHeight = getValue(argc, argv, OctreeSubHeight.options, 2);
params.nbThreads = getValue(argc, argv, NbThreads.options, 1);
params.algo = getStr(argc,argv,ParName::Algo.options,"task");
params.kernel = getStr(argc,argv,ParName::Kern.options,"lagrange");
params.adaptive_density = getValue(argc, argv, ParName::Density.options, 400);
params.omp_chunk_size = getValue(argc, argv, ParName::ChunkSize.options, 0);
}
// End of Parameter handling ///////
char hostname[HOST_NAME_MAX];
memset(hostname,'\0',HOST_NAME_MAX);
if ( -1 == gethostname(hostname, HOST_NAME_MAX-1) ) {
perror("Could not get hostname");
strncpy(hostname, "unknown", HOST_NAME_MAX);
}
std::cout << "Hostname: " << hostname << std::endl;
std::cout << "Available kernels: ";
scalfmm::kernel_list<FReal>();
std::cout << "Available algorithms: ";
scalfmm::algo_list<FReal>();
scalfmm::run<FReal>(params);
}
#ifndef TRAITSMANAGEMENT_HPP
#define TRAITSMANAGEMENT_HPP
#include <memory>
#include <vector>
#include "FPerfTestParams.hpp"
#include "Descriptors.hpp"
namespace scalfmm {
namespace sfinae {
/**
* \brief Algorithm / Kernel descriptor enabled type trait
*
* Inactive algorithm and kernel descriptors define a 'disabled' value
* convertible to bool.
*/
template<typename T>
struct is_enabled {
/// c++17 will define this for SFINAE convenience
template<class ...> using void_t = void;
/// Disabled type trait definition
template<class U, class = void_t<> >
struct disabled: std::false_type {};
/// Disabled type trait found specialisation
template<class U>
struct disabled<U, void_t<decltype(U::disabled)> >
{ enum : bool { value = U::disabled }; };
/// Type trait value, true if the T is enabled
enum : bool {value = !disabled<T>::value};
};
/**
* \brief Algorithm and kernel descriptors compatibility type trait
*
* Several inner type traits are defined to check individual properties.
*
*
* \tparam kernel_desc Kernel descriptor
* \tparam algo_desc Algorithm descriptor
*/
template<typename kernel_desc, typename algo_desc>
struct compatible {
/// c++17 will define this for SFINAE convenience
template<typename... Ts> using void_t = void;
/** \brief Tree descriptor */
using tree_desc = typename algo_desc::tree_desc;
/**
* \brief Tree factory type trait definition
*
* #tree_desc should declare a tree_factory template.
*
* \tparam T Dependent name, use with #tree_desc
*/
template<typename T, typename = void_t<> >
struct has_tree_factory : std::false_type {};
/**
* \brief Tree factory type trait success specialisation
*
* #tree_desc should declare a tree_factory template.
*
* \tparam T Dependent name, use with #tree_desc
*/
template<typename T>
struct has_tree_factory<T, void_t<typename T::template
tree_factory<algo_desc, kernel_desc>
> >
: std::true_type {};
/**
* \brief Loader factory definition type trait definition
*
* #algo_desc should declare a loader_factory template.
*
* \tparam T Dependent name, use with #algo_desc
*/
template<typename T, typename = void_t<> >
struct has_loader_factory : std::false_type {};
/**
* \brief Loader factory type trait success specialisation
*
* #algo_desc should declare a loader_descriptor::loader_factory
* type.
*
* \tparam T Dependent name, use with #algo_desc
*/
template<typename T>
struct has_loader_factory<T, void_t<typename T::loader_descriptor::
loader_factory> >
: std::true_type {};
/**
* \brief Kernel factory type trait definition
*
* #kernel_desc should declare a kernel_factory template.
*
* \tparam T Dependent name, use with #kernel_desc
*/
template<typename T, typename = void_t<> >
struct has_kernel_factory : std::false_type {};
/**
* \brief Kernel factory type trait success specialisation
*
* #kernel_desc should declare a kernel_factory template.
*
* \tparam T Dependent name, use with #kernel_desc
*/
template<typename T>
struct has_kernel_factory<
T, void_t<typename T::template
kernel_factory<typename algo_desc::tree_desc> > >
: std::true_type {};
/**
* \brief Algorithm factory type trait definition
*
* #algo_desc should declare an algo_factory template.
*
* \tparam T Dependent name, use with #algo_desc
*/
template<typename T, typename = void_t<> >
struct has_algo_factory : std::false_type {};
/**
* \brief Algorithm factory type trait succes specialisation
*
* #algo_desc should declare an algo_factory template.
*
* \tparam T Dependent name, use with #algo_desc
*/
template<typename T>
struct has_algo_factory<T, void_t< typename T::template
algo_factory<kernel_desc> > >
: std::true_type {};
/// Shorthand for the tree factory type trait value
enum : bool { has_tree_factory_v =
has_tree_factory<tree_desc>::value };
/// Shorthand for the loader factory type trait value
enum : bool { has_loader_factory_v =
has_loader_factory<algo_desc>::value };
/// Shorthand for the kernel factory type trait value
enum : bool { has_kernel_factory_v =
has_kernel_factory<kernel_desc>::value };
/// Shorthand for the algorithm factory type trait value
enum : bool { has_algo_factory_v =
has_algo_factory<algo_desc>::value };
/**
* \brief Algorithm / Kernel compatibility type trait definition
*
* A given algorithm and kernel may not be compatible and make
* compilation fail. To avoid this, this type trait tries the
* `build` definition of the #algo_desc.
*
* The actual check is done by an internal structure. This one
* checks the build expression prerequsites.
*
* \warning The adequate build method may exist in the #algo_desc,
* however if it fails to compile after its definition, this will
* not catch the error. The algorithm definition must check kernel
* compatibility (using SFINAE).
*
* \tparam T unused parameter, may be anything
* \tparam unnamed SFINAE checks for build expression validity
*/
template<typename T, bool =
has_tree_factory_v
&& has_kernel_factory_v
&& has_algo_factory_v
>
struct can_build_algo {
/// Tree type shorthand
using tree_t = typename tree_desc::
template tree_factory<algo_desc, kernel_desc>::tree_t;
/// Kernel factory shorthand
using kernel_t = typename kernel_desc::
template kernel_factory<tree_desc>::kernel_t;
/// Algorithm factory shorthand
using algo_factory = typename algo_desc::
template algo_factory<kernel_desc>;
/**
* \brief Build expression type trait definition
*
* \tparam algo_t Algorithm factory type
*/
template<typename algo_f, typename = void_t<> >
struct valid_build_expression : std::false_type {};
/**
* \brief Build expression type trait success specialisation
*
* \tparam algo_t Algorithm factory type
*/
template<typename algo_f>
struct valid_build_expression<
algo_f, void_t<
decltype(std::declval<algo_f>()
.build(std::declval<FPerfTestParams>(),
std::declval<tree_t&>(),
std::declval<kernel_t&>() ))
> >
: std::true_type {};
/// Type trait value, true if the build expression is declared
enum : bool {value = valid_build_expression<algo_factory>::value};
};
/**
* \brief Algorithm / Kernel compatibility type trait failure
* specialisation
*/
template<typename T>
struct can_build_algo<T, false> : std::false_type {};
/// Shorthand for the build compatibility type trait value
enum : bool { can_build_algo_v = can_build_algo<void>::value };
/// Type trait value, true if the algorithm & kernel seem compatible
enum : bool {
value = has_tree_factory_v
&& has_loader_factory_v
&& has_kernel_factory_v
&& has_algo_factory_v
&& can_build_algo_v
};
};
}
namespace details {
namespace {
enum {max_descriptor_count = 15};
}
/**
* \brief Compile time type list
*
* Used to pass type packs using template argument deduction.
*/
template<typename ...>
struct type_list {};
/**
* \brief Last step, setup everything and run if descriptors are compatible
*
* Called once all descriptors have been found. A compile check is done to
* ensure algorithm and kernel compatibility.
*
* \tparam kernel_desc Kernel descriptor
* \tparam algo_desc Algorithm descriptor
*/
template<typename kernel_desc, typename algo_desc,
bool = sfinae::compatible<kernel_desc, algo_desc>::value>
struct setup_step {
/// Tree descriptor
using tree_desc = typename algo_desc::tree_desc;
using tree_factory = typename tree_desc::template tree_factory<algo_desc, kernel_desc>;
using loader_factory = typename algo_desc::loader_descriptor::loader_factory;
using kernel_factory = typename kernel_desc::template kernel_factory<tree_desc>;
using algo_factory = typename algo_desc::template algo_factory<kernel_desc>;
/**
* \brief Build everything and run algorithm
*
* \param params The program parameters
*/
static void run(FPerfTestParams& params) {
tree_factory tree_f;
loader_factory loader_f;
kernel_factory kernel_f;
algo_factory algo_f;
// The factories return unique_ptr specialisations
auto loader = loader_f.build(params);
auto tree = tree_f.build(params, *loader);
loader->load(*tree);
auto kernel = kernel_f.build(params, *tree);
auto algo = algo_f.build(params, *tree, *kernel);
std::cout << algo->name() << '\n';
std::cout << algo->description() << '\n';
algo->Timers["total"].tic();
algo->execute();
algo->Timers["total"].tac();
for(auto t : algo->Timers) {
std::cout << "@@ " << t.first << ": " << t.second.cumulated() << '\n';
}
}
};
/**
* \brief Last step, abort run if descriptors are not compatible
*
* \tparam kernel_desc Kernel descriptor
* \tparam algo_desc Algorithm descriptor
*/
template<typename kernel_desc, typename algo_desc>
struct setup_step<kernel_desc, algo_desc, false> {
static void run(FPerfTestParams&) {
std::cerr << "Kernel '" << kernel_desc::name << "' is incompatible"
<< " with algorithm '" << algo_desc::name << "'" << '\n';
using concept = sfinae::compatible<kernel_desc, algo_desc>;
std::cerr << "tree fact: " << concept::has_tree_factory_v << '\n';
std::cerr << "loader fact: " << concept::has_loader_factory_v << '\n';
std::cerr << "kernel fact: " << concept::has_kernel_factory_v << '\n';
std::cerr << "algo fact: " << concept::has_algo_factory_v << '\n';
std::cerr << "can build algo: " << concept::can_build_algo_v << '\n';
}
};
/**
* \brief Kernel lookup
*
* Recursive template class to find whether the kernel name given in
* `params` matches a kernel descriptor. If so, calls the final step.
*
* \tparam FReal Floating point type
* \tparam I Current kernel index
*/
template<typename FReal, std::size_t I = 0,
bool = sfinae::is_enabled<kernel_descriptor<FReal, I> >::value,
bool = I < max_descriptor_count
>
struct kernel_lookup {
/// `I`th kernel descriptor type
using kernel_desc = kernel_descriptor<FReal, I>;
/**
* \brief Kernel lookup method
*
* Checks whether `I`th kernel descriptor name matches the given
* kernel. If so, calls the final step, otherwise checks next kernel
* desriptor.
*
* \tparam other_descs Descriptors found in previous steps
*
* \param params Program parameters
*/
template<typename... other_descs>
static void check(FPerfTestParams& params, type_list<other_descs...>) {
if(params.kernel == kernel_desc::name) {
setup_step<kernel_desc, other_descs...>::run(params);
} else {
kernel_lookup<FReal, I+1>::check(params, type_list<other_descs...>{});
}
}
};
template<typename FReal, std::size_t I>
struct kernel_lookup<FReal, I, false, true> {
template<typename T>
static void check(const FPerfTestParams& params, T) {
kernel_lookup<FReal, I+1>::check(params, T{});
}
};
/**
* \brief Kernel lookup recursion end
*
* Recursive template class to find whether the kernel name given in
* `params` matches a kernel descriptor. If so, calls the final step.
*
* \tparam FReal Floating point type
* \tparam I Current kernel index
*/
template<typename FReal, std::size_t I>
struct kernel_lookup<FReal, I, false, false> {
template<typename T>
static void check(const FPerfTestParams& params, T) {
std::cerr << "Kernel " << params.kernel << " does not exist.\n";
}
};
/**
* \brief Algorithm lookup
*
* Recursive template class to find whether the algorithm name given in
* `params` matches an algorithm descriptor. If so, calls the final step.
*
* \tparam FReal Floating point type
* \tparam I Current kernel index
*/
template<typename FReal, std::size_t I = 0,
bool = sfinae::is_enabled<algo_descriptor<FReal, I> >::value,
bool = I < max_descriptor_count >
struct algorithm_lookup {
/// `I`th algorithm descriptor
using algo_desc = algo_descriptor<FReal, I>;
/**
* \brief Algorithm lookup method
*
* Checks whether `I`th algorithm descriptor name matches the given
* algorithm. If so, calls the final step, otherwise checks next
* algorithm desriptor.
*
* \tparam other_descs Descriptors found in previous steps
*
* \param params Program parameters
*/
template<typename... other_descs>
static void check(FPerfTestParams& params, type_list<other_descs...>) {
if(params.algo == algo_desc::name) {
kernel_lookup<FReal>
::check(params, type_list<algo_desc, other_descs...>{});
} else {
algorithm_lookup<FReal, I+1>::check(params, type_list<other_descs...>{});
}
}
};
template<typename FReal, std::size_t I>
struct algorithm_lookup<FReal, I, false, true> {
template<typename T>
static void check(const FPerfTestParams& params, T) {
algorithm_lookup<FReal, I+1>::check(params, T{});
}
};
/** \brief Algorithm lookup recursion end */
template<typename FReal, std::size_t I>
struct algorithm_lookup<FReal, I, false, false> {
template<typename T>
static void check(const FPerfTestParams& params, T) {
std::cerr << "Algorithm " << params.algo << " does not exist.\n";
}
};
}
/**
* \brief Look for algorithm and kernel asked in parameter and run
*
* \param params Program paramters
*/
template<typename FReal>
void run(FPerfTestParams& params) {
details::algorithm_lookup<FReal>::check(params, details::type_list<>{});
}
template<typename FReal, template<typename, std::size_t> class descriptor, std::size_t... Is>
void descriptor_list(std::index_sequence<Is...>) {
std::vector<std::string> names;
auto l = {(names.push_back(descriptor<FReal, Is>::name), 0)..., 0};
(void)l;
std::ostream_iterator<std::string> it(std::cout, ", ");
names.erase(std::remove(names.begin(), names.end(), ""), names.end());
std::copy(names.begin(), names.end()-1, it);
std::cout << names.back() << '\n';
}
template<typename FReal>
void kernel_list() {
descriptor_list<FReal, kernel_descriptor>(
std::make_index_sequence<details::max_descriptor_count>());
}
template<typename FReal>
void algo_list() {
descriptor_list<FReal, algo_descriptor>(
std::make_index_sequence<details::max_descriptor_count>());
}
}
#endif /* TRAITSMANAGEMENT_HPP */
#ifndef DESCRIPTORS_HPP
#define DESCRIPTORS_HPP
#include <cstddef>
#include <memory>
#include "Utils/make_unique.hpp"
#include "FPerfTestParams.hpp"
/**
* \brief Kernel descriptor base model
*/
template<typename FReal, std::size_t I>
struct kernel_descriptor {
/// Optional, detected via SFINAE
enum : bool {disabled = true};
/// Kernel name for runtime lookup, must be unique
constexpr static const char* name = "";
/// Cell type used by kernel
using cell_data_t = void;
/// Used after a successful kernel lookup to build the kernel
template<typename tree_desc> struct kernel_factory {
/**
* \brief Build the kernel
*
* \note Template parameters are there to explain the method
* signature. It does not need to be a template.
*
* \tparam kernel_t Kernel type, should already be known or deduced
* \tparam tree_t Tree type
*/
template<typename kernel_t, typename tree_t>
std::unique_ptr<kernel_t> build(FPerfTestParams&, tree_t&);
};
};