Skip to content

Commit

Permalink
Perf Tests: adding utilities and instantiation wrapper
Browse files Browse the repository at this point in the history
The goal of this work is to create a common core infrastructure
for the performance test in order to simplify maintenance.
Here two ideas are introduced:
1. the instantiation wrapper
2. the common input parser

both are trying to capture some of the implementation of our
performance test in generic functions that can be called instead
of duplicating logic around instantiation and command line input
parsing.

The new parsing routine checks the parameter name and that the associated
value can be casted properly. It also add some logic to remove
the arguments from argv and argc once they are parsed properly.
  • Loading branch information
lucbv committed Feb 10, 2023
1 parent aac450b commit d3ffe82
Show file tree
Hide file tree
Showing 3 changed files with 364 additions and 183 deletions.
133 changes: 133 additions & 0 deletions perf_test/KokkosKernels_perf_test_instantiation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//@HEADER
// ************************************************************************
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
// See https://kokkos.org/LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//@HEADER
//
// Created by Berger-Vergiat, Luc on 2/6/23.
//

#ifndef KOKKOSKERNELS_PERF_TEST_INSTANTIATION_HPP
#define KOKKOSKERNELS_PERF_TEST_INSTANTIATION_HPP

#include "KokkosKernels_perf_test_utilities.hpp"

#ifndef KOKKOSKERNELS_PERF_TEST_NAME
#error "The macro KOKKOSKERNELS_PERF_TEST_NAME was not defined"
#endif

int main_instantiation(int argc, char** argv) {
perf_test::CommonInputParams params;
perf_test::parse_common_options(argc, argv, params);

/* Assumption is that use_openmp/use_threads variables are */
/* provided as numbers of threads */
int num_threads = 1;
if (params.use_openmp) {
num_threads = params.use_openmp;
} else if (params.use_threads) {
num_threads = params.use_threads;
}

int device_id = 0;
if (params.use_cuda)
device_id = params.use_cuda - 1;
else if (params.use_hip)
device_id = params.use_hip - 1;
else if (params.use_sycl)
device_id = params.use_sycl - 1;

Kokkos::initialize(Kokkos::InitializationSettings()
.set_num_threads(num_threads)
.set_device_id(device_id));
Kokkos::print_configuration(std::cout);
std::cout << '\n';

bool ran = false;

if (params.use_openmp) {
#if defined(KOKKOS_ENABLE_OPENMP)
std::cout << "Running on OpenMP backend.\n";
KOKKOSKERNELS_PERF_TEST_NAME<Kokkos::OpenMP>(argc, argv, params);
ran = true;
#else
std::cout << "ERROR: OpenMP requested, but not available.\n";
Kokkos::finalize();
return 1;
#endif
}
if (params.use_threads) {
#if defined(KOKKOS_ENABLE_THREADS)
std::cout << "Running on Threads backend.\n";
KOKKOSKERNELS_PERF_TEST_NAME<Kokkos::Threads>(argc, argv, params);
ran = true;
#else
std::cout << "ERROR: Threads requested, but not available.\n";
Kokkos::finalize();
return 1;
#endif
}
if (params.use_cuda) {
#if defined(KOKKOS_ENABLE_CUDA)
std::cout << "Running on Cuda backend.\n";
KOKKOSKERNELS_PERF_TEST_NAME<Kokkos::Cuda>(argc, argv, params);
ran = true;
#else
std::cout << "ERROR: CUDA requested, but not available.\n";
Kokkos::finalize();
return 1;
#endif
}
if (params.use_hip) {
#if defined(KOKKOS_ENABLE_HIP)
std::cout << "Running on HIP backend.\n";
KOKKOSKERNELS_PERF_TEST_NAME<Kokkos::HIP>(argc, argv, params);
ran = true;
#else
std::cout << "ERROR: HIP requested, but not available.\n";
Kokkos::finalize();
return 1;
#endif
}
if (params.use_sycl) {
#if defined(KOKKOS_ENABLE_SYCL)
std::cout << "Running on SYCL backend.\n";
KOKKOSKERNELS_PERF_TEST_NAME<Kokkos::Experimental::SYCL>(argc, argv,
params);
ran = true;
#else
std::cout << "ERROR: SYCL requested, but not available.\n";
Kokkos::finalize();
return 1;
#endif
}
if (!ran) {
#if defined(KOKKOS_ENABLE_SERIAL)
std::cout << "Running on Serial backend.\n";
KOKKOSKERNELS_PERF_TEST_NAME<Kokkos::Serial>(argc, argv, params);
#else
std::cout << "ERROR: Tried to run on Serial device (as no parallel"
" backends requested), but Serial is not enabled.\n";
Kokkos::finalize();
return 1;
#endif
}
Kokkos::finalize();
return 0;
}

