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/OptionParserArguments.h>
#if defined(VTKM_ENABLE_KOKKOS)
#include <vtkm/cont/kokkos/internal/Initialize.h>
#endif
#include <memory>
#include <sstream>
@ -128,12 +124,6 @@ InitializeResult Initialize(int& argc, char* argv[], InitializeOptions opts)
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
std::vector<opt::Descriptor> usage;
if ((opts & InitializeOptions::AddHelp) != InitializeOptions::None)

@ -92,20 +92,20 @@ public:
}
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");
}
VTKM_CONT virtual vtkm::cont::internal::RuntimeDeviceConfigReturnCode SetNumaRegions(
const vtkm::Id&) const override final
const vtkm::Id&) override final
{
throw vtkm::cont::ErrorBadDevice(
"Tried to set the number of numa regions on an invalid device");
}
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");
}
@ -128,6 +128,18 @@ public:
{
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{};
}
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(
const vtkm::Id&) const override final
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(const vtkm::Id&) override final
{
// TODO: set the cuda device instance
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?)
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxDevices(vtkm::Id&) const override final
{
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
};
} // namespace vtkm::cont::internal
} // namespace vtkm::cont

@ -7,6 +7,7 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/internal/RuntimeDeviceConfiguration.h>
namespace vtkm
@ -16,52 +17,112 @@ namespace cont
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;
void RuntimeDeviceConfigurationBase::Initialize(
const RuntimeDeviceConfigurationOptions& configOptions) const
const RuntimeDeviceConfigurationOptions& configOptions)
{
if (configOptions.VTKmNumThreads.IsSet())
{
auto value = configOptions.VTKmNumThreads.GetValue();
auto code = this->SetThreads(value);
this->LogReturnCode(code, "SetThreads", value);
}
if (configOptions.VTKmNumaRegions.IsSet())
{
auto value = configOptions.VTKmNumaRegions.GetValue();
auto code = this->SetNumaRegions(value);
this->LogReturnCode(code, "SetNumaRegions", value);
}
if (configOptions.VTKmDeviceInstance.IsSet())
{
auto value = configOptions.VTKmDeviceInstance.GetValue();
auto code = this->SetDeviceInstance(value);
this->LogReturnCode(code, "SetDeviceInstance", value);
}
InitializeOption(
configOptions.VTKmNumThreads,
[&](const vtkm::Id& value) { return this->SetThreads(value); },
"SetThreads",
this->GetDevice().GetName());
InitializeOption(
configOptions.VTKmNumaRegions,
[&](const vtkm::Id& value) { return this->SetNumaRegions(value); },
"SetNumaRegions",
this->GetDevice().GetName());
InitializeOption(
configOptions.VTKmDeviceInstance,
[&](const vtkm::Id& value) { return this->SetDeviceInstance(value); },
"SetDeviceInstance",
this->GetDevice().GetName());
this->InitializeSubsystem();
}
void RuntimeDeviceConfigurationBase::Initialize(
const RuntimeDeviceConfigurationOptions& configOptions,
int& argc,
char* argv[]) const
char* argv[])
{
this->ParseExtraArguments(argc, argv);
this->Initialize(configOptions);
}
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetThreads(const vtkm::Id&) const
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetThreads(const vtkm::Id&)
{
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
}
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetNumaRegions(const vtkm::Id&) const
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetNumaRegions(const vtkm::Id&)
{
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
}
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetDeviceInstance(
const vtkm::Id&) const
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::SetDeviceInstance(const vtkm::Id&)
{
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
}
@ -81,35 +142,20 @@ RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::GetDeviceInstance(
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
}
void RuntimeDeviceConfigurationBase::ParseExtraArguments(int&, char*[]) const {}
void RuntimeDeviceConfigurationBase::LogReturnCode(const RuntimeDeviceConfigReturnCode& code,
const std::string& function,
const vtkm::Id& value) const
RuntimeDeviceConfigReturnCode RuntimeDeviceConfigurationBase::GetMaxThreads(vtkm::Id&) const
{
// 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::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
return RuntimeDeviceConfigReturnCode::INVALID_FOR_DEVICE;
}
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
} // namespace vtkm

