implement kokkos runtime device configuration

This commit is contained in:
nadavi 2021-07-21 21:50:49 +00:00
parent bf6d6ca517
commit cd2a6c1385
20 changed files with 847 additions and 522 deletions

@ -15,10 +15,6 @@
#include <vtkm/cont/internal/OptionParser.h> #include <vtkm/cont/internal/OptionParser.h>
#include <vtkm/cont/internal/OptionParserArguments.h> #include <vtkm/cont/internal/OptionParserArguments.h>
#if defined(VTKM_ENABLE_KOKKOS)
#include <vtkm/cont/kokkos/internal/Initialize.h>
#endif
#include <memory> #include <memory>
#include <sstream> #include <sstream>
@ -128,12 +124,6 @@ InitializeResult Initialize(int& argc, char* argv[], InitializeOptions opts)
vtkm::cont::InitLogging(argc, argv, loggingFlag); vtkm::cont::InitLogging(argc, argv, loggingFlag);
} }
#ifdef VTKM_ENABLE_KOKKOS
// TODO: remove this once runtime config updates are completely implemented
vtkm::cont::kokkos::internal::Initialize(argc, argv);
#endif
{ // Parse VTKm options { // Parse VTKm options
std::vector<opt::Descriptor> usage; std::vector<opt::Descriptor> usage;
if ((opts & InitializeOptions::AddHelp) != InitializeOptions::None) if ((opts & InitializeOptions::AddHelp) != InitializeOptions::None)

@ -92,20 +92,20 @@ public:
} }
VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetThreads( VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetThreads(
const vtkm::Id&) const override final const vtkm::Id&) override final
{ {
throw vtkm::cont::ErrorBadDevice("Tried to set the number of threads on an invalid device"); throw vtkm::cont::ErrorBadDevice("Tried to set the number of threads on an invalid device");
} }
VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetNumaRegions( VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetNumaRegions(
const vtkm::Id&) const override final const vtkm::Id&) override final
{ {
throw vtkm::cont::ErrorBadDevice( throw vtkm::cont::ErrorBadDevice(
"Tried to set the number of numa regions on an invalid device"); "Tried to set the number of numa regions on an invalid device");
} }
VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetDeviceInstance( VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetDeviceInstance(
const vtkm::Id&) const override final const vtkm::Id&) override final
{ {
throw vtkm::cont::ErrorBadDevice("Tried to set the device instance on an invalid device"); throw vtkm::cont::ErrorBadDevice("Tried to set the device instance on an invalid device");
} }
@ -128,6 +128,18 @@ public:
{ {
throw vtkm::cont::ErrorBadDevice("Tried to get the device instance on an invalid device"); throw vtkm::cont::ErrorBadDevice("Tried to get the device instance on an invalid device");
} }
VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode GetMaxThreads(
vtkm::Id&) const override final
{
throw vtkm::cont::ErrorBadDevice("Tried to get the max number of threads on an invalid device");
}
VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode GetMaxDevices(
vtkm::Id&) const override final
{
throw vtkm::cont::ErrorBadDevice("Tried to get the max number of devices on an invalid device");
}
}; };

@ -29,8 +29,7 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagCuda>
return vtkm::cont::DeviceAdapterTagCuda{}; return vtkm::cont::DeviceAdapterTagCuda{};
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance( VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(const vtkm::Id&) override final
const vtkm::Id&) const override final
{ {
// TODO: set the cuda device instance // TODO: set the cuda device instance
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
@ -41,6 +40,11 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagCuda>
// TODO: Get the cuda device instance (also maybe a list of available devices?) // TODO: Get the cuda device instance (also maybe a list of available devices?)
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxDevices(vtkm::Id&) const override final
{
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
}; };
} // namespace vtkm::cont::internal } // namespace vtkm::cont::internal
} // namespace vtkm::cont } // namespace vtkm::cont

@ -7,6 +7,7 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information. // PURPOSE. See the above copyright notice for more information.
//============================================================================ //============================================================================
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/internal/RuntimeDeviceConfiguration.h> #include <vtkm/cont/internal/RuntimeDeviceConfiguration.h>
namespace vtkm namespace vtkm
@ -16,52 +17,112 @@ namespace cont
namespace internal namespace internal
{ {
namespace
{
VTKM_CONT
std::string RuntimeDeviceConfigReturnCodeToString(const RuntimeDeviceConfigReturnCode& code)
{
switch (code)
{
case RuntimeDeviceConfigReturnCode::SUCCESS:
return "SUCCESS";
case RuntimeDeviceConfigReturnCode::OUT_OF_BOUNDS:
return "OUT_OF_BOUNDS";
case RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE:
return "INVALID_FOR_DEVICE";
case RuntimeDeviceConfigReturnCode::INVALID_VALUE:
return "INVALID_VALUE";
case RuntimeDeviceConfigReturnCode::NOT_APPLIED:
return "NOT_APPLIED";
default:
return "";
}
}
VTKM_CONT
void LogReturnCode(const RuntimeDeviceConfigReturnCode& code,
const std::string& function,
const vtkm::Id& value,
const std::string& deviceName)
{
// Note that we intentionally are not logging a warning for INVALID_FOR_DEVICE. When a
// user provides a command line argument, it gets sent to all possible devices during
// `Initialize` regardless of whether it is used. The user does not need a lot of
// useless warnings about (for example) the serial device not supporting parameters
// intended for a real parallel device.
if (code != RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE ||
code != RuntimeDeviceConfigReturnCode::SUCCESS)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
function << " for device: " << deviceName << " had code: "
<< RuntimeDeviceConfigReturnCodeToString(code) << " with value: " << value);
}
#ifndef VTKM_ENABLE_LOGGING
(void)function;
(void)value;
(void)deviceName;
#endif
}
template <typename SetFunc>
VTKM_CONT void InitializeOption(RuntimeDeviceOption option,
SetFunc setFunc,
const std::string& funcName,
const std::string& deviceName)
{
if (option.IsSet())
{
auto value = option.GetValue();
auto code = setFunc(value);
LogReturnCode(code, funcName, value, deviceName);
}
}
} // namespace anonymous
RuntimeDeviceConfigurationBase::~RuntimeDeviceConfigurationBase() noexcept = default; RuntimeDeviceConfigurationBase::~RuntimeDeviceConfigurationBase() noexcept = default;
void RuntimeDeviceConfigurationBase::Initialize( void RuntimeDeviceConfigurationBase::Initialize(
const RuntimeDeviceConfigurationOptions& configOptions) const const RuntimeDeviceConfigurationOptions& configOptions)
{ {
if (configOptions.VTKmNumThreads.IsSet()) InitializeOption(
{ configOptions.VTKmNumThreads,
auto value = configOptions.VTKmNumThreads.GetValue(); [&](const vtkm::Id& value) { return this->SetThreads(value); },
auto code = this->SetThreads(value); "SetThreads",
this->LogReturnCode(code, "SetThreads", value); this->GetDevice().GetName());
} InitializeOption(
if (configOptions.VTKmNumaRegions.IsSet()) configOptions.VTKmNumaRegions,
{ [&](const vtkm::Id& value) { return this->SetNumaRegions(value); },
auto value = configOptions.VTKmNumaRegions.GetValue(); "SetNumaRegions",
auto code = this->SetNumaRegions(value); this->GetDevice().GetName());
this->LogReturnCode(code, "SetNumaRegions", value); InitializeOption(
} configOptions.VTKmDeviceInstance,
if (configOptions.VTKmDeviceInstance.IsSet()) [&](const vtkm::Id& value) { return this->SetDeviceInstance(value); },
{ "SetDeviceInstance",
auto value = configOptions.VTKmDeviceInstance.GetValue(); this->GetDevice().GetName());
auto code = this->SetDeviceInstance(value); this->InitializeSubsystem();
this->LogReturnCode(code, "SetDeviceInstance", value);
}
} }
void RuntimeDeviceConfigurationBase::Initialize( void RuntimeDeviceConfigurationBase::Initialize(
const RuntimeDeviceConfigurationOptions& configOptions, const RuntimeDeviceConfigurationOptions& configOptions,
int& argc, int& argc,
char* argv[]) const char* argv[])
{ {
this->ParseExtraArguments(argc, argv); this->ParseExtraArguments(argc, argv);
this->Initialize(configOptions); this->Initialize(configOptions);
} }
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetThreads(const vtkm::Id&) const RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetThreads(const vtkm::Id&)
{ {
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE; return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
} }
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetNumaRegions(const vtkm::Id&) const RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetNumaRegions(const vtkm::Id&)
{ {
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE; return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
} }
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetDeviceInstance( RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetDeviceInstance(const vtkm::Id&)
const vtkm::Id&) const
{ {
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE; return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
} }
@ -81,35 +142,20 @@ RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::GetDeviceInstance(
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE; return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
} }
void RuntimeDeviceConfigurationBase::ParseExtraArguments(int&, char*[]) const {} RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::GetMaxThreads(vtkm::Id&) const
void RuntimeDeviceConfigurationBase::LogReturnCode(const RuntimeDeviceConfigReturnCode& code,
const std::string& function,
const vtkm::Id& value) const
{ {
// Note that we intentionally are not logging a warning for INVALID_FOR_DEVICE. When a return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
// user provides a command line argument, it gets sent to all possible devices during
// `Initialize` regardless of whether it is used. The user does not need a lot of
// useless warnings about (for example) the serial device not supporting parameters
// intended for a real parallel device.
if (code == RuntimeDeviceConfigReturnCode::OUT_OF_BOUNDS)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
function << " for " << this->GetDevice().GetName()
<< "was OUT_OF_BOUNDS with value: " << value);
}
else if (code == RuntimeDeviceConfigReturnCode::INVALID_VALUE)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
function << "for " << this->GetDevice().GetName()
<< "had INVLAID_VALUE for value: " << value);
}
#ifndef VTKM_ENABLE_LOGGING
(void)function;
(void)value;
#endif
} }
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::GetMaxDevices(vtkm::Id&) const
{
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
}
void RuntimeDeviceConfigurationBase::ParseExtraArguments(int&, char*[]) {}
void RuntimeDeviceConfigurationBase::InitializeSubsystem() {}
} // namespace vtkm::cont::internal } // namespace vtkm::cont::internal
} // namespace vtkm::cont } // namespace vtkm::cont
} // namespace vtkm } // namespace vtkm

