From 03e31c59a9e5abb72b4ebcafa1ee2fad628e59b0 Mon Sep 17 00:00:00 2001 From: Petr Slonek <48959829+Petronous@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:28:16 +0200 Subject: [PATCH 01/19] Add ability to choose compute API for and Example --- Examples/LegacyExamples/verify_refactor.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Examples/LegacyExamples/verify_refactor.sh b/Examples/LegacyExamples/verify_refactor.sh index d92b0294..9cbd79f6 100755 --- a/Examples/LegacyExamples/verify_refactor.sh +++ b/Examples/LegacyExamples/verify_refactor.sh @@ -9,12 +9,21 @@ set -e +USAGE="Usage: $0 +Example: $0 Sort OpenCl" # Get example name from argument EXAMPLE_NAME="${1}" if [ -z "$EXAMPLE_NAME" ]; then echo "Error: Example name required" - echo "Usage: $0 " - echo "Example: $0 Sort" + echo $USAGE + exit 1 +fi + +API_TYPE="${2}" +if [ -z "$API_TYPE" ]; then + echo "Error: Compute API required" + echo "Usage: $0 " + echo "Example: $0 Sort OpenCl" exit 1 fi @@ -46,7 +55,7 @@ cd "$PROJECT_DIR" premake5 gmake --reference-versions --no-cuda --platform=amd --cpp -echo "Step 2: Building ${EXAMPLE_NAME}OpenCl and ${EXAMPLE_NAME}ReferenceOpenCl..." +echo "Step 2: Building ${EXAMPLE_NAME}${API_TYPE} and ${EXAMPLE_NAME}Reference${API_TYPE}..." cd "$BUILD_DIR" make config=release_x86_64 @@ -63,7 +72,7 @@ if [ -f "$REF_OUTPUT_JSON" ]; then else cd "$BIN_DIR" rm $BIN_DIR/*.json || true - ./${EXAMPLE_NAME}ReferenceOpenCl || true + ./${EXAMPLE_NAME}Reference${API_TYPE} || true # Save reference version Output.json if [ -f "$OUTPUT_JSON" ]; then @@ -85,7 +94,7 @@ echo "" echo "Step 4: Running refactored $EXAMPLE_NAME example..." cd "$BIN_DIR" rm $BIN_DIR/*.json || true -./${EXAMPLE_NAME}OpenCl || true +./${EXAMPLE_NAME}${API_TYPE} || true # Save refactored version Output.json if [ -f "$OUTPUT_JSON" ]; then From d867ec5278006aa8b0005019c874e9d916734eb0 Mon Sep 17 00:00:00 2001 From: Petr Slonek <48959829+Petronous@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:34:35 +0200 Subject: [PATCH 02/19] Refactor Example includes to be compiled separately and linked Formerly each Example had to compile the same Examples/*.cpp files, this should speed up compilation. Further refactoring possible: Creating three different versions based on cuda/opencl/cpp usage is somewhat ugly. Could try to find a way around this, or at least parametrize the project declaration so there's no copied code. --- premake5.lua | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/premake5.lua b/premake5.lua index 8e64db0e..36e261c2 100644 --- a/premake5.lua +++ b/premake5.lua @@ -389,15 +389,24 @@ function addExampleProject(name, kernelExt, apiDefine, useRefVersions, shouldEna if useRefVersions then cppFiles = {"Examples/LegacyExamples/" .. name .. "/*.cpp"} else - cppFiles = {"Examples/*.cpp", "Examples/" .. name .. "/*.cpp"} + cppFiles = {"Examples/" .. name .. "/*.cpp"} + end + + local exLib + if apiDefine == "KTT_CUDA_EXAMPLE" then + exLib = "ExamplesLibCuda" + elseif apiDefine == "KTT_OPENCL_EXAMPLE" then + exLib = "ExamplesLibOpenCl" + else + exLib = "ExamplesLibCpp" end project(projectName) kind "ConsoleApp" files {table.unpack(cppFiles)} - includedirs {"Source"} + includedirs {"Source", "Examples"} defines {apiDefine} - links {"ktt"} + links {"ktt", exLib} if shouldEnableOpenMP then enableOpenMP() end @@ -682,7 +691,39 @@ end -- vulkanProjects end -- _OPTIONS["no-tutorials"] --- Examples configuration +-- Examples shared library (compiled once, linked by all examples) +if not _OPTIONS["no-examples"] then + +project "ExamplesLibCuda" + kind "StaticLib" + files + { + "Examples/*.cpp" + } + includedirs {"Source"} + defines {"KTT_CUDA_EXAMPLE"} + +project "ExamplesLibOpenCl" + kind "StaticLib" + files + { + "Examples/*.cpp" + } + includedirs {"Source"} + defines {"KTT_OPENCL_EXAMPLE"} + +project "ExamplesLibCpp" + kind "StaticLib" + files + { + "Examples/*.cpp" + } + includedirs {"Source"} + defines {"KTT_CPP_EXAMPLE"} + +end -- _OPTIONS["no-examples"] + +-- Examples configuration if not _OPTIONS["no-examples"] then if openClProjects then From 266f4cb7b5bd3576cb779f300f091baf51c9e198 Mon Sep 17 00:00:00 2001 From: Petr Slonek <48959829+Petronous@users.noreply.github.com> Date: Tue, 26 May 2026 11:59:16 +0200 Subject: [PATCH 03/19] WIP: Start CLI rewrite --- Examples/CliComponent.cpp | 78 +++++++++++ Examples/CliComponent.h | 56 ++++++++ Examples/CompilerTuningComponent.cpp | 30 +++++ Examples/CompilerTuningComponent.h | 27 ++++ Examples/ExampleBase.cpp | 177 +++++++++++++++++++------ Examples/ExampleBase.h | 33 ++++- Examples/ExampleConfigurator.cpp | 185 --------------------------- Examples/ExampleConfigurator.h | 54 -------- 8 files changed, 359 insertions(+), 281 deletions(-) create mode 100644 Examples/CliComponent.cpp create mode 100644 Examples/CliComponent.h create mode 100644 Examples/CompilerTuningComponent.cpp create mode 100644 Examples/CompilerTuningComponent.h delete mode 100644 Examples/ExampleConfigurator.cpp delete mode 100644 Examples/ExampleConfigurator.h diff --git a/Examples/CliComponent.cpp b/Examples/CliComponent.cpp new file mode 100644 index 00000000..14e909e3 --- /dev/null +++ b/Examples/CliComponent.cpp @@ -0,0 +1,78 @@ +#include "CliComponent.h" +#include "Api/Configuration/PreciseMeasurementParameters.h" +#include +#include +#include +#include + +using namespace std; + +CliOption::CliOption(function &)> callback, const string &trigger, const string &description, + const string &argumentDescriptions, const int argumentCount) + : m_callback(callback), m_trigger(trigger), m_description(description), + m_argumentDescriptions(argumentDescriptions), m_argumentCount(argumentCount) +{ +} + +string CliOption::get_string() const +{ + return m_trigger + " " + m_argumentDescriptions + "\n\t" + m_description; +} + +bool CliOption::TryTrigger(int argc, char **argv, int &i) const { + assert(i < argc); + if (argv[i] != m_trigger) return false; + if (i + m_argumentCount >= argc) + { + cerr << m_trigger << " expects a value to be passed!" << endl; + exit(1); + } + vector arguments; + for (int j = 0; j < m_argumentCount; ++j) { + arguments.push_back(argv[++i]); + } + m_callback(arguments); + return true; +} + +CliComponent::CliComponent() +{ + AddOption({[this](const vector &) { + cout << "Usage: program [options]" << endl << endl; + cout << "Options:" << endl; + for (const auto& option : m_options) { + cout << option.get_string() << endl; + } + exit(0); + }, "--help", "Show this help message and exit."}); +} + +void CliComponent::AddOption(const CliOption &cliOption) { + m_options.push_back(cliOption); +} + +void CliComponent::ProcessInput(int argc, char **argv) { + for (int i = 1; i < argc; ++i) { + bool triggered = false; + for (const auto& option : m_options) { + if (option.TryTrigger(argc, argv, i)) { + triggered = true; + break; + } + } + if (!triggered) { + cerr << argv[i] << " is not a valid option.\n"; + exit(1); + } + } +} + + +void SetUpCommonOptions(vector &options, ExampleConfiguration *config) { +} + +void SetUpRefKernelOption(vector &options, ExampleRefKernelConfiguration &config) { + options.emplace_back([&config](const vector &args) { + config.refKernelFile = args[0]; + }, "--refKernelPath", "Reference kernel file path (expects string)", "", 1); +} \ No newline at end of file diff --git a/Examples/CliComponent.h b/Examples/CliComponent.h new file mode 100644 index 00000000..586d95b5 --- /dev/null +++ b/Examples/CliComponent.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Ktt.h" +#include +#include +#include +#include +#include +#include + +class CliOption +{ + std::function &)> m_callback; + const std::string m_trigger; + const std::string m_description; + const std::string m_argumentDescriptions; + const int m_argumentCount; + +public: + CliOption(std::function &)> callback, const std::string &trigger, const std::string &description, + const std::string &argumentDescriptions = "", const int argumentCount = 0); + + std::string get_string() const; + + bool TryTrigger(int argc, char **argv, int &i) const; +}; + +// struct ExampleConfiguration +// { +// bool rapidTest = false; +// bool useProfiling = false; +// unsigned platform = 0; +// unsigned device = 0; +// int problemSize = -1; +// std::string kernelFile = ""; +// std::unique_ptr stopCondition = nullptr; +// std::unique_ptr searcher = nullptr; +// std::string profileSearchModelPath = ""; +// std::optional preciseParams = std::nullopt; +// bool useDynamicTuning = false; +// double dynamicTuningTime = 0; +// }; + +// struct ExampleRefKernelConfiguration : public ExampleConfiguration { +// std::string refKernelFile = ""; +// }; + +class CliComponent { +public: + CliComponent(); + void AddOption(const CliOption &option); + void ProcessInput(int argc, char **argv); + +protected: + std::vector m_options; +}; \ No newline at end of file diff --git a/Examples/CompilerTuningComponent.cpp b/Examples/CompilerTuningComponent.cpp new file mode 100644 index 00000000..7a0818e7 --- /dev/null +++ b/Examples/CompilerTuningComponent.cpp @@ -0,0 +1,30 @@ +#include "CompilerTuningComponent.h" + +CompilerTuningComponent::CompilerTuningComponent(ktt::Tuner &tuner, ktt::KernelId kernel) + : m_tuner(tuner), m_kernel(kernel) { + useSeparateTuning = false; +} + +void CompilerTuningComponent::AddCompilerParameter(const std::string &name, const std::vector &values) { + if (useSeparateTuning) { + + } +} + +void CompilerTuningComponent::InitCLIOptions(std::vector &options) { + // TODO: Implement CLI options initialization +} + +void CompilerTuningComponent::Run() { + // TODO: Implement main execution logic +} + + +void NoCompilerTuning::AddCompilerParameter(const std::string &name, const std::vector &values) { +} + +void NoCompilerTuning::InitCLIOptions(std::vector &options) { +} + +void NoCompilerTuning::Run() { +} diff --git a/Examples/CompilerTuningComponent.h b/Examples/CompilerTuningComponent.h new file mode 100644 index 00000000..41a6dfe1 --- /dev/null +++ b/Examples/CompilerTuningComponent.h @@ -0,0 +1,27 @@ +#include "CliComponent.h" +#include + +class CompilerTuningComponent { +public: + CompilerTuningComponent(ktt::Tuner &tuner, ktt::KernelId kernel); + + // Public virtual despite NVI guidelines because they would just be thin wrappers otherwise. + // Follows "Do not generalize prematurely" and "you aren't gonna need it" + virtual void InitCLIOptions(std::vector &options); + virtual void AddCompilerParameter(const std::string &name, const std::vector &values); + virtual void Run(); + +protected: + ktt::Tuner &m_tuner; + ktt::KernelId m_kernel; + bool useSeparateTuning; +}; + +class NoCompilerTuning : public CompilerTuningComponent { +public: + using CompilerTuningComponent::CompilerTuningComponent; + + void AddCompilerParameter(const std::string &name, const std::vector &values) override; + void InitCLIOptions(std::vector &options) override; + void Run() override; +}; \ No newline at end of file diff --git a/Examples/ExampleBase.cpp b/Examples/ExampleBase.cpp index 0dd5308c..06e0a6b2 100644 --- a/Examples/ExampleBase.cpp +++ b/Examples/ExampleBase.cpp @@ -1,10 +1,11 @@ #include "ExampleBase.h" #include "Api/Output/KernelResult.h" #include "ComputeEngine/ComputeApi.h" -#include "ExampleConfigurator.h" +#include "CliComponent.h" #include "Utility/Logger/Logger.h" #include "Utility/Logger/LoggingLevel.h" #include +#include #include #include #include @@ -85,7 +86,7 @@ ExampleBase::RunStats ExampleBase::RunTuningPhase( break; } - const auto result = m_tuner.TuneIteration(m_kernel, {}, false, m_config->preciseParams); + const auto result = m_tuner->TuneIteration(m_kernel, {}, false, m_preciseParams); stats.Update(result); if (stats.totalRuns % printInterval == 0 || stats.totalRuns == 1) { @@ -112,7 +113,7 @@ ExampleBase::RunStats ExampleBase::RunExecutionPhase( break; } - const auto result = m_tuner.Run(m_kernel, bestConfig, {}); + const auto result = m_tuner->Run(m_kernel, bestConfig, {}); stats.Update(result); if (stats.totalRuns % printInterval == 0) { @@ -129,7 +130,7 @@ void ExampleBase::RunDynamic() { ktt::Logger::GetLogger().SetLoggingLevel(ktt::LoggingLevel::Warning); const auto startTime = std::chrono::steady_clock::now(); - const double timeBudgetSeconds = m_config->dynamicTuningTime > 0 ? m_config->dynamicTuningTime : 60.0; + const double timeBudgetSeconds = m_dynamicTuningTime > 0 ? m_dynamicTuningTime : 60.0; constexpr int printInterval = 50; RunStats tuningStats = RunTuningPhase(startTime, timeBudgetSeconds, printInterval); @@ -140,7 +141,7 @@ void ExampleBase::RunDynamic() PrintRunStats("Tuning phase", tuningStats, tuningThroughput); - const auto bestConfigData = m_tuner.GetBestConfiguration(m_kernel); + const auto bestConfigData = m_tuner->GetBestConfiguration(m_kernel); cout << "\n--- Running with best configuration ---" << endl; const auto runStartTime = std::chrono::steady_clock::now(); @@ -157,9 +158,9 @@ void ExampleBase::RunOffline() { const auto startTime = std::chrono::steady_clock::now(); - const auto results = m_tuner.Tune(m_kernel, std::move(m_config->stopCondition), m_config->preciseParams); - m_tuner.SaveResults(results, "Output", ktt::OutputFormat::XML); - m_tuner.SaveResults(results, "Output", ktt::OutputFormat::JSON); + const auto results = m_tuner->Tune(m_kernel, std::move(m_stopCondition), m_preciseParams); + m_tuner->SaveResults(results, "Output", ktt::OutputFormat::XML); + m_tuner->SaveResults(results, "Output", ktt::OutputFormat::JSON); const auto endTime = std::chrono::steady_clock::now(); double elapsed = std::chrono::duration(endTime - startTime).count(); @@ -184,12 +185,13 @@ void ExampleBase::RunOffline() void ExampleBase::Run() { - if (m_config->useDynamicTuning) RunDynamic(); + if (m_useDynamicTuning) RunDynamic(); else RunOffline(); } ExampleBase::ExampleBase( - shared_ptr config, + int argc, + char **argv, int defaultProblemSize, string exampleFolderPath, string defaultKernelFileBaseName @@ -201,42 +203,145 @@ ExampleBase::ExampleBase( #elif KTT_CPP_EXAMPLE m_computeApi(ktt::ComputeApi::Cpp), #endif - m_config(config), - m_tuner(config->platform, config->device, m_computeApi) + m_argc(argc), + m_argv(argv) { - m_problemSize = config->problemSize >= 0 ? config->problemSize : defaultProblemSize; - m_kernelFile = config->kernelFile.empty() - ? GetKernelFilePath(exampleFolderPath, defaultKernelFileBaseName) - : config->kernelFile; - - - if (config->useProfiling) - { - printf("Executing with profiling switched ON.\n"); - m_tuner.SetProfiling(true); - } - - m_tuner.SetGlobalSizeType(ktt::GlobalSizeType::CUDA); - m_tuner.SetTimeUnit(ktt::TimeUnit::Microseconds); + m_problemSize = defaultProblemSize; + m_kernelFile = GetKernelFilePath(exampleFolderPath, defaultKernelFileBaseName); } void ExampleBase::PostInitialize() { + InitCLI(); + ProcessCLI(); + InitTuner(); InitData(); InitKernel(); InitTuningSpace(); InitSearcher(); } +void ExampleBase::InitCLI() { + m_cli.AddOption({[this](const vector &) { + m_rapidTest = true; + }, "--rapidTest", "Run in rapid test mode"}); + + m_cli.AddOption({[this](const vector &) { + m_useProfiling = true; + }, "--profile", "Enable profiling"}); + + m_cli.AddOption({[this](const vector &args) { + m_platform = stoul(args[0]); + }, "--platform", "Platform index (expects int)", "", 1}); + + m_cli.AddOption({[this](const vector &args) { + m_device = stoul(args[0]); + }, "--device", "Device index (expects int)", "", 1}); + + m_cli.AddOption({[this](const vector &args) { + m_problemSize = stoi(args[0]); + }, "--problemSize", "Problem size in MiB (expects int)", "", 1}); + + m_cli.AddOption({[this](const vector &args) { + m_kernelFile = args[0]; + }, "--kernelPath", "Kernel file path (expects string)", "", 1}); + + m_cli.AddOption({[this](const vector &args) { + if (args[0] == "ds") { + m_searcher = make_unique(); + } else if (args[0] == "random") { + m_searcher = make_unique(); + } else if (args[0] == "mcmc") { + m_searcher = make_unique(); + } else { + cerr << "--searcher expects one of (ds, random, mcmc)\n"; + exit(1); + } + }, "--searcher", "Searcher type (ds, random, mcmc)", "", 1}); + + m_cli.AddOption({[this](const vector &args) { + m_profileSearchModelPath = args[0]; + m_useProfiling = true; + }, "--profileSearcher", + "Enable profile searcher and set path to model (expects string) (functions only on CUDA devices)", + "", 1}); + + m_cli.AddOption({[this](const vector &args) { + if (args[0] == "confs") { + m_stopCondition = make_unique(stoul(args[1])); + } else if (args[0] == "fails") { + m_stopCondition = make_unique(stoul(args[1])); + } else if (args[0] == "time") { + m_stopCondition = make_unique(stod(args[1])); + } else if (args[0] == "best") { + m_stopCondition = make_unique(stod(args[1])); + } else { + cerr << "--stopCondition expects one of (confs, fails, time, best)\n"; + exit(1); + } + }, "--stopCondition", + "Set a stop condition. can be confs, fails, time, best. " + " is respectively configuration count (ulong), failed kernel run count (ulong), " + "total tuning duration in seconds (double), best configuration duration in milliseconds (double).", + " ", 2}); + + m_cli.AddOption({[this](const vector &args) { + m_preciseParams = ktt::PreciseMeasurementParameters(stoul(args[0]), + stoul(args[1]), stod(args[2])); + }, "--preciseParams", "Set PreciseMeasurementParameters, calculationDurationMethod is the default Minimum, refer to KTT documentation for details.", + " ", 3}); + m_cli.AddOption({[this](const vector &args) { + if (m_preciseParams == std::nullopt) { + cerr << "--preciseParams must be used before this option.\n"; + exit(1); + } + ktt::DurationCalculationMethod calcMethod = ktt::DurationCalculationMethod::Minimum; + if (args[0] == "min") {} + else if (args[0] == "median") { + calcMethod = ktt::DurationCalculationMethod::Median; + } else if (args[0] == "avg") { + calcMethod = ktt::DurationCalculationMethod::Average; + } else { + cerr << "--preciseParamsCalcMethod expects one of (min, median, avg)\n"; + exit(1); + } + m_preciseParams->durationCalculationMethod = calcMethod; + }, "--preciseParamsCalcMethod", "Optionally set PreciseMeasurementParameters::durationCalculationMethod AFTER USING --preciseParams, expects one of " + "(min, median, avg), refer to KTT documentation for details.", + "", 1}); + + m_cli.AddOption({[this](const vector &args) { + m_useDynamicTuning = true; + m_dynamicTuningTime = stod(args[0]); + }, "--useDynamicTuning", "Enables a basic implementation of dynamic tuning." + "The tuning will last