@ -29,7 +29,8 @@ enum class RuntimeDeviceConfigReturnCode
SUCCESS,
OUT_OF_BOUNDS,
INVALID_FOR_DEVICE,
INVALID_VALUE
INVALID_VALUE,
NOT_APPLIED
};
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
/// warning is logged based on the value of the `RuntimeDeviceConfigReturnCode` returned
/// 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,
int& argc,
char* argv[]) const;
char* argv[]);
/// The following public methods should be overriden in each individual device.
/// A method should return INVALID_FOR_DEVICE if the overriden device does not
/// support the particular set method.
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id&) const;
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(const vtkm::Id&) const;
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetDeviceInstance(const vtkm::Id&) const;
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetThreads(const vtkm::Id& value);
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(const vtkm::Id& value);
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 GetNumaRegions(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:
/// 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
/// 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
/// calling a specific `Set*` method.
///
/// params:
/// code - The code to log a message for
/// 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;
/// An overriden method that can be used to perform extra initialization after Extra
/// Arguments are parsed and the Initialized ConfigOptions are used to call the various
/// Set* methods at the end of Initialize. Particuarly useful when initializing
/// additional subystems (like Kokkos).
VTKM_CONT virtual void InitializeSubsystem();
};
template <typename DeviceAdapterTag>