@ -29,7 +29,8 @@ enum class RuntimeDeviceConfigReturnCode
SUCCESS, SUCCESS,
OUT_OF_BOUNDS, OUT_OF_BOUNDS,
INVALID_FOR_DEVICE, INVALID_FOR_DEVICE,
INVALID_VALUE INVALID_VALUE,
NOT_APPLIED
}; };
class VTKM_CONT_EXPORT RuntimeDeviceConfigurationBase class VTKM_CONT_EXPORT RuntimeDeviceConfigurationBase
@ -44,38 +45,42 @@ public:
/// Each `Set*` method is called only if the corresponding vtk-m option is set, and a /// Each `Set*` method is called only if the corresponding vtk-m option is set, and a
/// warning is logged based on the value of the `RuntimeDeviceConfigReturnCode` returned /// warning is logged based on the value of the `RuntimeDeviceConfigReturnCode` returned
/// via the `Set*` method. /// via the `Set*` method.
VTKM_CONT void Initialize(const RuntimeDeviceConfigurationOptions& configOptions) const; VTKM_CONT void Initialize(const RuntimeDeviceConfigurationOptions& configOptions);
VTKM_CONT void Initialize(const RuntimeDeviceConfigurationOptions& configOptions, VTKM_CONT void Initialize(const RuntimeDeviceConfigurationOptions& configOptions,
int& argc, int& argc,
char* argv[]) const; char* argv[]);
/// The following public methods should be overriden in each individual device. /// The following public methods should be overriden in each individual device.
/// A method should return INVALID_FOR_DEVICE if the overriden device does not /// A method should return INVALID_FOR_DEVICE if the overriden device does not
/// support the particular set method. /// support the particular set method.
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) const; VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id& value);
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(const vtkm::Id&) const; VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(const vtkm::Id& value);
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(const vtkm::Id&) const; VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(const vtkm::Id& value);
/// The following public methods are overriden in each individual device and store the
/// values that were set via the above Set* methods for the given device.
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetThreads(vtkm::Id& value) const; VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetThreads(vtkm::Id& value) const;
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetNumaRegions(vtkm::Id& value) const; VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetNumaRegions(vtkm::Id& value) const;
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetDeviceInstance(vtkm::Id& value) const; VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetDeviceInstance(vtkm::Id& value) const;
/// The following public methods should be overriden as needed for each individual device
/// as they describe various device parameters.
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxThreads(vtkm::Id& value) const;
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxDevices(vtkm::Id& value) const;
protected: protected:
/// An overriden method that can be used to perform extra command line argument parsing /// An overriden method that can be used to perform extra command line argument parsing
/// for cases where a specific device may use additional command line arguments. At the /// for cases where a specific device may use additional command line arguments. At the
/// moment Kokkos is the only device that overrides this method. /// moment Kokkos is the only device that overrides this method.
VTKM_CONT virtual void ParseExtraArguments(int&, char*[]) const; /// Note: This method assumes that vtk-m arguments have already been parsed and removed
/// from argv.
VTKM_CONT virtual void ParseExtraArguments(int& argc, char* argv[]);
/// Used during Initialize to log a warning message dependent on the return code when /// An overriden method that can be used to perform extra initialization after Extra
/// calling a specific `Set*` method. /// Arguments are parsed and the Initialized ConfigOptions are used to call the various
/// /// Set* methods at the end of Initialize. Particuarly useful when initializing
/// params: /// additional subystems (like Kokkos).
/// code - The code to log a message for VTKM_CONT virtual void InitializeSubsystem();
/// function - The name of the `Set*` function the code was returned from
/// value - The value used as the argument to the `Set*` call.
VTKM_CONT virtual void LogReturnCode(const RuntimeDeviceConfigReturnCode& code,
const std::string& function,
const vtkm::Id& value) const;
}; };
template <typename DeviceAdapterTag> template <typename DeviceAdapterTag>