// Undefine the macro to avoid potential bad interaction
// with other parts of the code...
#undef KOKKOSKERNELS_PERF_TEST_NAME

#endif // KOKKOSKERNELS_PERF_TEST_INSTANTIATION_HPP
146 changes: 146 additions & 0 deletions perf_test/KokkosKernels_perf_test_utilities.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//@HEADER
// ************************************************************************
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
// See https://kokkos.org/LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//@HEADER
//
// Created by Berger-Vergiat, Luc on 2/6/23.
//

#ifndef KOKKOSKERNELS_PERF_TEST_UTILITIES_HPP
#define KOKKOSKERNELS_PERF_TEST_UTILITIES_HPP

// Namepsace that defines common utilities
// for performance tests
namespace perf_test {

struct CommonInputParams {
int use_cuda = 0;
int use_hip = 0;
int use_sycl = 0;
int use_openmp = 0;
int use_threads = 0;
};

std::string list_common_options() {
std::ostringstream common_options;
common_options
<< "\t[Required] BACKEND:\n"
<< "\t\t'--threads [numThreads]' |\n"
<< "\t\t'--openmp [numThreads]' |\n"
<< "\t\t'--cuda [deviceIndex]' |\n"
<< "\t\t'--hip [deviceIndex]' |\n"
<< "\t\t'--sycl [deviceIndex]'\n\n"
<< "\tIf no parallel backend is requested, Serial will be used "
"(if enabled)\n\n";

return common_options.str();
}

void process_arg_int(char const* str_val, int& val) {
errno = 0;
char* ptr_end;
val = std::strtol(str_val, &ptr_end, 10);

if (str_val == ptr_end) {
std::stringstream ss;
ss << "Error: cannot convert command line argument '" << str_val
<< "' to an integer.\n";
throw std::invalid_argument(ss.str());
}

if (errno == ERANGE) {
std::stringstream ss;
ss << "Error: converted value for command line argument '" << str_val
<< "' falls out of range.\n";
throw std::invalid_argument(ss.str());
}
}

bool check_arg_int(int const i, int const argc, char** argv, char const* name,
int& val) {
if (0 != Test::string_compare_no_case(argv[i], name)) {
return false;
}

if (i < argc - 1) {
process_arg_int(argv[i + 1], val);
} else {
std::stringstream msg;
msg << name << " input argument needs to be followed by an int";
throw std::invalid_argument(msg.str());
}
return true;
}

bool check_arg_bool(int const i, int const /*argc*/, char** argv,
char const* name, bool& val) {
if (0 != Test::string_compare_no_case(argv[i], name)) {
return false;
}
val = true;
return true;
}

bool check_arg_str(int const i, int const argc, char** argv, char const* name,
std::string& val) {
if (0 != Test::string_compare_no_case(argv[i], name)) {
return false;
}

if (i < argc - 1) {
val = std::string(argv[i + 1]);
} else {
std::stringstream msg;
msg << name << " input argument needs to be followed by a string";
throw std::invalid_argument(msg.str());
}
return true;
}

void parse_common_options(int& argc, char** argv, CommonInputParams& params) {
// Skip the program name, start with argIdx=1
int argIdx = 1;
while (argIdx < argc) {
bool remove_flag = false;
if (check_arg_int(argIdx, argc, argv, "--threads", params.use_threads)) {
remove_flag = true;
} else if (check_arg_int(argIdx, argc, argv, "--openmp",
params.use_openmp)) {
remove_flag = true;
} else if (check_arg_int(argIdx, argc, argv, "--cuda", params.use_cuda)) {
remove_flag = true;
} else if (check_arg_int(argIdx, argc, argv, "--hip", params.use_hip)) {
remove_flag = true;
} else if (check_arg_int(argIdx, argc, argv, "--sycl", params.use_sycl)) {
remove_flag = true;
}

if (remove_flag) {
// Shift the remainder of the argv list by one. Note that argv has
// (argc + 1) arguments, the last one always being nullptr. The following
// loop moves the trailing nullptr element as well
for (int k = argIdx; k < argc - 1; ++k) {
argv[k] = argv[k + 2];
argv[k + 1] = argv[k + 3];
}
argc = argc - 2;
} else {
++argIdx;
}
}
} // parse_common_options()

} // namespace perf_test

#endif // KOKKOSKERNELS_PERF_TEST_UTILITIES_HPP
Loading

0 comments on commit d3ffe82

Please sign in to comment.