@ -9,6 +9,9 @@
//============================================================================
#include <vtkm/cont/internal/RuntimeDeviceConfigurationOptions.h>
#include <memory>
#include <sstream>
namespace vtkm
{
namespace cont
@ -16,42 +19,83 @@ namespace cont
namespace internal
{
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions()
: 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)
namespace
{
}
RuntimeDeviceConfigurationOptions::~RuntimeDeviceConfigurationOptions() noexcept = default;
RuntimeDeviceConfigurationOptions::RuntimeDeviceConfigurationOptions(
std::vector<option::Descriptor>& usage)
: RuntimeDeviceConfigurationOptions()
void AppendOptionDescriptors(std::vector<option::Descriptor>& usage,
const bool& useOptionIndex = true)
{
usage.push_back(
{ option::OptionIndex::NUM_THREADS,
{ useOptionIndex ? static_cast<uint32_t>(option::OptionIndex::NUM_THREADS) : 0,
0,
"",
"vtkm-num-threads",
option::VtkmArg::Required,
" --vtkm-num-threads <dev> \tSets the number of threads to use for the selected device" });
usage.push_back(
{ option::OptionIndex::NUMA_REGIONS,
{ useOptionIndex ? static_cast<uint32_t>(option::OptionIndex::NUMA_REGIONS) : 1,
0,
"",
"vtkm-numa-regions",
option::VtkmArg::Required,
" --vtkm-numa-regions <dev> \tSets the number of numa regions when using kokkos/OpenMP" });
usage.push_back({ option::OptionIndex::DEVICE_INSTANCE,
0,
"",
"vtkm-device-instance",
option::VtkmArg::Required,
" --vtkm-device-instance <dev> \tSets the device instance to use when using "
"kokkos/cuda" });
usage.push_back(
{ useOptionIndex ? static_cast<uint32_t>(option::OptionIndex::DEVICE_INSTANCE) : 2,
0,
"",
"vtkm-device-instance",
option::VtkmArg::Required,
" --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)
{

@ -30,13 +30,16 @@ namespace internal
class VTKM_CONT_EXPORT RuntimeDeviceConfigurationOptions
{
public:
/// Sets the option indices and environment varaible names for the vtkm supported options.
VTKM_CONT RuntimeDeviceConfigurationOptions();
/// Calls the default constructor and additionally pushes back additional command line
/// options to the provided usage vector for integration with the vtkm option parser.
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;
/// Calls Initialize for each of this class's current configuration options and marks
@ -48,6 +51,12 @@ public:
RuntimeDeviceOption VTKmNumaRegions;
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:
bool Initialized;
};

@ -10,6 +10,7 @@
#include <vtkm/cont/internal/RuntimeDeviceOption.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/Logging.h>
#include <string>
@ -92,6 +93,11 @@ void RuntimeDeviceOption::SetOption(const vtkm::Id& value)
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;
}

@ -23,47 +23,12 @@ namespace opt = internal::option;
namespace
{
static std::vector<std::pair<std::string, std::string>> envVars{};
enum
{
UNKNOWN,
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,
char** argv,
std::vector<opt::Descriptor>& usage)
@ -81,25 +46,6 @@ std::unique_ptr<opt::Option[]> GetOptions(int& argc,
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()
{
std::vector<opt::Descriptor> usage;
@ -108,7 +54,7 @@ void TestRuntimeDeviceOptionHappy()
usage.push_back({ 0, 0, 0, 0, 0, 0 });
const std::string env{ "TEST_OPTION" };
my_unsetenv(env);
vtkm::cont::testing::Testing::UnsetEnv(env);
// Basic no value initialize
{
@ -117,7 +63,7 @@ void TestRuntimeDeviceOptionHappy()
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
{
@ -131,7 +77,7 @@ void TestRuntimeDeviceOptionHappy()
int argc;
char** argv;
MakeArgs(argc, argv, "--test-option", "2");
vtkm::cont::testing::Testing::MakeArgs(argc, argv, "--test-option", "2");
auto options = GetOptions(argc, argv, usage);
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");
}
my_unsetenv(env);
vtkm::cont::testing::Testing::UnsetEnv(env);
}
void TestRuntimeDeviceOptionError()
@ -169,14 +115,14 @@ void TestRuntimeDeviceOptionError()
usage.push_back({ 0, 0, 0, 0, 0, 0 });
const std::string env{ "TEST_OPTION" };
my_unsetenv(env);
vtkm::cont::testing::Testing::UnsetEnv(env);
bool threw = true;
// Parse a non integer
{
internal::RuntimeDeviceOption testOption(TEST, env);
my_setenv(env, "bad");
vtkm::cont::testing::Testing::SetEnv(env, "bad");
try
{
testOption.Initialize(nullptr);
@ -196,7 +142,7 @@ void TestRuntimeDeviceOptionError()
// Parse an integer that's too large
{
internal::RuntimeDeviceOption testOption(TEST, env);
my_setenv(env, "9938489298493882949384989");
vtkm::cont::testing::Testing::SetEnv(env, "9938489298493882949384989");
try
{
testOption.Initialize(nullptr);
@ -216,7 +162,7 @@ void TestRuntimeDeviceOptionError()
// Parse an integer with some stuff on the end
{
internal::RuntimeDeviceOption testOption(TEST, env);
my_setenv(env, "100bad");
vtkm::cont::testing::Testing::SetEnv(env, "100bad");
try
{
testOption.Initialize(nullptr);
@ -233,40 +179,11 @@ void TestRuntimeDeviceOptionError()
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.VTKmNumThreads.IsSet(), "num threads should be set");
@ -278,6 +195,54 @@ void TestRuntimeDeviceConfigurationOptions()
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()
{
TestRuntimeDeviceOptionHappy();

@ -13,7 +13,6 @@ set(headers
DeviceAdapterMemoryManagerKokkos.h
DeviceAdapterRuntimeDetectorKokkos.h
DeviceAdapterTagKokkos.h
Initialize.h
KokkosAlloc.h
KokkosTypes.h
RuntimeDeviceConfigurationKokkos.h
@ -32,7 +31,6 @@ if (TARGET vtkm::kokkos)
${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterAlgorithmKokkos.cxx
${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterMemoryManagerKokkos.cxx
${CMAKE_CURRENT_SOURCE_DIR}/DeviceAdapterRuntimeDetectorKokkos.cxx
${CMAKE_CURRENT_SOURCE_DIR}/Initialize.cxx
${CMAKE_CURRENT_SOURCE_DIR}/KokkosAlloc.cxx
${CMAKE_CURRENT_SOURCE_DIR}/KokkosTypes.cxx)
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
#define vtk_m_cont_kokkos_internal_RuntimeDeviceConfigurationKokkos_h
#include <vtkm/cont/ErrorInternal.h>
#include <vtkm/cont/internal/RuntimeDeviceConfiguration.h>
#include <vtkm/cont/kokkos/internal/DeviceAdapterTagKokkos.h>
VTKM_THIRDPARTY_PRE_INCLUDE
#include <Kokkos_Core.hpp>
VTKM_THIRDPARTY_POST_INCLUDE
#include <cstring>
#include <vector>
namespace vtkm
{
@ -22,6 +28,52 @@ namespace cont
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 <>
class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagKokkos>
: public vtkm::cont::internal::RuntimeDeviceConfigurationBase
@ -32,54 +84,114 @@ public:
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;
}
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;
}
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;
}
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 RuntimeDeviceConfigReturnCode::SUCCESS;
return GetArgFromList(this->KokkosArguments, "--kokkos-threads", value);
}
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 RuntimeDeviceConfigReturnCode::SUCCESS;
return GetArgFromList(this->KokkosArguments, "--kokkos-numa", value);
}
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 RuntimeDeviceConfigReturnCode::SUCCESS;
return GetArgFromList(this->KokkosArguments, "--kokkos-device-id", value);
}
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:
Kokkos::InitArguments ParsedCommandLineArgs;
Kokkos::InitArguments VTKmInitializedArgs;
std::vector<std::string> KokkosArguments;
};
} // namespace vtkm::cont::internal
} // namespace vtkm::cont
} // namespace vtkm

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

@ -7,22 +7,13 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_cont_kokkos_internal_Initialize_h
#define vtk_m_cont_kokkos_internal_Initialize_h
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/kokkos/DeviceAdapterKokkos.h>
#include <vtkm/cont/testing/TestingRuntimeDeviceConfiguration.h>
namespace vtkm
int UnitTestKokkosRuntimeDeviceConfiguration(int argc, char* argv[])
{
namespace cont
{
namespace kokkos
{
namespace internal
{
void Initialize(int& argc, char* argv[]);
vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagKokkos{});
return vtkm::cont::testing::TestingRuntimeDeviceConfiguration<
vtkm::cont::DeviceAdapterTagKokkos>::Run(argc, 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{};
}
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
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(
const vtkm::Id&) const override final
VTKM_CONT virtual RuntimeDeviceConfigReturnCode SetNumaRegions(const vtkm::Id&) override final
{
// TODO: Set the numa regions in OpenMP
return RuntimeDeviceConfigReturnCode::SUCCESS;
@ -53,6 +52,11 @@ class RuntimeDeviceConfiguration<vtkm::cont::DeviceAdapterTagOpenMP>
// TODO: Get the number of OpenMP NumaRegions
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxThreads(vtkm::Id&) const override final
{
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
};
} // namespace vtkm::cont::internal
} // namespace vtkm::cont

@ -29,7 +29,7 @@ class RuntimeDeviceConfiguration<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
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)
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
VTKM_CONT virtual RuntimeDeviceConfigReturnCode GetMaxThreads(vtkm::Id&) const override final
{
return RuntimeDeviceConfigReturnCode::SUCCESS;
}
};
} // namespace vktm::cont::internal
} // namespace vtkm::cont

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

@ -101,20 +101,8 @@ public:
}
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
{
function();
@ -144,6 +132,23 @@ public:
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>
static VTKM_CONT int RunOnDevice(Func function, int argc, char* argv[])
{
@ -151,33 +156,66 @@ public:
auto config = vtkm::cont::Initialize(argc, argv, opts);
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)
{
std::cerr << "Error at " << error.GetFile() << ":" << error.GetLine() << ":"
<< error.GetFunction() << "\n\t" << error.GetMessage() << "\n";
return 1;
}
catch (vtkm::cont::Error& error)
{
std::cerr << "Uncaught VTKm exception thrown.\n" << error.GetMessage() << "\n";
std::cerr << "Stacktrace:\n" << error.GetStackTrace() << "\n";
return 1;
}
catch (std::exception& error)
{
std::cerr << "STL exception throw.\n\t" << error.what() << "\n";
return 1;
}
catch (...)
{
std::cerr << "Unidentified exception thrown.\n";
return 1;
}
return 0;
finalArgs[numArgs] = nullptr;
std::cout << std::endl;
argc = static_cast<int>(numArgs);
argv = finalArgs.data();
}
template <typename... T>
static VTKM_CONT void MakeArgsAddProgramName(int& argc, char**& argv, T&&... args)
{
MakeArgs(argc, argv, "program-name", args...);
}
static void SetEnv(const std::string& var, const std::string& value)
{
static std::vector<std::pair<std::string, std::string>> envVars{};
#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
}
static void UnsetEnv(const std::string& var)
{
#ifdef _MSC_VER
SetEnv(var, "");
#else
unsetenv(var.c_str());
#endif
}
private:
@ -248,143 +286,141 @@ private:
// Method to parse the extra arguments given to unit tests
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,
0,
"",
"vtkm-data-dir",
opt::VtkmArg::Required,
" --vtkm-data-dir "
"<data-dir-path> \tPath to the "
"base data directory in the VTK-m "
"src dir." });
usage.push_back({ BASELINEDIR,
0,
"",
"vtkm-baseline-dir",
opt::VtkmArg::Required,
" --vtkm-baseline-dir "
"<baseline-dir-path> "
"\tPath to the base dir "
"for regression test "
"images" });
usage.push_back({ WRITEDIR,
0,
"",
"vtkm-write-dir",
opt::VtkmArg::Required,
" --vtkm-write-dir "
"<write-dir-path> "
"\tPath to the write dir "
"to store generated "
"regression test images" });
usage.push_back({ DEPRECATED_DATADIR,
0,
"D",
"data-dir",
opt::VtkmArg::Required,
" --data-dir "
"<data-dir-path> "
"\tDEPRECATED: use --vtkm-data-dir instead" });
usage.push_back({ DEPRECATED_BASELINEDIR,
0,
"B",
"baseline-dir",
opt::VtkmArg::Required,
" --baseline-dir "
"<baseline-dir-path> "
"\tDEPRECATED: use --vtkm-baseline-dir instead" });
usage.push_back({ WRITEDIR,
0,
"",
"write-dir",
opt::VtkmArg::Required,
" --write-dir "
"<write-dir-path> "
"\tDEPRECATED: use --vtkm-write-dir instead" });
usage.push_back({ DATADIR,
0,
"",
"vtkm-data-dir",
opt::VtkmArg::Required,
" --vtkm-data-dir "
"<data-dir-path> \tPath to the "
"base data directory in the VTK-m "
"src dir." });
usage.push_back({ BASELINEDIR,
0,
"",
"vtkm-baseline-dir",
opt::VtkmArg::Required,
" --vtkm-baseline-dir "
"<baseline-dir-path> "
"\tPath to the base dir "
"for regression test "
"images" });
usage.push_back({ WRITEDIR,
0,
"",
"vtkm-write-dir",
opt::VtkmArg::Required,
" --vtkm-write-dir "
"<write-dir-path> "
"\tPath to the write dir "
"to store generated "
"regression test images" });
usage.push_back({ DEPRECATED_DATADIR,
0,
"D",
"data-dir",
opt::VtkmArg::Required,
" --data-dir "
"<data-dir-path> "
"\tDEPRECATED: use --vtkm-data-dir instead" });
usage.push_back({ DEPRECATED_BASELINEDIR,
0,
"B",
"baseline-dir",
opt::VtkmArg::Required,
" --baseline-dir "
"<baseline-dir-path> "
"\tDEPRECATED: use --vtkm-baseline-dir instead" });
usage.push_back({ WRITEDIR,
0,
"",
"write-dir",
opt::VtkmArg::Required,
" --write-dir "
"<write-dir-path> "
"\tDEPRECATED: use --vtkm-write-dir instead" });
// Required to collect unknown arguments when help is off.
usage.push_back({ TEST_UNKNOWN, 0, "", "", opt::VtkmArg::UnknownOption, "" });
usage.push_back({ 0, 0, 0, 0, 0, 0 });
// Required to collect unknown arguments when help is off.
usage.push_back({ TEST_UNKNOWN, 0, "", "", opt::VtkmArg::UnknownOption, "" });
usage.push_back({ 0, 0, 0, 0, 0, 0 });
// Remove argv[0] (executable name) if present:
int vtkmArgc = argc > 0 ? argc - 1 : 0;
char** vtkmArgv = argc > 0 ? argv + 1 : argv;
// Remove argv[0] (executable name) if present:
int vtkmArgc = argc > 0 ? argc - 1 : 0;
char** vtkmArgv = argc > 0 ? argv + 1 : argv;
opt::Stats stats(usage.data(), vtkmArgc, vtkmArgv);
std::unique_ptr<opt::Option[]> options{ new opt::Option[stats.options_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::Stats stats(usage.data(), vtkmArgc, vtkmArgv);
std::unique_ptr<opt::Option[]> options{ new opt::Option[stats.options_max] };
std::unique_ptr<opt::Option[]> buffer{ new opt::Option[stats.buffer_max] };
opt::Parser parse(usage.data(), vtkmArgc, vtkmArgv, options.get(), buffer.get());
if (parse.error())
{
std::cerr << "Internal Initialize parser error" << std::endl;
exit(1);
}
if (parse.error())
{
std::cerr << "Internal Initialize parser error" << std::endl;
exit(1);
}
if (options[DEPRECATED_DATADIR])
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated datadir flag: "
<< std::string{ options[DEPRECATED_DATADIR].name }
<< ", use --vtkm-data-dir instead");
SetAndGetTestDataBasePath(options[DEPRECATED_DATADIR].arg);
}
if (options[DEPRECATED_DATADIR])
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated datadir flag: "
<< std::string{ options[DEPRECATED_DATADIR].name }
<< ", use --vtkm-data-dir instead");
SetAndGetTestDataBasePath(options[DEPRECATED_DATADIR].arg);
}
if (options[DEPRECATED_BASELINEDIR])
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated baselinedir flag: "
<< std::string{ options[DEPRECATED_BASELINEDIR].name }
<< ", use --vtkm-baseline-dir instead");
SetAndGetRegressionImageBasePath(options[DEPRECATED_BASELINEDIR].arg);
}
if (options[DEPRECATED_BASELINEDIR])
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated baselinedir flag: "
<< std::string{ options[DEPRECATED_BASELINEDIR].name }
<< ", use --vtkm-baseline-dir instead");
SetAndGetRegressionImageBasePath(options[DEPRECATED_BASELINEDIR].arg);
}
if (options[DEPRECATED_WRITEDIR])
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated writedir flag: "
<< std::string{ options[DEPRECATED_WRITEDIR].name }
<< ", use --vtkm-write-dir instead");
SetAndGetWriteDirBasePath(options[DEPRECATED_WRITEDIR].arg);
}
if (options[DEPRECATED_WRITEDIR])
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Supplied deprecated writedir flag: "
<< std::string{ options[DEPRECATED_WRITEDIR].name }
<< ", use --vtkm-write-dir instead");
SetAndGetWriteDirBasePath(options[DEPRECATED_WRITEDIR].arg);
}
if (options[DATADIR])
{
SetAndGetTestDataBasePath(options[DATADIR].arg);
}
if (options[DATADIR])
{
SetAndGetTestDataBasePath(options[DATADIR].arg);
}
if (options[BASELINEDIR])
{
SetAndGetRegressionImageBasePath(options[BASELINEDIR].arg);
}
if (options[BASELINEDIR])
{
SetAndGetRegressionImageBasePath(options[BASELINEDIR].arg);
}
if (options[WRITEDIR])
{
SetAndGetWriteDirBasePath(options[WRITEDIR].arg);
}
if (options[WRITEDIR])
{
SetAndGetWriteDirBasePath(options[WRITEDIR].arg);
}
for (const opt::Option* opt = options[TEST_UNKNOWN]; opt != nullptr; opt = opt->next())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Unknown option to internal Initialize: " << opt->name << "\n");
}
for (const opt::Option* opt = options[TEST_UNKNOWN]; opt != nullptr; opt = opt->next())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Unknown option to internal Initialize: " << opt->name << "\n");
}
for (int nonOpt = 0; nonOpt < parse.nonOptionsCount(); ++nonOpt)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Unknown argument to internal Initialize: " << parse.nonOption(nonOpt) << "\n");
}
for (int nonOpt = 0; nonOpt < parse.nonOptionsCount(); ++nonOpt)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Unknown argument to internal Initialize: " << parse.nonOption(nonOpt) << "\n");
}
}
};
}
}
} // namespace vtkm::cont::testing
} // namespace vtkm::cont
} // namespace vtkm
//============================================================================
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
{
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>
void CheckArgs(int argc, char* argv[], T&&... args)
{
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:";
for (std::size_t i = 0; i < numArgs; ++i)
@ -94,7 +59,7 @@ void InitializeNoOptions()
int argc;
char** argv;
MakeArgs(argc, argv);
vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc, argv);
vtkm::cont::InitializeResult result = vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv);
@ -108,7 +73,7 @@ void InitializeStandardOptions()
int argc;
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);
CheckArgs(argc, argv);
}
@ -119,11 +84,12 @@ void InitializeCustomOptions()
int argc;
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);
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);
CheckArgs(argc, argv, "--foo", "-bar", "--", "baz", "buz");
}
@ -134,16 +100,17 @@ void InitializeMixedOptions()
int argc;
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);
CheckArgs(argc, argv, "--foo", "--bar", "baz");
MakeArgs(
vtkm::cont::testing::Testing::MakeArgsAddProgramName(
argc, argv, "--foo", "--vtkm-log-level", "OFF", "--", "--vtkm-device", "Any", "--bar", "baz");
vtkm::cont::Initialize(argc, argv);
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);
CheckArgs(argc, argv, "foo");
}
@ -154,19 +121,23 @@ void InitializeCustomOptionsWithArgs()
int argc;
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);
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);
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);
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);
CheckArgs(argc, argv, "--foo", "bar", "--baz");
}
@ -177,23 +148,28 @@ void InitializeDeprecatedOptionsWithArgs()
int argc;
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);
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);
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);
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);
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);
CheckArgs(argc, argv, "--foo", "--", "--device", "Any", "--bar", "baz");
}
@ -202,16 +178,16 @@ void InitializeRuntimeDeviceConfigurationWithArgs()
{
int argc;
char** argv;
MakeArgs(argc,
argv,
"--device",
"Any",
"--vtkm-num-threads",
"100",
"--vtkm-numa-regions",
"4",
"--vtkm-device-instance",
"2");
vtkm::cont::testing::Testing::MakeArgsAddProgramName(argc,
argv,
"--device",
"Any",
"--vtkm-num-threads",
"100",
"--vtkm-numa-regions",
"4",
"--vtkm-device-instance",
"2");
vtkm::cont::Initialize(argc, argv);
CheckArgs(argc, argv);
}
@ -222,7 +198,7 @@ void InitializeWithHelp()
int argc;
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_TEST_FAIL("Help argument did not exit as expected.");