@ -9,6 +9,9 @@
//============================================================================ //============================================================================
#include <vtkm/cont/internal/RuntimeDeviceConfigurationOptions.h> #include <vtkm/cont/internal/RuntimeDeviceConfigurationOptions.h>
#include <memory>
#include <sstream>
namespace vtkm namespace vtkm
{ {
namespace cont namespace cont
@ -16,42 +19,83 @@ namespace cont
namespace internal namespace internal
{ {
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions() namespace
: VTKmNumThreads(option::OptionIndex::NUM_THREADS, "VTKM_NUM_THREADS")
, VTKmNumaRegions(option::OptionIndex::NUMA_REGIONS, "VTKM_NUMA_REGIONS")
, VTKmDeviceInstance(option::OptionIndex::DEVICE_INSTANCE, "VTKM_DEVICE_INSTANCE")
, Initialized(false)
{ {
} void AppendOptionDescriptors(std::vector<option::Descriptor>& usage,
const bool& useOptionIndex = true)
RuntimeDeviceConfigurationOptions::~RuntimeDeviceConfigurationOptions() noexcept = default;
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions(
std::vector<option::Descriptor>& usage)
: RuntimeDeviceConfigurationOptions()
{ {
usage.push_back( usage.push_back(
{ option::OptionIndex::NUM_THREADS, { useOptionIndex ? static_cast<uint32_t>(option::OptionIndex::NUM_THREADS) : 0,
0, 0,
"", "",
"vtkm-num-threads", "vtkm-num-threads",
option::VtkmArg::Required, option::VtkmArg::Required,
" --vtkm-num-threads <dev> \tSets the number of threads to use for the selected device" }); " --vtkm-num-threads <dev> \tSets the number of threads to use for the selected device" });
usage.push_back( usage.push_back(
{ option::OptionIndex::NUMA_REGIONS, { useOptionIndex ? static_cast<uint32_t>(option::OptionIndex::NUMA_REGIONS) : 1,
0, 0,
"", "",
"vtkm-numa-regions", "vtkm-numa-regions",
option::VtkmArg::Required, option::VtkmArg::Required,
" --vtkm-numa-regions <dev> \tSets the number of numa regions when using kokkos/OpenMP" }); " --vtkm-numa-regions <dev> \tSets the number of numa regions when using kokkos/OpenMP" });
usage.push_back({ option::OptionIndex::DEVICE_INSTANCE, usage.push_back(
0, { useOptionIndex ? static_cast<uint32_t>(option::OptionIndex::DEVICE_INSTANCE) : 2,
"", 0,
"vtkm-device-instance", "",
option::VtkmArg::Required, "vtkm-device-instance",
" --vtkm-device-instance <dev> \tSets the device instance to use when using " option::VtkmArg::Required,
"kokkos/cuda" }); " --vtkm-device-instance <dev> \tSets the device instance to use when using "
"kokkos/cuda" });
} }
} // anonymous namespace
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions(const bool& useOptionIndex)
: VTKmNumThreads(useOptionIndex ? option::OptionIndex::NUM_THREADS : 0, "VTKM_NUM_THREADS")
, VTKmNumaRegions(useOptionIndex ? option::OptionIndex::NUMA_REGIONS : 1, "VTKM_NUMA_REGIONS")
, VTKmDeviceInstance(useOptionIndex ? option::OptionIndex::DEVICE_INSTANCE : 2,
"VTKM_DEVICE_INSTANCE")
, Initialized(false)
{
}
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions()
: RuntimeDeviceConfigurationOptions(true)
{
}
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions(
std::vector<option::Descriptor>& usage)
: RuntimeDeviceConfigurationOptions(true)
{
AppendOptionDescriptors(usage);
}
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions(int& argc, char* argv[])
: RuntimeDeviceConfigurationOptions(false)
{
std::vector<option::Descriptor> usage;
AppendOptionDescriptors(usage, false);
usage.push_back({ option::OptionIndex::UNKNOWN, 0, "", "", option::VtkmArg::UnknownOption, "" });
usage.push_back({ 0, 0, 0, 0, 0, 0 });
option::Stats stats(usage.data(), argc, argv);
std::unique_ptr<option::Option[]> options{ new option::Option[stats.options_max] };
std::unique_ptr<option::Option[]> buffer{ new option::Option[stats.buffer_max] };
option::Parser parse(usage.data(), argc, argv, options.get(), buffer.get());
if (parse.error())
{
std::stringstream streamBuffer;
option::printUsage(streamBuffer, usage.data());
std::cerr << streamBuffer.str();
exit(1);
}
this->Initialize(options.get());
}
RuntimeDeviceConfigurationOptions::~RuntimeDeviceConfigurationOptions() noexcept = default;
void RuntimeDeviceConfigurationOptions::Initialize(const option::Option* options) void RuntimeDeviceConfigurationOptions::Initialize(const option::Option* options)
{ {

@ -30,13 +30,16 @@ namespace internal
class VTKM_CONT_EXPORT RuntimeDeviceConfigurationOptions class VTKM_CONT_EXPORT RuntimeDeviceConfigurationOptions
{ {
public: public:
/// Sets the option indices and environment varaible names for the vtkm supported options.
VTKM_CONT RuntimeDeviceConfigurationOptions(); VTKM_CONT RuntimeDeviceConfigurationOptions();
/// Calls the default constructor and additionally pushes back additional command line /// Calls the default constructor and additionally pushes back additional command line
/// options to the provided usage vector for integration with the vtkm option parser. /// options to the provided usage vector for integration with the vtkm option parser.
VTKM_CONT RuntimeDeviceConfigurationOptions(std::vector<option::Descriptor>& usage); VTKM_CONT RuntimeDeviceConfigurationOptions(std::vector<option::Descriptor>& usage);
/// Allows the caller to initialize these runtime config arguments directly from
/// command line arguments
VTKM_CONT RuntimeDeviceConfigurationOptions(int& argc, char* argv[]);
VTKM_CONT virtual ~RuntimeDeviceConfigurationOptions() noexcept; VTKM_CONT virtual ~RuntimeDeviceConfigurationOptions() noexcept;
/// Calls Initialize for each of this class's current configuration options and marks /// Calls Initialize for each of this class's current configuration options and marks
@ -48,6 +51,12 @@ public:
RuntimeDeviceOption VTKmNumaRegions; RuntimeDeviceOption VTKmNumaRegions;
RuntimeDeviceOption VTKmDeviceInstance; RuntimeDeviceOption VTKmDeviceInstance;
protected:
/// Sets the option indices and environment varaible names for the vtkm supported options.
/// If useOptionIndex is set the OptionParserArguments enum for option indices will be used,
/// otherwise ints from 0 - numOptions will be used.
VTKM_CONT RuntimeDeviceConfigurationOptions(const bool& useOptionIndex);
private: private:
bool Initialized; bool Initialized;
}; };

@ -10,6 +10,7 @@
#include <vtkm/cont/internal/RuntimeDeviceOption.h> #include <vtkm/cont/internal/RuntimeDeviceOption.h>
#include <vtkm/cont/ErrorBadValue.h> #include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/Logging.h>
#include <string> #include <string>
@ -92,6 +93,11 @@ void RuntimeDeviceOption::SetOption(const vtkm::Id& value)
vtkm::Id RuntimeDeviceOption::GetValue() const vtkm::Id RuntimeDeviceOption::GetValue() const
{ {
if (!this->IsSet())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
"GetValue() called on Argument '" << this->EnvName << "' when it was not set.");
}
return this->Value; return this->Value;
} }

@ -23,47 +23,12 @@ namespace opt = internal::option;
namespace namespace
{ {
static std::vector<std::pair<std::string, std::string>> envVars{};
enum enum
{ {
UNKNOWN, UNKNOWN,
TEST TEST
}; };
template <typename... T>
void MakeArgs(int& argc, char**& argv, T&&... args)
{
constexpr std::size_t numArgs = sizeof...(args);
std::array<std::string, numArgs> stringArgs = { { args... } };
// These static variables are declared as static so that the memory will stick around but won't
// be reported as a leak.
static std::array<std::vector<char>, numArgs> vecArgs;
static std::array<char*, numArgs + 1> finalArgs;
std::cout << " starting args:";
for (std::size_t i = 0; i < numArgs; ++i)
{
std::cout << " " << stringArgs[i];
// Safely copying a C-style string is a PITA
vecArgs[i].resize(0);
vecArgs[i].reserve(stringArgs[i].size() + 1);
for (auto&& c : stringArgs[i])
{
vecArgs[i].push_back(c);
}
vecArgs[i].push_back('\0');
finalArgs[i] = vecArgs[i].data();
}
finalArgs[numArgs] = nullptr;
std::cout << std::endl;
argc = static_cast<int>(numArgs);
argv = finalArgs.data();
}
std::unique_ptr<opt::Option[]> GetOptions(int& argc, std::unique_ptr<opt::Option[]> GetOptions(int& argc,
char** argv, char** argv,
std::vector<opt::Descriptor>& usage) std::vector<opt::Descriptor>& usage)
@ -81,25 +46,6 @@ std::unique_ptr<opt::Option[]> GetOptions(int& argc,
return options; return options;
} }
void my_setenv(const std::string& var, const std::string& value)
{
#ifdef _MSC_VER
auto iter = envVars.emplace(envVars.end(), var, value);
_putenv_s(iter->first.c_str(), iter->second.c_str());
#else
setenv(var.c_str(), value.c_str(), 1);
#endif
}
void my_unsetenv(const std::string& var)
{
#ifdef _MSC_VER
my_setenv(var, "");
#else
unsetenv(var.c_str());
#endif
}
void TestRuntimeDeviceOptionHappy() void TestRuntimeDeviceOptionHappy()
{ {
std::vector<opt::Descriptor> usage; std::vector<opt::Descriptor> usage;
@ -108,7 +54,7 @@ void TestRuntimeDeviceOptionHappy()
usage.push_back({ 0, 0, 0, 0, 0, 0 }); usage.push_back({ 0, 0, 0, 0, 0, 0 });
const std::string env{ "TEST_OPTION" }; const std::string env{ "TEST_OPTION" };
my_unsetenv(env); vtkm::cont::testing::Testing::UnsetEnv(env);
// Basic no value initialize // Basic no value initialize
{ {
@ -117,7 +63,7 @@ void TestRuntimeDeviceOptionHappy()
VTKM_TEST_ASSERT(!testOption.IsSet(), "test option should not be set"); VTKM_TEST_ASSERT(!testOption.IsSet(), "test option should not be set");
} }
my_setenv(env, "1"); vtkm::cont::testing::Testing::SetEnv(env, "1");
// Initialize from environment // Initialize from environment
{ {
@ -131,7 +77,7 @@ void TestRuntimeDeviceOptionHappy()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--test-option", "2"); vtkm::cont::testing::Testing::MakeArgs(argc, argv, "--test-option", "2");
auto options = GetOptions(argc, argv, usage); auto options = GetOptions(argc, argv, usage);
VTKM_TEST_ASSERT(options[TEST], "should be and option"); VTKM_TEST_ASSERT(options[TEST], "should be and option");
@ -158,7 +104,7 @@ void TestRuntimeDeviceOptionHappy()
VTKM_TEST_ASSERT(testOption.GetValue() == 3, "Option value should be 3"); VTKM_TEST_ASSERT(testOption.GetValue() == 3, "Option value should be 3");
} }
my_unsetenv(env); vtkm::cont::testing::Testing::UnsetEnv(env);
} }
void TestRuntimeDeviceOptionError() void TestRuntimeDeviceOptionError()
@ -169,14 +115,14 @@ void TestRuntimeDeviceOptionError()
usage.push_back({ 0, 0, 0, 0, 0, 0 }); usage.push_back({ 0, 0, 0, 0, 0, 0 });
const std::string env{ "TEST_OPTION" }; const std::string env{ "TEST_OPTION" };
my_unsetenv(env); vtkm::cont::testing::Testing::UnsetEnv(env);
bool threw = true; bool threw = true;
// Parse a non integer // Parse a non integer
{ {
internal::RuntimeDeviceOption testOption(TEST, env); internal::RuntimeDeviceOption testOption(TEST, env);
my_setenv(env, "bad"); vtkm::cont::testing::Testing::SetEnv(env, "bad");
try try
{ {
testOption.Initialize(nullptr); testOption.Initialize(nullptr);
@ -196,7 +142,7 @@ void TestRuntimeDeviceOptionError()
// Parse an integer that's too large // Parse an integer that's too large
{ {
internal::RuntimeDeviceOption testOption(TEST, env); internal::RuntimeDeviceOption testOption(TEST, env);
my_setenv(env, "9938489298493882949384989"); vtkm::cont::testing::Testing::SetEnv(env, "9938489298493882949384989");
try try
{ {
testOption.Initialize(nullptr); testOption.Initialize(nullptr);
@ -216,7 +162,7 @@ void TestRuntimeDeviceOptionError()
// Parse an integer with some stuff on the end // Parse an integer with some stuff on the end
{ {
internal::RuntimeDeviceOption testOption(TEST, env); internal::RuntimeDeviceOption testOption(TEST, env);
my_setenv(env, "100bad"); vtkm::cont::testing::Testing::SetEnv(env, "100bad");
try try
{ {
testOption.Initialize(nullptr); testOption.Initialize(nullptr);
@ -233,40 +179,11 @@ void TestRuntimeDeviceOptionError()
VTKM_TEST_ASSERT(threw, "Should have thrown"); VTKM_TEST_ASSERT(threw, "Should have thrown");
} }
my_unsetenv(env); vtkm::cont::testing::Testing::UnsetEnv(env);
} }
void TestRuntimeDeviceConfigurationOptions() void TestConfigOptionValues(const internal::RuntimeDeviceConfigurationOptions& configOptions)
{ {
std::vector<opt::Descriptor> usage;
usage.push_back({ opt::OptionIndex::DEVICE, 0, "", "tester", opt::VtkmArg::Required, "" });
usage.push_back({ opt::OptionIndex::LOGLEVEL, 0, "", "filler", opt::VtkmArg::Required, "" });
usage.push_back({ opt::OptionIndex::HELP, 0, "", "fancy", opt::VtkmArg::Required, "" });
usage.push_back(
{ opt::OptionIndex::DEPRECATED_DEVICE, 0, "", "just", opt::VtkmArg::Required, "" });
usage.push_back(
{ opt::OptionIndex::DEPRECATED_LOGLEVEL, 0, "", "gotta", opt::VtkmArg::Required, "" });
internal::RuntimeDeviceConfigurationOptions configOptions(usage);
usage.push_back({ opt::OptionIndex::UNKNOWN, 0, "", "", opt::VtkmArg::UnknownOption, "" });
usage.push_back({ 0, 0, 0, 0, 0, 0 });
int argc;
char** argv;
MakeArgs(argc,
argv,
"--vtkm-num-threads",
"100",
"--vtkm-numa-regions",
"2",
"--vtkm-device-instance",
"1");
auto options = GetOptions(argc, argv, usage);
VTKM_TEST_ASSERT(!configOptions.IsInitialized(),
"runtime config options should not be initialized");
configOptions.Initialize(options.get());
VTKM_TEST_ASSERT(configOptions.IsInitialized(), "runtime config options should be initialized"); VTKM_TEST_ASSERT(configOptions.IsInitialized(), "runtime config options should be initialized");
VTKM_TEST_ASSERT(configOptions.VTKmNumThreads.IsSet(), "num threads should be set"); VTKM_TEST_ASSERT(configOptions.VTKmNumThreads.IsSet(), "num threads should be set");
@ -278,6 +195,54 @@ void TestRuntimeDeviceConfigurationOptions()
VTKM_TEST_ASSERT(configOptions.VTKmDeviceInstance.GetValue() == 1, "device instance should == 1"); VTKM_TEST_ASSERT(configOptions.VTKmDeviceInstance.GetValue() == 1, "device instance should == 1");
} }
void TestRuntimeDeviceConfigurationOptions()
{
{
std::vector<opt::Descriptor> usage;
usage.push_back({ 0, 0, "", "need", opt::VtkmArg::Required, "" });
usage.push_back({ 1, 0, "", "filler", opt::VtkmArg::Required, "" });
usage.push_back({ 2, 0, "", "args", opt::VtkmArg::Required, "" });
usage.push_back({ 3, 0, "", "to", opt::VtkmArg::Required, "" });
usage.push_back({ 4, 0, "", "pass", opt::VtkmArg::Required, "" });
internal::RuntimeDeviceConfigurationOptions configOptions(usage);
usage.push_back({ opt::OptionIndex::UNKNOWN, 0, "", "", opt::VtkmArg::UnknownOption, "" });
usage.push_back({ 0, 0, 0, 0, 0, 0 });
int argc;
char** argv;
vtkm::cont::testing::Testing::MakeArgs(argc,
argv,
"--vtkm-num-threads",
"100",
"--vtkm-numa-regions",
"2",
"--vtkm-device-instance",
"1");
auto options = GetOptions(argc, argv, usage);
VTKM_TEST_ASSERT(!configOptions.IsInitialized(),
"runtime config options should not be initialized");
configOptions.Initialize(options.get());
TestConfigOptionValues(configOptions);
}
{
int argc;
char** argv;
vtkm::cont::testing::Testing::MakeArgs(argc,
argv,
"--vtkm-num-threads",
"100",
"--vtkm-numa-regions",
"2",
"--vtkm-device-instance",
"1");
internal::RuntimeDeviceConfigurationOptions configOptions(argc, argv);
TestConfigOptionValues(configOptions);
}
}
void TestRuntimeConfigurationOptions() void TestRuntimeConfigurationOptions()
{ {
TestRuntimeDeviceOptionHappy(); TestRuntimeDeviceOptionHappy();

@ -13,7 +13,6 @@ set(headers
DeviceAdapterMemoryManagerKokkos.h DeviceAdapterMemoryManagerKokkos.h
DeviceAdapterRuntimeDetectorKokkos.h DeviceAdapterRuntimeDetectorKokkos.h
DeviceAdapterTagKokkos.h DeviceAdapterTagKokkos.h
Initialize.h
KokkosAlloc.h KokkosAlloc.h
KokkosTypes.h KokkosTypes.h
RuntimeDeviceConfigurationKokkos.h RuntimeDeviceConfigurationKokkos.h
@ -32,7 +31,6 @@ if (TARGET vtkm::kokkos)
${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterAlgorithmKokkos.cxx ${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterAlgorithmKokkos.cxx
${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterMemoryManagerKokkos.cxx ${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterMemoryManagerKokkos.cxx
${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterRuntimeDetectorKokkos.cxx ${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterRuntimeDetectorKokkos.cxx
${CMAKE_CURRENT_SOURCE_DIR}/Initialize.cxx
${CMAKE_CURRENT_SOURCE_DIR}/KokkosAlloc.cxx ${CMAKE_CURRENT_SOURCE_DIR}/KokkosAlloc.cxx
${CMAKE_CURRENT_SOURCE_DIR}/KokkosTypes.cxx) ${CMAKE_CURRENT_SOURCE_DIR}/KokkosTypes.cxx)
target_sources(vtkm_cont PRIVATE ${sources}) target_sources(vtkm_cont PRIVATE ${sources})

@ -1,65 +0,0 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/kokkos/internal/Initialize.h>
#include <vtkm/Assert.h>
#include <vtkm/internal/Configure.h>
VTKM_THIRDPARTY_PRE_INCLUDE
#include <Kokkos_Core.hpp>
VTKM_THIRDPARTY_POST_INCLUDE
#include <cstdlib>
#include <string>
#include <vector>
namespace
{
// Performs an in-place change to the name of an argument in the parameter list.
// Requires `newName` length to be <= `origName` length.
inline void ChangeArgumentName(const std::string& origName,
const std::string& newName,
int argc,
char* argv[])
{
VTKM_ASSERT(newName.length() <= origName.length());
for (int i = 0; i < argc; ++i)
{
auto argStr = std::string(argv[i]);
auto argName = argStr.substr(0, argStr.find_first_of('='));
if (argName == origName)
{
auto newArg = newName + argStr.substr(argName.length());
newArg.copy(argv[i], newArg.length());
argv[i][newArg.length()] = '\0';
}
}
}
} // anonymous namespace
void vtkm::cont::kokkos::internal::Initialize(int& argc, char* argv[])
{
// TODO: remove this argument mangling once DEPRECATED options are fully removed
// mangle --device to prevent conflict
ChangeArgumentName("--device", "--vtkm_d", argc, argv);
// rename to what is expected by kokkos
ChangeArgumentName("--kokkos_device", "--device", argc, argv);
ChangeArgumentName("--kokkos_device-id", "--device-id", argc, argv);
if (!Kokkos::is_initialized())
{
Kokkos::initialize(argc, argv);
std::atexit(Kokkos::finalize);
}
// de-mangle
ChangeArgumentName("--vtkm_d", "--device", argc, argv);
}

@ -10,10 +10,16 @@
#ifndef vtk_m_cont_kokkos_internal_RuntimeDeviceConfigurationKokkos_h #ifndef vtk_m_cont_kokkos_internal_RuntimeDeviceConfigurationKokkos_h
#define vtk_m_cont_kokkos_internal_RuntimeDeviceConfigurationKokkos_h #define vtk_m_cont_kokkos_internal_RuntimeDeviceConfigurationKokkos_h
#include <vtkm/cont/ErrorInternal.h>
#include <vtkm/cont/internal/RuntimeDeviceConfiguration.h> #include <vtkm/cont/internal/RuntimeDeviceConfiguration.h>
#include <vtkm/cont/kokkos/internal/DeviceAdapterTagKokkos.h> #include <vtkm/cont/kokkos/internal/DeviceAdapterTagKokkos.h>
VTKM_THIRDPARTY_PRE_INCLUDE
#include <Kokkos_Core.hpp> #include <Kokkos_Core.hpp>
VTKM_THIRDPARTY_POST_INCLUDE
#include <cstring>
#include <vector>
namespace vtkm namespace vtkm
{ {
@ -22,6 +28,52 @@ namespace cont
namespace internal namespace internal
{ {
namespace
{
VTKM_CONT
RuntimeDeviceConfigReturnCode GetArgFromList(const std::vector<std::string>& argList,
const std::string& argName,
vtkm::Id& value)
{
size_t pos;
try
{
for (auto argItr = argList.rbegin(); argItr != argList.rend(); argItr++)
{
if (argItr->rfind(argName, 0) == 0)
{
if (argItr->size() == argName.size())
{
value = std::stoi(*(--argItr), &pos, 10);
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
else
{
value = std::stoi(argItr->substr(argName.size() + 1), &pos, 10);
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
}
}
}
catch (const std::invalid_argument&)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Unable to get arg " + argName +
"from kokkos argList, invalid argument thrown... This shouldn't have happened");
return RuntimeDeviceConfigReturnCode::INVALID_VALUE;
}
catch (const std::out_of_range&)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Unable to get arg " + argName +
"from kokkos argList, out of range thrown... This shouldn't have happened");
return RuntimeDeviceConfigReturnCode::INVALID_VALUE;
}
return RuntimeDeviceConfigReturnCode::NOT_APPLIED;
}
} // namespace anonymous
template <> template <>
class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagKokkos> class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagKokkos>
: public vtkm::cont::internal::RuntimeDeviceConfigurationBase : public vtkm::cont::internal::RuntimeDeviceConfigurationBase
@ -32,54 +84,114 @@ public:
return vtkm::cont::DeviceAdapterTagKokkos{}; return vtkm::cont::DeviceAdapterTagKokkos{};
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) const override final VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id& value) override final
{ {
// TODO: set the kokkos threads if (Kokkos::is_initialized())
{
VTKM_LOG_S(
vtkm::cont::LogLevel::Warn,
"SetThreads was called but Kokkos was already initailized! Updates will not be applied.");
return RuntimeDeviceConfigReturnCode::NOT_APPLIED;
}
this->KokkosArguments.insert(this->KokkosArguments.begin(),
"--kokkos-threads=" + std::to_string(value));
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions( VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(
const vtkm::Id&) const override final const vtkm::Id& value) override final
{ {
// TODO: set the kokkos numa regions if (Kokkos::is_initialized())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
"SetNumaRegions was called but Kokkos was already initailized! Updates will not "
"be applied.");
return RuntimeDeviceConfigReturnCode::NOT_APPLIED;
}
this->KokkosArguments.insert(this->KokkosArguments.begin(),
"--kokkos-numa=" + std::to_string(value));
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance( VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(
const vtkm::Id&) const override final const vtkm::Id& value) override final
{ {
// TODO: set the kokkos device instance if (Kokkos::is_initialized())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
"SetDeviceInstance was called but Kokkos was already initailized! Updates will "
"not be applied.");
return RuntimeDeviceConfigReturnCode::NOT_APPLIED;
}
this->KokkosArguments.insert(this->KokkosArguments.begin(),
"--kokkos-device-id=" + std::to_string(value));
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetThreads(vtkm::Id&) const override final VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetThreads(vtkm::Id& value) const override final
{ {
// TODO: get the kokkos threads return GetArgFromList(this->KokkosArguments, "--kokkos-threads", value);
return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetNumaRegions(vtkm::Id&) const override final VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetNumaRegions(
vtkm::Id& value) const override final
{ {
// TODO: get the kokkos numa regions return GetArgFromList(this->KokkosArguments, "--kokkos-numa", value);
return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetDeviceInstance(vtkm::Id&) const override final VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetDeviceInstance(
vtkm::Id& value) const override final
{ {
// TODO: get the kokkos device instance return GetArgFromList(this->KokkosArguments, "--kokkos-device-id", value);
return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
protected: protected:
VTKM_CONT virtual void ParseExtraArguments(int&, char*[]) const override final /// Store a copy of the current arguments when initializing the Kokkos subsystem later
/// Appends a copy of the argv values in the KokkosArguments vector: this assumes the
/// argv values contain kokkos command line arguments (like --kokkos-threads, etc)
VTKM_CONT virtual void ParseExtraArguments(int& argc, char* argv[]) override final
{ {
// TODO: ugh, kokkos. Manually parse the kokkos config args, store them for usage if (argc > 0 && argv)
{
this->KokkosArguments.insert(this->KokkosArguments.end(), argv, argv + argc);
}
}
/// Calls kokkos initiailze if kokkos has not been initialized yet and sets up an atexit
/// to call kokkos finalize. Converts the KokkosArguments vector to a standard argc/argv
/// list of arguments when calling kokkos initialize.
///
/// When using vtkm::Initialize, the standard order for kokkos argument priority is as
/// follows (this assumes kokkos still prioritizes arguments found at the end of the
/// argv list over similarly named arguements found earlier in the list):
/// 1. Environment Variables
/// 2. Kokkos Command Line Arguments
/// 3. VTK-m Interpreted Command Line Arguements
VTKM_CONT virtual void InitializeSubsystem() override final
{
if (!Kokkos::is_initialized())
{
std::vector<char*> argv;
for (auto& arg : this->KokkosArguments)
{
argv.push_back(&arg[0]);
}
int size = argv.size();
Kokkos::initialize(size, argv.data());
std::atexit(Kokkos::finalize);
}
else
{
VTKM_LOG_S(
vtkm::cont::LogLevel::Warn,
"Attempted to Re-initialize Kokkos! The Kokkos subsystem can only be initialized once");
}
} }
private: private:
Kokkos::InitArguments ParsedCommandLineArgs; std::vector<std::string> KokkosArguments;
Kokkos::InitArguments VTKmInitializedArgs;
}; };
} // namespace vtkm::cont::internal } // namespace vtkm::cont::internal
} // namespace vtkm::cont } // namespace vtkm::cont
} // namespace vtkm } // namespace vtkm

@ -23,6 +23,7 @@ set(unit_tests
UnitTestKokkosDeviceAdapter.cxx UnitTestKokkosDeviceAdapter.cxx
UnitTestKokkosImplicitFunction.cxx UnitTestKokkosImplicitFunction.cxx
UnitTestKokkosPointLocatorSparseGrid.cxx UnitTestKokkosPointLocatorSparseGrid.cxx
UnitTestKokkosRuntimeDeviceConfiguration.cxx
) )
if (NOT VTKm_NO_DEPRECATED_VIRTUAL) if (NOT VTKm_NO_DEPRECATED_VIRTUAL)

@ -7,22 +7,13 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information. // PURPOSE. See the above copyright notice for more information.
//============================================================================ //============================================================================
#ifndef vtk_m_cont_kokkos_internal_Initialize_h #include <vtkm/cont/RuntimeDeviceTracker.h>
#define vtk_m_cont_kokkos_internal_Initialize_h #include <vtkm/cont/kokkos/DeviceAdapterKokkos.h>
#include <vtkm/cont/testing/TestingRuntimeDeviceConfiguration.h>
namespace vtkm int UnitTestKokkosRuntimeDeviceConfiguration(int argc, char* argv[])
{ {
namespace cont vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagKokkos{});
{ return vtkm::cont::testing::TestingRuntimeDeviceConfiguration<
namespace kokkos vtkm::cont::DeviceAdapterTagKokkos>::Run(argc, argv);
{
namespace internal
{
void Initialize(int& argc, char* argv[]);
} }
}
}
} // vtkm::cont::kokkos::internal
#endif // vtk_m_cont_kokkos_internal_Initialize_h

@ -29,14 +29,13 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagOpenMP>
return vtkm::cont::DeviceAdapterTagOpenMP{}; return vtkm::cont::DeviceAdapterTagOpenMP{};
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) const override final VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) override final
{ {
// TODO: Set the threads in OpenMP // TODO: Set the threads in OpenMP
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions( VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(const vtkm::Id&) override final
const vtkm::Id&) const override final
{ {
// TODO: Set the numa regions in OpenMP // TODO: Set the numa regions in OpenMP
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
@ -53,6 +52,11 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagOpenMP>
// TODO: Get the number of OpenMP NumaRegions // TODO: Get the number of OpenMP NumaRegions
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxThreads(vtkm::Id&) const override final
{
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
}; };
} // namespace vtkm::cont::internal } // namespace vtkm::cont::internal
} // namespace vtkm::cont } // namespace vtkm::cont

@ -29,7 +29,7 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagTBB>
return vtkm::cont::DeviceAdapterTagTBB{}; return vtkm::cont::DeviceAdapterTagTBB{};
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) const override final VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) override final
{ {
// TODO: vtk-m set the number of global threads // TODO: vtk-m set the number of global threads
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
@ -40,6 +40,11 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagTBB>
// TODO: Get number of TBB threads here (essentially just threads supported by architecture) // TODO: Get number of TBB threads here (essentially just threads supported by architecture)
return RuntimeDeviceConfigReturnCode::SUCCESS; return RuntimeDeviceConfigReturnCode::SUCCESS;
} }
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxThreads(vtkm::Id&) const override final
{
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
}; };
} // namespace vktm::cont::internal } // namespace vktm::cont::internal
} // namespace vtkm::cont } // namespace vtkm::cont

@ -25,6 +25,7 @@ set(headers
TestingFancyArrayHandles.h TestingFancyArrayHandles.h
TestingImplicitFunction.h TestingImplicitFunction.h
TestingPointLocatorSparseGrid.h TestingPointLocatorSparseGrid.h
TestingRuntimeDeviceConfiguration.h
TestingSerialization.h TestingSerialization.h
TestingVirtualObjectHandle.h TestingVirtualObjectHandle.h
) )

@ -101,20 +101,8 @@ public:
} }
template <class Func> template <class Func>
static VTKM_CONT int Run(Func function, int& argc, char* argv[]) static VTKM_CONT int ExecuteFunction(Func function)
{ {
std::unique_ptr<vtkmdiy::mpi::environment> env_diy = nullptr;
if (!vtkmdiy::mpi::environment::initialized())
{
env_diy.reset(new vtkmdiy::mpi::environment(argc, argv));
}
vtkm::cont::Initialize(argc, argv);
ParseAdditionalTestArgs(argc, argv);
// Turn on floating point exception trapping where available
vtkm::testing::FloatingPointExceptionTrapEnable();
try try
{ {
function(); function();
@ -144,6 +132,23 @@ public:
return 0; return 0;
} }
template <class Func>
static VTKM_CONT int Run(Func function, int& argc, char* argv[])
{
std::unique_ptr<vtkmdiy::mpi::environment> env_diy = nullptr;
if (!vtkmdiy::mpi::environment::initialized())
{
env_diy.reset(new vtkmdiy::mpi::environment(argc, argv));
}
vtkm::cont::Initialize(argc, argv);
ParseAdditionalTestArgs(argc, argv);
// Turn on floating point exception trapping where available
vtkm::testing::FloatingPointExceptionTrapEnable();
return ExecuteFunction(function);
}
template <class Func> template <class Func>
static VTKM_CONT int RunOnDevice(Func function, int argc, char* argv[]) static VTKM_CONT int RunOnDevice(Func function, int argc, char* argv[])
{ {
@ -151,33 +156,66 @@ public:
auto config = vtkm::cont::Initialize(argc, argv, opts); auto config = vtkm::cont::Initialize(argc, argv, opts);
ParseAdditionalTestArgs(argc, argv); ParseAdditionalTestArgs(argc, argv);
try return ExecuteFunction([&]() { function(config.Device); });
}
template <typename... T>
static VTKM_CONT void MakeArgs(int& argc, char**& argv, T&&... args)
{
constexpr std::size_t numArgs = sizeof...(args);
std::array<std::string, numArgs> stringArgs = { { args... } };
// These static variables are declared as static so that the memory will stick around but won't
// be reported as a leak.
static std::array<std::vector<char>, numArgs> vecArgs;
static std::array<char*, numArgs + 1> finalArgs;
std::cout << " starting args:";
for (std::size_t i = 0; i < numArgs; ++i)
{ {
function(config.Device); std::cout << " " << stringArgs[i];
// Safely copying a C-style string is a PITA
vecArgs[i].resize(0);
vecArgs[i].reserve(stringArgs[i].size() + 1);
for (auto&& c : stringArgs[i])
{
vecArgs[i].push_back(c);
}
vecArgs[i].push_back('\0');
finalArgs[i] = vecArgs[i].data();
} }
catch (vtkm::testing::Testing::TestFailure& error) finalArgs[numArgs] = nullptr;
{ std::cout << std::endl;
std::cerr << "Error at " << error.GetFile() << ":" << error.GetLine() << ":"
<< error.GetFunction() << "\n\t" << error.GetMessage() << "\n"; argc = static_cast<int>(numArgs);
return 1; argv = finalArgs.data();
} }
catch (vtkm::cont::Error& error)
{ template <typename... T>
std::cerr << "Uncaught VTKm exception thrown.\n" << error.GetMessage() << "\n"; static VTKM_CONT void MakeArgsAddProgramName(int& argc, char**& argv, T&&... args)
std::cerr << "Stacktrace:\n" << error.GetStackTrace() << "\n"; {
return 1; MakeArgs(argc, argv, "program-name", args...);
} }
catch (std::exception& error)
{ static void SetEnv(const std::string& var, const std::string& value)
std::cerr << "STL exception throw.\n\t" << error.what() << "\n"; {
return 1; static std::vector<std::pair<std::string, std::string>> envVars{};
} #ifdef _MSC_VER
catch (...) auto iter = envVars.emplace(envVars.end(), var, value);
{ _putenv_s(iter->first.c_str(), iter->second.c_str());
std::cerr << "Unidentified exception thrown.\n"; #else
return 1; setenv(var.c_str(), value.c_str(), 1);
} #endif
return 0; }
static void UnsetEnv(const std::string& var)
{
#ifdef _MSC_VER
SetEnv(var, "");
#else
unsetenv(var.c_str());
#endif
} }
private: private:
@ -248,143 +286,141 @@ private:
// Method to parse the extra arguments given to unit tests // Method to parse the extra arguments given to unit tests
static VTKM_CONT void ParseAdditionalTestArgs(int& argc, char* argv[]) static VTKM_CONT void ParseAdditionalTestArgs(int& argc, char* argv[])
{ {
{ // Parse test arguments std::vector<opt::Descriptor> usage;
std::vector<opt::Descriptor> usage;
usage.push_back({ DATADIR, usage.push_back({ DATADIR,
0, 0,
"", "",
"vtkm-data-dir", "vtkm-data-dir",
opt::VtkmArg::Required, opt::VtkmArg::Required,
" --vtkm-data-dir " " --vtkm-data-dir "
"<data-dir-path> \tPath to the " "<data-dir-path> \tPath to the "
"base data directory in the VTK-m " "base data directory in the VTK-m "
"src dir." }); "src dir." });
usage.push_back({ BASELINEDIR, usage.push_back({ BASELINEDIR,
0, 0,
"", "",
"vtkm-baseline-dir", "vtkm-baseline-dir",
opt::VtkmArg::Required, opt::VtkmArg::Required,
" --vtkm-baseline-dir " " --vtkm-baseline-dir "
"<baseline-dir-path> " "<baseline-dir-path> "
"\tPath to the base dir " "\tPath to the base dir "
"for regression test " "for regression test "
"images" }); "images" });
usage.push_back({ WRITEDIR, usage.push_back({ WRITEDIR,
0, 0,
"", "",
"vtkm-write-dir", "vtkm-write-dir",
opt::VtkmArg::Required, opt::VtkmArg::Required,
" --vtkm-write-dir " " --vtkm-write-dir "
"<write-dir-path> " "<write-dir-path> "
"\tPath to the write dir " "\tPath to the write dir "
"to store generated " "to store generated "
"regression test images" }); "regression test images" });
usage.push_back({ DEPRECATED_DATADIR, usage.push_back({ DEPRECATED_DATADIR,
0, 0,
"D", "D",
"data-dir", "data-dir",
opt::VtkmArg::Required, opt::VtkmArg::Required,
" --data-dir " " --data-dir "
"<data-dir-path> " "<data-dir-path> "
"\tDEPRECATED: use --vtkm-data-dir instead" }); "\tDEPRECATED: use --vtkm-data-dir instead" });
usage.push_back({ DEPRECATED_BASELINEDIR, usage.push_back({ DEPRECATED_BASELINEDIR,
0, 0,
"B", "B",
"baseline-dir", "baseline-dir",
opt::VtkmArg::Required, opt::VtkmArg::Required,
" --baseline-dir " " --baseline-dir "
"<baseline-dir-path> " "<baseline-dir-path> "
"\tDEPRECATED: use --vtkm-baseline-dir instead" }); "\tDEPRECATED: use --vtkm-baseline-dir instead" });
usage.push_back({ WRITEDIR, usage.push_back({ WRITEDIR,
0, 0,
"", "",
"write-dir", "write-dir",
opt::VtkmArg::Required, opt::VtkmArg::Required,
" --write-dir " " --write-dir "
"<write-dir-path> " "<write-dir-path> "
"\tDEPRECATED: use --vtkm-write-dir instead" }); "\tDEPRECATED: use --vtkm-write-dir instead" });
// Required to collect unknown arguments when help is off. // Required to collect unknown arguments when help is off.
usage.push_back({ TEST_UNKNOWN, 0, "", "", opt::VtkmArg::UnknownOption, "" }); usage.push_back({ TEST_UNKNOWN, 0, "", "", opt::VtkmArg::UnknownOption, "" });
usage.push_back({ 0, 0, 0, 0, 0, 0 }); usage.push_back({ 0, 0, 0, 0, 0, 0 });
// Remove argv[0] (executable name) if present: // Remove argv[0] (executable name) if present:
int vtkmArgc = argc > 0 ? argc - 1 : 0; int vtkmArgc = argc > 0 ? argc - 1 : 0;
char** vtkmArgv = argc > 0 ? argv + 1 : argv; char** vtkmArgv = argc > 0 ? argv + 1 : argv;
opt::Stats stats(usage.data(), vtkmArgc, vtkmArgv); opt::Stats stats(usage.data(), vtkmArgc, vtkmArgv);
std::unique_ptr<opt::Option[]> options{ new opt::Option[stats.options_max] }; std::unique_ptr<opt::Option[]> options{ new opt::Option[stats.options_max] };
std::unique_ptr<opt::Option[]> buffer{ new opt::Option[stats.buffer_max] }; std::unique_ptr<opt::Option[]> buffer{ new opt::Option[stats.buffer_max] };
opt::Parser parse(usage.data(), vtkmArgc, vtkmArgv, options.get(), buffer.get()); opt::Parser parse(usage.data(), vtkmArgc, vtkmArgv, options.get(), buffer.get());
if (parse.error()) if (parse.error())
{ {
std::cerr << "Internal Initialize parser error" << std::endl; std::cerr << "Internal Initialize parser error" << std::endl;
exit(1); exit(1);
} }
if (options[DEPRECATED_DATADIR]) if (options[DEPRECATED_DATADIR])
{ {
VTKM_LOG_S(vtkm::cont::LogLevel::Error, VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated datadir flag: " "Supplied deprecated datadir flag: "
<< std::string{ options[DEPRECATED_DATADIR].name } << std::string{ options[DEPRECATED_DATADIR].name }
<< ", use --vtkm-data-dir instead"); << ", use --vtkm-data-dir instead");
SetAndGetTestDataBasePath(options[DEPRECATED_DATADIR].arg); SetAndGetTestDataBasePath(options[DEPRECATED_DATADIR].arg);
} }
if (options[DEPRECATED_BASELINEDIR]) if (options[DEPRECATED_BASELINEDIR])
{ {
VTKM_LOG_S(vtkm::cont::LogLevel::Error, VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated baselinedir flag: " "Supplied deprecated baselinedir flag: "
<< std::string{ options[DEPRECATED_BASELINEDIR].name } << std::string{ options[DEPRECATED_BASELINEDIR].name }
<< ", use --vtkm-baseline-dir instead"); << ", use --vtkm-baseline-dir instead");
SetAndGetRegressionImageBasePath(options[DEPRECATED_BASELINEDIR].arg); SetAndGetRegressionImageBasePath(options[DEPRECATED_BASELINEDIR].arg);
} }
if (options[DEPRECATED_WRITEDIR]) if (options[DEPRECATED_WRITEDIR])
{ {
VTKM_LOG_S(vtkm::cont::LogLevel::Error, VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated writedir flag: " "Supplied deprecated writedir flag: "
<< std::string{ options[DEPRECATED_WRITEDIR].name } << std::string{ options[DEPRECATED_WRITEDIR].name }
<< ", use --vtkm-write-dir instead"); << ", use --vtkm-write-dir instead");
SetAndGetWriteDirBasePath(options[DEPRECATED_WRITEDIR].arg); SetAndGetWriteDirBasePath(options[DEPRECATED_WRITEDIR].arg);
} }
if (options[DATADIR]) if (options[DATADIR])
{ {
SetAndGetTestDataBasePath(options[DATADIR].arg); SetAndGetTestDataBasePath(options[DATADIR].arg);
} }
if (options[BASELINEDIR]) if (options[BASELINEDIR])
{ {
SetAndGetRegressionImageBasePath(options[BASELINEDIR].arg); SetAndGetRegressionImageBasePath(options[BASELINEDIR].arg);
} }
if (options[WRITEDIR]) if (options[WRITEDIR])
{ {
SetAndGetWriteDirBasePath(options[WRITEDIR].arg); SetAndGetWriteDirBasePath(options[WRITEDIR].arg);
} }
for (const opt::Option* opt = options[TEST_UNKNOWN]; opt != nullptr; opt = opt->next()) for (const opt::Option* opt = options[TEST_UNKNOWN]; opt != nullptr; opt = opt->next())
{ {
VTKM_LOG_S(vtkm::cont::LogLevel::Info, VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Unknown option to internal Initialize: " << opt->name << "\n"); "Unknown option to internal Initialize: " << opt->name << "\n");
} }
for (int nonOpt = 0; nonOpt < parse.nonOptionsCount(); ++nonOpt) for (int nonOpt = 0; nonOpt < parse.nonOptionsCount(); ++nonOpt)
{ {
VTKM_LOG_S(vtkm::cont::LogLevel::Info, VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Unknown argument to internal Initialize: " << parse.nonOption(nonOpt) << "\n"); "Unknown argument to internal Initialize: " << parse.nonOption(nonOpt) << "\n");
}
} }
} }
}; };
}
}
} // namespace vtkm::cont::testing } // namespace vtkm::cont::testing
} // namespace vtkm::cont
} // namespace vtkm
//============================================================================ //============================================================================
template <typename T1, typename T2, typename StorageTag1, typename StorageTag2> template <typename T1, typename T2, typename StorageTag1, typename StorageTag2>

@ -0,0 +1,185 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_cont_testing_TestingRuntimeDeviceConfiguration_h
#define vtk_m_cont_testing_TestingRuntimeDeviceConfiguration_h
#include <vtkm/cont/DeviceAdapterTag.h>
#include <vtkm/cont/RuntimeDeviceInformation.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/internal/RuntimeDeviceConfiguration.h>
#include <vtkm/cont/internal/RuntimeDeviceConfigurationOptions.h>
#include <vtkm/cont/testing/Testing.h>
namespace internal = vtkm::cont::internal;
namespace vtkm
{
namespace cont
{
namespace testing
{
template <class DeviceAdapterTag>
struct TestingRuntimeDeviceConfiguration
{
VTKM_CONT
static internal::RuntimeDeviceConfigurationOptions DefaultInitializeConfigOptions()
{
internal::RuntimeDeviceConfigurationOptions runtimeDeviceOptions{};
runtimeDeviceOptions.VTKmNumThreads.SetOption(8);
runtimeDeviceOptions.VTKmNumaRegions.SetOption(0);
runtimeDeviceOptions.VTKmDeviceInstance.SetOption(2);
runtimeDeviceOptions.Initialize(nullptr);
VTKM_TEST_ASSERT(runtimeDeviceOptions.IsInitialized(),
"Failed to default initialize runtime config options.");
return runtimeDeviceOptions;
}
VTKM_CONT
static void TestSerial() {}
VTKM_CONT
static void TestTBB() {}
VTKM_CONT
static void TestOpenMP() {}
VTKM_CONT
static void TestCuda() {}
VTKM_CONT
static void TestKokkos()
{
int argc;
char** argv;
vtkm::cont::testing::Testing::MakeArgs(argc, argv, "--kokkos-numa=4");
vtkm::cont::testing::Testing::SetEnv("KOKKOS_DEVICE_ID", "0");
auto deviceOptions = TestingRuntimeDeviceConfiguration::DefaultInitializeConfigOptions();
bool threw = false;
try
{
RuntimeDeviceInformation{}.GetRuntimeConfiguration(
DeviceAdapterTag(), deviceOptions, argc, argv);
}
catch (const std::runtime_error& e)
{
threw = true;
}
VTKM_TEST_ASSERT(
threw, "GetRuntimeConfiguration should have thrown, env KOKKOS_DEVICE_ID didn't match");
VTKM_TEST_ASSERT(!Kokkos::is_initialized(), "Kokkos should not be initialized at this point");
deviceOptions.VTKmDeviceInstance.SetOption(0);
internal::RuntimeDeviceConfigurationBase& config =
RuntimeDeviceInformation{}.GetRuntimeConfiguration(
DeviceAdapterTag(), deviceOptions, argc, argv);
VTKM_TEST_ASSERT(Kokkos::is_initialized(), "Kokkos should be initialized at this point");
// Test that args are set and the right arg priority is applied
vtkm::Id testValue;
VTKM_TEST_ASSERT(config.GetThreads(testValue) ==
internal::RuntimeDeviceConfigReturnCode::SUCCESS,
"Failed to get set threads");
VTKM_TEST_ASSERT(testValue == 8,
"Set threads does not match expected value: 8 != " +
std::to_string(testValue));
VTKM_TEST_ASSERT(config.GetNumaRegions(testValue) ==
internal::RuntimeDeviceConfigReturnCode::SUCCESS,
"Failed to get set numa regions");
VTKM_TEST_ASSERT(testValue == 4,
"Set numa regions does not match expected value: 4 != " +
std::to_string(testValue));
VTKM_TEST_ASSERT(config.GetDeviceInstance(testValue) ==
internal::RuntimeDeviceConfigReturnCode::SUCCESS,
"Failed to get set device instance");
VTKM_TEST_ASSERT(testValue == 0,
"Set device instance does not match expected value: 0 != " +
std::to_string(testValue));
// Ensure that with kokkos we can't re-initialize or set values after the first initialize
// Should pop up a few warnings in the test logs
deviceOptions.VTKmNumThreads.SetOption(16);
deviceOptions.VTKmNumaRegions.SetOption(2);
deviceOptions.VTKmDeviceInstance.SetOption(5);
config.Initialize(deviceOptions);
VTKM_TEST_ASSERT(config.SetThreads(1) == internal::RuntimeDeviceConfigReturnCode::NOT_APPLIED,
"Shouldn't be able to set threads after kokkos is initalized");
VTKM_TEST_ASSERT(config.SetNumaRegions(1) ==
internal::RuntimeDeviceConfigReturnCode::NOT_APPLIED,
"Shouldn't be able to set numa regions after kokkos is initalized");
VTKM_TEST_ASSERT(config.SetDeviceInstance(1) ==
internal::RuntimeDeviceConfigReturnCode::NOT_APPLIED,
"Shouldn't be able to set device instnace after kokkos is initalized");
// make sure all the values are the same
VTKM_TEST_ASSERT(config.GetThreads(testValue) ==
internal::RuntimeDeviceConfigReturnCode::SUCCESS,
"Failed to get set threads");
VTKM_TEST_ASSERT(testValue == 8,
"Set threads does not match expected value: 8 != " +
std::to_string(testValue));
VTKM_TEST_ASSERT(config.GetNumaRegions(testValue) ==
internal::RuntimeDeviceConfigReturnCode::SUCCESS,
"Failed to get set numa regions");
VTKM_TEST_ASSERT(testValue == 4,
"Set numa regions does not match expected value: 4 != " +
std::to_string(testValue));
VTKM_TEST_ASSERT(config.GetDeviceInstance(testValue) ==
internal::RuntimeDeviceConfigReturnCode::SUCCESS,
"Failed to get set device instance");
VTKM_TEST_ASSERT(testValue == 0,
"Set device instance does not match expected value: 0 != " +
std::to_string(testValue));
vtkm::cont::testing::Testing::UnsetEnv("KOKKOS_DEVICE_ID");
}
struct TestRunner
{
VTKM_CONT
void operator()() const
{
switch (DeviceAdapterTag{}.GetValue())
{
case vtkm::cont::DeviceAdapterTagSerial{}.GetValue():
TestingRuntimeDeviceConfiguration::TestSerial();
break;
case vtkm::cont::DeviceAdapterTagTBB{}.GetValue():
TestingRuntimeDeviceConfiguration::TestTBB();
break;
case vtkm::cont::DeviceAdapterTagOpenMP{}.GetValue():
TestingRuntimeDeviceConfiguration::TestOpenMP();
break;
case vtkm::cont::DeviceAdapterTagCuda{}.GetValue():
TestingRuntimeDeviceConfiguration::TestCuda();
break;
case vtkm::cont::DeviceAdapterTagKokkos{}.GetValue():
TestingRuntimeDeviceConfiguration::TestKokkos();
break;
default:
break;
}
}
};
static VTKM_CONT int Run(int, char*[])
{
// For the Kokkos version of this test we don't not want Initialize to be called
// so we directly execute the testing functor instead of calling Run
vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(DeviceAdapterTag());
return vtkm::cont::testing::Testing::ExecuteFunction(TestRunner{});
}
};
} // namespace vtkm::cont::testing
} // namespace vtkm::cont
} // namespace vtkm
#endif // vtk_m_cont_testing_TestingRuntimeDeviceConfiguration_h

@ -15,47 +15,12 @@
namespace namespace
{ {
constexpr const char* PROGRAM_NAME = "program-name";
template <typename... T>
void MakeArgs(int& argc, char**& argv, T&&... args)
{
constexpr std::size_t numArgs = sizeof...(args) + 1;
std::array<std::string, numArgs> stringArgs = { { PROGRAM_NAME, args... } };
// These static variables are declared as static so that the memory will stick around but won't
// be reported as a leak.
static std::array<std::vector<char>, numArgs> vecArgs;
static std::array<char*, numArgs + 1> finalArgs;
std::cout << " starting args:";
for (std::size_t i = 0; i < numArgs; ++i)
{
std::cout << " " << stringArgs[i];
// Safely copying a C-style string is a PITA
vecArgs[i].resize(0);
vecArgs[i].reserve(stringArgs[i].size() + 1);
for (auto&& c : stringArgs[i])
{
vecArgs[i].push_back(c);
}
vecArgs[i].push_back('\0');
finalArgs[i] = vecArgs[i].data();
}
finalArgs[numArgs] = nullptr;
std::cout << std::endl;
argc = static_cast<int>(numArgs);
argv = finalArgs.data();
}
template <typename... T> template <typename... T>
void CheckArgs(int argc, char* argv[], T&&... args) void CheckArgs(int argc, char* argv[], T&&... args)
{ {
constexpr std::size_t numArgs = sizeof...(args) + 1; constexpr std::size_t numArgs = sizeof...(args) + 1;
std::array<std::string, numArgs> expectedArgs = { { PROGRAM_NAME, args... } }; std::array<std::string, numArgs> expectedArgs = { { "program-name", args... } };
std::cout << " expected args:"; std::cout << " expected args:";
for (std::size_t i = 0; i < numArgs; ++i) for (std::size_t i = 0; i < numArgs; ++i)
@ -94,7 +59,7 @@ void InitializeNoOptions()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv); vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc, argv);
vtkm::cont::InitializeResult result = vtkm::cont::Initialize(argc, argv); vtkm::cont::InitializeResult result = vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv); CheckArgs(argc, argv);
@ -108,7 +73,7 @@ void InitializeStandardOptions()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--vtkm-device", "Any"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc, argv, "--vtkm-device", "Any");
vtkm::cont::Initialize(argc, argv, vtkm::cont::InitializeOptions::Strict); vtkm::cont::Initialize(argc, argv, vtkm::cont::InitializeOptions::Strict);
CheckArgs(argc, argv); CheckArgs(argc, argv);
} }
@ -119,11 +84,12 @@ void InitializeCustomOptions()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--foo", "-bar", "baz", "buz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc, argv, "--foo", "-bar", "baz", "buz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "-bar", "baz", "buz"); CheckArgs(argc, argv, "--foo", "-bar", "baz", "buz");
MakeArgs(argc, argv, "--foo", "-bar", "--", "baz", "buz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "-bar", "--", "baz", "buz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "-bar", "--", "baz", "buz"); CheckArgs(argc, argv, "--foo", "-bar", "--", "baz", "buz");
} }
@ -134,16 +100,17 @@ void InitializeMixedOptions()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--foo", "--vtkm-device", "Any", "--bar", "baz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "--vtkm-device", "Any", "--bar", "baz");
vtkm::cont::Initialize(argc, argv, vtkm::cont::InitializeOptions::AddHelp); vtkm::cont::Initialize(argc, argv, vtkm::cont::InitializeOptions::AddHelp);
CheckArgs(argc, argv, "--foo", "--bar", "baz"); CheckArgs(argc, argv, "--foo", "--bar", "baz");
MakeArgs( vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "--vtkm-log-level", "OFF", "--", "--vtkm-device", "Any", "--bar", "baz"); argc, argv, "--foo", "--vtkm-log-level", "OFF", "--", "--vtkm-device", "Any", "--bar", "baz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "--", "--vtkm-device", "Any", "--bar", "baz"); CheckArgs(argc, argv, "--foo", "--", "--vtkm-device", "Any", "--bar", "baz");
MakeArgs(argc, argv, "--vtkm-device", "Any", "foo"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc, argv, "--vtkm-device", "Any", "foo");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "foo"); CheckArgs(argc, argv, "foo");
} }
@ -154,19 +121,23 @@ void InitializeCustomOptionsWithArgs()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--vtkm-device", "Any", "--foo=bar", "--baz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--vtkm-device", "Any", "--foo=bar", "--baz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo=bar", "--baz"); CheckArgs(argc, argv, "--foo=bar", "--baz");
MakeArgs(argc, argv, "--foo=bar", "--baz", "--vtkm-device", "Any"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo=bar", "--baz", "--vtkm-device", "Any");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo=bar", "--baz"); CheckArgs(argc, argv, "--foo=bar", "--baz");
MakeArgs(argc, argv, "--vtkm-device", "Any", "--foo", "bar", "--baz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--vtkm-device", "Any", "--foo", "bar", "--baz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "bar", "--baz"); CheckArgs(argc, argv, "--foo", "bar", "--baz");
MakeArgs(argc, argv, "--foo", "bar", "--baz", "--vtkm-device", "Any"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "bar", "--baz", "--vtkm-device", "Any");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "bar", "--baz"); CheckArgs(argc, argv, "--foo", "bar", "--baz");
} }
@ -177,23 +148,28 @@ void InitializeDeprecatedOptionsWithArgs()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--device", "Any", "--foo=bar", "--baz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--device", "Any", "--foo=bar", "--baz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo=bar", "--baz"); CheckArgs(argc, argv, "--foo=bar", "--baz");
MakeArgs(argc, argv, "--foo=bar", "--baz", "--device", "Any"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo=bar", "--baz", "--device", "Any");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo=bar", "--baz"); CheckArgs(argc, argv, "--foo=bar", "--baz");
MakeArgs(argc, argv, "-d", "Any", "--foo", "bar", "--baz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "-d", "Any", "--foo", "bar", "--baz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "bar", "--baz"); CheckArgs(argc, argv, "--foo", "bar", "--baz");
MakeArgs(argc, argv, "--foo", "bar", "--baz", "-d", "Any"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "bar", "--baz", "-d", "Any");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "bar", "--baz"); CheckArgs(argc, argv, "--foo", "bar", "--baz");
MakeArgs(argc, argv, "--foo", "-v", "OFF", "--", "--device", "Any", "--bar", "baz"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "-v", "OFF", "--", "--device", "Any", "--bar", "baz");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv, "--foo", "--", "--device", "Any", "--bar", "baz"); CheckArgs(argc, argv, "--foo", "--", "--device", "Any", "--bar", "baz");
} }
@ -202,16 +178,16 @@ void InitializeRuntimeDeviceConfigurationWithArgs()
{ {
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc,
argv, argv,
"--device", "--device",
"Any", "Any",
"--vtkm-num-threads", "--vtkm-num-threads",
"100", "100",
"--vtkm-numa-regions", "--vtkm-numa-regions",
"4", "4",
"--vtkm-device-instance", "--vtkm-device-instance",
"2"); "2");
vtkm::cont::Initialize(argc, argv); vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv); CheckArgs(argc, argv);
} }
@ -222,7 +198,7 @@ void InitializeWithHelp()
int argc; int argc;
char** argv; char** argv;
MakeArgs(argc, argv, "--vtkm-help"); vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc, argv, "--vtkm-help");
vtkm::cont::Initialize(argc, argv, vtkm::cont::InitializeOptions::AddHelp); vtkm::cont::Initialize(argc, argv, vtkm::cont::InitializeOptions::AddHelp);
VTKM_TEST_FAIL("Help argument did not exit as expected."); VTKM_TEST_FAIL("Help argument did not exit as expected.");