diff --git a/src/opencl/Compare.md b/src/opencl/Compare.md new file mode 100644 index 000000000..6df361e57 --- /dev/null +++ b/src/opencl/Compare.md @@ -0,0 +1,372 @@ +# Сравнение библиотек для создания механизма конфигурирования +Вся информация взята из репозиториев соответствующих проектов. Дата обращения: 21.04.26. + +## Критерии сравнения + +| Критерий | Метрика | Единицы измерения | +|:---|:---|:---| +| **Зависимости** | наличие | +/- | +| **Источники** | CLI (argv) / env / файлы | +/-, форматы | +| **Стандарт C++** | минимальная версия | C++хх | +| **Поддерживает стандарт иерархии** | Linux/Windows/MacOS | +/- | +| **Способ распространения** | header-only / статическая / динамическая | — | +| **ОС** | Linux / Windows / macOS | +/-, компиляторы | +| **Активность** | среднее число коммитов | коммитов/мес (за последний мес) | +| **Issues** | open, closed/open | n, коэффициент | +| **Сообщество** | GitHub Stars | n | +| **Проходит ли CI** | CI | +/- | + +> ? - не указано в README. + + +## Сравнение парсеров командной строки (CLI) + +| Критерий | CLI11 | cxxopts | +|:---|:---:|:---:| +| Зависимости | - | - | +| **Источники** | | | +|  CLI (argv) | + | + | +|  env | + | - | +|  Файлы | - | - | +| Стандарт C++ | ≥ C++11 | ≥ C++11 | +| **Поддерживает стандарт иерархии** | | | +|  Linux | + | + | +|  Windows | + | + | +|  MacOS | + | + | +| Способ распространения | header-only | header-only +| **ОС** | | | +|  Linux | + (GCC 4.8+, Clang 3.5+) | + (GCC ≥ 4.9, Clang ≥ 3.1) | +|  Windows | + (MSVC ≥ 2015) | + (MSVC ≥ 2015) | +|  macOS | + (AppleClang 7+) | + (Clang ≥ 3.1 с libc++) | +| **Сообщество** | | | +|  Активность (коммитов/мес) | 4 | 1 | +|  Issues всего | 506 | 307 | +|  Issues closed/open | 9 | 6 | +|  Stars | 4.3k | 4.7k | +| Проходит ли CI | + | + | + | + + +## Парсеры файлов + +| Критерий | nlohmann/json | yaml-cpp | toml++ | toml11 | config-cxx | taocpp/config +|:---|:---:|:---:|:---:|:---:|:---:|:---:| +| Зависимости | - | - | - | - | - | - | +| **Источники** | +|  CLI (argv) | - | - | - | - | - | - | +|  env | - | - | - | - | + | + | +|  Файлы | JSON | YAML | TOML | TOML | JSON, YAML, XML | JSON, JAXN | +| Стандарт C++ | ≥ C++11 | ≥ C++11 | ≥ C++17 | ≥ C++11 | ≥ C++20 | ≥ C++17 | +| **Поддерживает стандарт иерархии** | +|  Linux | + | + | + | + | + | + | +|  Windows | + | + | + | + | + | + | +|  MacOS | + | + | + | + | + | + | +| Способ распространения | header-only | статическая/динамическая¹ | header-only/статическая/динамическая¹ | header-only/статическая/динамическая¹ | header-only | header-only | +| **ОС** | | | +|  Linux | + (GCC 4.8–14.2, Clang 3.4–21.0) | + (GCC, Clang) | + (Clang 8+, GCC 8+) | + (GCC, Clang) | + (GCC 13+, Clang 16+) | + +|  Windows | + (MSVC 2015–2022) | + (MSVC) | + (MSVC VS2019+) | + (MSVC, MinGW) | + (MSVC 143+ (VS 2022)) | + +|  macOS | + (AppleClang 9.1–16.0) | + (Xcode, AppleClang) | + (AppleClang) | + (AppleClang) | + (AppleClang 16+) | + +| **Сообщество** | | | +|  Активность (коммитов/мес) | 2 | 9 | 2 | 4 | 2 | 13 | +|  Issues всего | 3271 | 905 | 193 | 194 | 24 | 5 | +|  Issues closed/open | 68 | 3 | 8 | 5 | 0 open | 0 open | +|  Stars | 49.4k | 15k | 2k | 1.3k | 31 | 194 | +| Проходит ли CI | + | + | + | - | + | ? | + +> ¹ Опционально. + +## Сравнение универсальных инструментов + +| Критерий | Boost.Program_options +|:---|:---:| +| Зависимости | +¹ | +| **Источники** | | | +|  CLI (argv) | + | + | +|  env | + | + | +|  Файлы | INI | +| Стандарт C++ | ≥ C++3 | +| **Поддерживает стандарт иерархии** | | | +|  Linux | + | +|  Windows | + | +|  MacOS | + | +| Способ распространения | статическая/динамическая² | +| **ОС** | | | +|  Linux | + (GCC 5+, Clang 3.6+) | +|  Windows | + (MSVC 2015 (vc140)+) | +|  macOS | + (AppleClang) | +| **Сообщество** | | | +|  Активность (коммитов/мес) | 12 | +|  Issues всего | 400 | +|  Issues closed/open | 1 | +|  Stars | 9.4k | +| Проходит ли CI | + | + | + +> ¹ Список зависимостей: +> 1. Boost.Any +> 2. Boost.Bind +> 3. Boost.Config +> 4. Boost.Core +> 5. Boost.Detail +> 6. Boost.Function +> 7. Boost.Iterator +> 8. Boost.Lexical Cast +> 9. Boost.Smart Ptr +> 10. Boost.ThrowException +> 11. Boost.Tokenizer +> 12. Boost.Type Traits + +> ² Опционально. + + +## Итог: +По результатам сравнения, вероятно, лучшим вариантом будет использование `CLI11` и `nlohmann/json`, так как они, в отличие от `Boost.program_options`, не требуют зависимостей и являются header-only. + + +# Механизм конфигурирования библиотеки SPLA + +## МАНУАЛ: + +## НАЗВАНИЕ +spla - это фреймворк для математических вычислений с использованием GPU/ускорения + +## СИНОПСИС + +```cpp +#include + + +int main(int argc, char* argv[]) { + + spla_configure(argc, argv); + + return 0; +} +``` +Функция `spla_configure(argc, argv)` необходима для обработки аргументов командной строки, переменных окружения и конфигурационных файлов. + +(будет написана) + +## ОПИСАНИЕ +**spla** - это библиотека с открытым исходным кодом, предоставляющая примитивы разреженной линейной алгебры (матрицы, векторы, скаляры) для математических вычислений с ускорением на GPU. Библиотека поддерживает широкий спектр операций, включая умножение матрицы на вектор, решение систем линейных уравнений и другие алгоритмы разреженной алгебры. + +Для большинства пользователей важно, на каком ускорителе будет запускаться их проект, поэтому необходима возможность явного указания графического ускорителя, OpenCL платформы, количества очередей команд и других параметров, влияющих на производительность и удобство пользователя. + +Библиотека предоставляет механизмы конфигурирования, показанные в порядке убывания приоритета. + +1. Аргументы командной строки 1. +2. Переменные окружения 2. +3. Пользовательский конфигурационный TOML/INI файл 3. +4. Системный конфигурационный TOML/INI файл 3. + +Системный конфигурационный файл имеет наименьший приоритет. \ +Его настройки переопределяются пользовательским конфигурационным файлом, переменными окружения и аргументами командной строки. + +Пользовательский конфигурационный файл имеет приоритет над системным +конфигурационным файлом. \ +Его настройки переопределяются переменными +окружения и аргументами командной строки. + +Переменные окружения имеют приоритет над системным и пользовательским конфигурационными файлами, но уступают аргументам командной строки. + +Аргументы командной строки имеют наивысший приоритет и переопределяют любые настройки, определенные предыдущими способами. + +Если источника конфигурации с меньшим приоритетом не существует, библиотека игнорирует его и переходит к следующему источнику конфигурации. + +Библиотека требует настройки всех обязательных параметров. Без этого будет генерироваться исключение `IncompleteConfigError`.\ +Чтобы исключение не возникало, пользователь должен явно настроить все параметры любым [способом](#configuration_methods). + +> 1 Описание аргументов командной строки приведены в разделе [ПАРАМЕТРЫ КОМАНДНОЙ СТРОКИ](#options). \ +2 Описание переменнных окружения приведены в разделе [ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ](#env_variables). \ +3 Шаблон конфигурационного файла с описанием приведен [здесь](#config). + +## ПАРАМЕТРЫ КОМАНДНОЙ СТРОКИ +```bash +-sh +--spla-help +``` +Выводит список всех доступных флагов spla с кратким описанием. + +```bash +-sv +--spla-version +``` +Показывает номер версии spla. + +```bash +-ss <путь> +--spla-sconf <путь> +``` +Путь к системному конфигурационному файлу. + +По умолчанию ищет файл в стандартном для данной ОС месте.\ +`/etc/spla/spla.toml` на Linux. \ +`/Library/Preferences/spla/spla.toml` на macOS. \ +`%ProgramData%\spla\spla.toml` на Windows. + +```bash +-su <путь> +--spla-uconf <путь> +``` +Путь к пользовательскому конфигурационному файлу. + +По умолчанию ищет файл в стандартном для данной ОС месте. \ +`~/.config/spla/spla.toml` на Linux. \ +`~/Library/Application Support/spla/spla.toml` на macOS. \ +`%APPDATA%\spla\spla.toml` на Windows. + +```bash +-sp <индекс> +--spla-platform <индекс> +``` +Индекс OpenCL платформы. + +```bash +-sd <индекс> +--spla-device <индекс> +``` +Индекс OpenCL-устройства выбранной платформы (GPU/CPU). + +```bash +-sq <число> +--spla-queues <число> +``` +Количество очередей команд. + +Увеличение числа очередей может повысить производительность +за счет параллельного выполнения независимых задач, но слишком большое значение может снизить производительность из-за накладных расходов. \ +Рекомендуется начинать с 1 и экспериментально подбирать оптимальное значение. + +```bash +-sV <уровень> +--spla-verbosity <уровень> +``` +Уровень детализации отладочной информации, выводимой библиотекой. + +| Уровень | Значение | Описание | +|:-------:|:---------|:---------| +| **0** | OFF | Вывод отключён. Сообщения не выводятся. | +| **1** | ERROR | Только критические ошибки. | +| **2** | WARNING | Ошибки и предупреждения. | +| **3** | INFO | Основные этапы работы библиотеки. | + +Что выводится на каждом уровне: + +1. ERROR — `Error`, `NoAcceleration`, `PlatformNotFound`, `DeviceNotFound`, `InvalidState`, `InvalidArgument`, `CompilationError` + +2. WARNING — добавляет к ERROR: `NoValue`, `NotImplemented` + +3. INFO — добавляет к WARNING: `Ok` - информация об инициализации, выборе платформы/устройства, загрузке конфигурации + +```bash +-sp +--spla-profiling +``` +Профилирование очередей команд OpenCL. + +Bключение профилирования добавляет накладные расходы на каждую +операцию и может замедлить выполнение. + +```bash +-sa <тип> +--spla-allocator <тип> +``` +Тип аллокатора памяти для OpenCL-устройства. \ +Ожидает: lineral | general. + +Выбор аллокатора влияет на производительность операций с памятью. +Линейный аллокатор даёт прирост скорости за счёт последовательного выделения, +но может привести к фрагментации памяти при длительной работе. +Общий аллокатор более надёжен, но медленнее. + +```bash +-sS <размер> +--spla-allocator-size <размер> +``` +Размер линейного аллокатора в байтах. + +Увеличение размера позволяет выделять больше памяти за один раз, но увеличивает потребление ресурсов. \ +Этот параметр необходим только при использовании линейного аллокатора `--spla-allocator linear`. +При использовании общего аллокатора можно не указывать. + +Пример: +```bash +./my_program --spla-uconf ~/my_configs/spla.toml --spla-platform 1 -sd 0 --spla-verbosity 3 -sp true --spla-allocator linear --spla-allocator-size 1048576 +``` + +## ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ +Для всех параметров командной строки (кроме --spla-help, --spla-version) существуют аналоги в виде переменных окружения. + +`SPLA_SYSTEM_CONFIG_PATH` \ +Путь к системному конфигурационному файлу. Аналог параметра --spla-sconf. + +`SPLA_USER_CONFIG_PATH` \ +Путь к пользовательскому конфигурационному файлу. Аналог параметра --spla-uconf. + +`SPLA_OPENCL_PLATFORM` \ +Индекс OpenCL платформы. Аналог параметра --spla-platform. + +`SPLA_OPENCL_DEVICE`\ +Индекс OpenCL-устройства на выбранной платформе. +Аналог параметра --spla-device. + +`SPLA_QUEUES`\ +Количество очередей команд OpenCL. +Аналог параметра --spla-queues. + +`SPLA_VERBOSITY`\ +Уровень детализации отладочной информации. +Аналог параметра --spla-verbosity.\ +Принимает значения от 0 до 3. + +`SPLA_PROFILING`\ +Включает профилирование очередей команд OpenCL. +Аналог параметра --spla-profiling. + +`SPLA_ALLOCATOR`\ +Тип аллокатора памяти для OpenCL-устройства. +Аналог параметра --spla-allocator.\ +Принимает значения: linear, general. + +`SPLA_ALLOCATOR_SIZE`\ +Размер линейного аллокатора в байтах. +Аналог параметра --spla-allocator-size.\ +Имеет смысл только при использовании линейного аллокатора. + + +## КОНФИГУРАЦИОННЫЕ ФАЙЛЫ +Для всех аргументов командной строки (кроме --spla-help, --spla-version, --spla-sconf, --spla-uconf) и всех переменных окружения существуют аналоги в виде параметров конфигурационного файла. + +Формат файла - TOML или INI. + +Типы значений. +1. platform = int +2. device = int +3. queues = int +4. verbosity = int +5. profiling = true | false +6. allocator = linear | general +7. allocator_size = int + +Примеры конфигурационных файлов.\ +Этот формат справедлив как для пользовательского, так и для системного конфигурационного файла. + +Формат TOML. +```toml +platform = 1 +device = 0 +queues = 4 +verbosity = 2 +profiling = false +allocator = "linear" +allocator_size = 1048576 +``` + +Формата INI. +```ini +platform = 1 +device = 0 +queues = 4 +verbosity = 2 +profiling = true +allocator = linear +allocator_size = 1048576 +``` \ No newline at end of file diff --git a/src/opencl/cl_configure.cpp b/src/opencl/cl_configure.cpp new file mode 100644 index 000000000..a28f1de2c --- /dev/null +++ b/src/opencl/cl_configure.cpp @@ -0,0 +1,445 @@ +#include "cl_configure.hpp" +#include "opencl.hpp" + +namespace spla { + + Config config_user_and_system; + Config config_cli_and_env; + Config config_final; + + void Config::merge(const Config& source) { + if (source.system_config_path.has_value()) system_config_path = source.system_config_path; + if (source.user_config_path.has_value()) user_config_path = source.user_config_path; + if (source.platform.has_value()) platform = source.platform; + if (source.device.has_value()) device = source.device; + if (source.queues.has_value()) queues = source.queues; + if (source.profiling.has_value()) profiling = source.profiling; + if (source.allocator.has_value()) allocator = source.allocator; + if (source.allocator_size.has_value()) allocator_size = source.allocator_size; + if (source.default_wgs.has_value()) default_wgs = source.default_wgs; + if (source.wave_size.has_value()) wave_size = source.wave_size; + if (source.verbosity.has_value()) verbosity = source.verbosity; + } + + void Config::reset() { + *this = Config{}; + } + + std::string get_home_directory() { +#ifdef _WIN32 + const char* home = std::getenv("USERPROFILE"); + if (home) return std::string(home); + + const char* drive = std::getenv("HOMEDRIVE"); + const char* path = std::getenv("HOMEPATH"); + if (drive && path) return std::string(drive) + std::string(path); + return ""; + +#elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) + const char* home = std::getenv("HOME"); + if (home) return std::string(home); + + struct passwd* pw = getpwuid(getuid()); + if (pw) return std::string(pw->pw_dir); + return ""; + +#else + #error "Unsupported operating system" +#endif + } + + std::string get_default_system_config_path() { +#ifdef _WIN32 + const char* program_data = std::getenv("PROGRAMDATA"); + if (program_data) { + return std::string(program_data) + "\\spla\\"; + } + return "C:\\ProgramData\\spla\\"; + +#elif defined(__APPLE__) + return "/Library/Application Support/spla/"; + +#elif defined(__linux__) || defined(__unix__) + return "/etc/spla/"; + +#else + #error "Unsupported operating system" +#endif + } + + std::string get_default_user_config_path() { + std::string home = get_home_directory(); + if (home.empty()) { + return ""; + } + +#ifdef _WIN32 + const char* app_data = std::getenv("APPDATA"); + if (app_data) return std::string(app_data) + "\\spla\\"; + return home + "\\AppData\\Roaming\\spla\\"; + +#elif defined(__APPLE__) + return home + "/Library/Application Support/spla/"; + +#elif defined(__linux__) || defined(__unix__) + return home + "/.config/spla/"; + +#else + #error "Unsupported operating system" +#endif + } + + std::string find_first_json_file(const std::string& directory) { + if (directory.empty() || !std::filesystem::exists(directory)) { + return ""; + } + + for (const auto& entry : std::filesystem::directory_iterator(directory)) { + if (entry.is_regular_file()) { + std::string path = entry.path().string(); + if (path.size() >= 5 && + path.compare(path.size() - 5, 5, ".json") == 0) { + return path; + } + } + } + return ""; + } + + ConfigStatus load_from_file(const std::string& path, Config& cfg) { + try { + std::ifstream file(path); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << path << std::endl; + return ConfigStatus::OpenFileError; + } + + nlohmann::json config_data = nlohmann::json::parse(file); + + if (config_data.contains("platform")) { + cfg.platform = config_data["platform"].get(); + } + if (config_data.contains("device")) { + cfg.device = config_data["device"].get(); + } + if (config_data.contains("queues")) { + cfg.queues = config_data["queues"].get(); + } + if (config_data.contains("profiling")) { + cfg.profiling = config_data["profiling"].get(); + } + if (config_data.contains("allocator")) { + cfg.allocator = config_data["allocator"].get(); + } + if (config_data.contains("allocator_size")) { + cfg.allocator_size = config_data["allocator_size"].get(); + } + if (config_data.contains("default_wgs")) { + cfg.default_wgs = config_data["default_wgs"].get(); + } + if (config_data.contains("wave_size")) { + cfg.wave_size = config_data["wave_size"].get(); + } + if (config_data.contains("verbosity")) { + cfg.verbosity = config_data["verbosity"].get(); + } + + return ConfigStatus::Ok; + + } catch (const nlohmann::json::exception& e) { + std::cerr << "Failed to parse config files" << std::endl; + std::cerr << "Error parsing JSON file '" << path << "': " << e.what() << std::endl; + return ConfigStatus::UserOrSystemConfParseError; + } + } + + ConfigStatus parse_system_and_user_conf(const Config& cli_env_config, Config& file_config) { + std::string sys_dir = cli_env_config.system_config_path.value_or(get_default_system_config_path()); + std::string user_dir = cli_env_config.user_config_path.value_or(get_default_user_config_path()); + + ConfigStatus status = ConfigStatus::Ok; + + if (sys_dir != "") { + std::string sys_file = find_first_json_file(sys_dir); + if (sys_file != "") { + std::cout << "Loading system config: " << sys_file << std::endl; + status = load_from_file(sys_file, file_config); + if (status == ConfigStatus::UserOrSystemConfParseError) return status; + } else + std::cout << "No system config found in: " << sys_dir << std::endl; + } else + std::cout << "System config directory is not set. Skipping." << std::endl; + + if (user_dir != "") { + std::string user_file = find_first_json_file(user_dir); + if (user_file != "") { + std::cout << "Loading user config: " << user_file << std::endl; + status = load_from_file(user_file, file_config); + if (status == ConfigStatus::UserOrSystemConfParseError) return status; + } else + std::cout << "No user config found in: " << user_dir << std::endl; + } else + std::cout << "User config directory is not set. Skipping." << std::endl; + + if (status == ConfigStatus::OpenFileError) return status; + + return ConfigStatus::Ok; + } + + std::string get_spla_version() { + return "SPLA version: 0.0.0"; + } + + ConfigStatus parse_cli_and_env(int argc, char** argv, Config& cfg) { + CLI::App app{"SPLA configuration"}; + + app.add_flag("-sh,--spla-help", cfg.help, "Show help and exit"); + app.add_flag("-sv,--spla-version", cfg.version, "Show version and exit"); + + app.add_option("-ss,--spla-sconf", cfg.system_config_path, + "Path to system configuration file\n" + "Config key: (not used in config file)") + ->envname("SPLA_SYSTEM_CONFIG_PATH"); + + app.add_option("-su,--spla-uconf", cfg.user_config_path, + "Path to user configuration file\n" + "Config key: (not used in config file)") + ->envname("SPLA_USER_CONFIG_PATH"); + + app.add_option("-sp,--spla-platform", cfg.platform, + "OpenCL platform index\n" + "Config key: platform") + ->envname("SPLA_OPENCL_PLATFORM"); + + app.add_option("-sd,--spla-device", cfg.device, + "OpenCL device index\n" + "Config key: device") + ->envname("SPLA_OPENCL_DEVICE"); + + app.add_option("-sq,--spla-queues", cfg.queues, + "Number of command queues\n" + "Config key: queues") + ->envname("SPLA_QUEUES"); + + app.add_flag("-pr,--spla-profiling", cfg.profiling, + "Enable profiling of command queues\n" + "Config key: profiling\n") + ->envname("SPLA_PROFILING"); + + app.add_option("-sa,--spla-allocator", cfg.allocator, + "Allocator type: linear or general\n" + "Config key: allocator") + ->envname("SPLA_ALLOCATOR"); + + app.add_option("-as,--spla-allocator-size", cfg.allocator_size, + "Linear allocator size in bytes\n" + "Required for 'linear' allocator. Ignored for 'general'.\n" + "Config key: allocator_size") + ->envname("SPLA_ALLOCATOR_SIZE"); + + app.add_option("-dw,--spla-default-wgs", cfg.default_wgs, + "Default work group size\n" + "Config key: default_wgs") + ->envname("SPLA_DEFAULT_WGS"); + + app.add_option("ws,--spla-wave-size", cfg.wave_size, + "Wave size for device\n" + "Config key: wave_size") + ->envname("SPLA_WAVE_SIZE"); + + app.add_option("-sV,--spla-verbosity", cfg.verbosity, + "Verbosity level:\n" + " 0: No output\n" + " 1: Errors only\n" + " 2: Errors + warnings\n" + " 3: All messages (info, warnings, errors)") + ->envname("SPLA_VERBOSITY"); + + try { + app.parse(argc, argv); + } catch (const CLI::ParseError& e) { + std::cerr << "Failed to parse CLI/ENV" << std::endl; + app.exit(e); + return ConfigStatus::CliOrEnvParseError; + } + + if (cfg.help) { + std::cout << app.help() << std::endl; + return ConfigStatus::HelpRequested; + } + + if (cfg.version) { + std::cout << get_spla_version() << std::endl; + return ConfigStatus::VersionRequested; + } + + return ConfigStatus::Ok; + } + + + ConfigStatus check_platform_and_device(int platform_index, int device_index) { + if (platform_index < 0) { + std::cerr << "Error: platform must be >= 0 (got " << platform_index << ")" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + std::vector platforms; + cl::Platform::get(&platforms); + + if (static_cast(platform_index) >= platforms.size()) { + std::cerr << "Error: Platform " << platform_index << " not found" << std::endl; + return ConfigStatus::PlatformNotFound; + } + + if (device_index < 0) { + std::cerr << "Error: device must be >= 0 (got " << device_index << ")" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + std::vector devices; + platforms[platform_index].getDevices(CL_DEVICE_TYPE_ALL, &devices); + + if (static_cast(device_index) >= devices.size()) { + std::cerr << "Error: Device " << device_index << " not found on platform " << platform_index; + std::cerr << ". Available devices: " << devices.size() << std::endl; + return ConfigStatus::DeviceNotFound; + } + + return ConfigStatus::Ok; + } + + + ConfigStatus validate(const Config& cfg) { + + if (!cfg.platform.has_value()) { + std::cerr << "Error: platform is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.device.has_value()) { + std::cerr << "Error: device is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.queues.has_value()) { + std::cerr << "Error: queues is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.profiling.has_value()) { + std::cerr << "Error: profiling is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.allocator.has_value()) { + std::cerr << "Error: allocator is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (cfg.allocator.value() == "linear" && !cfg.allocator_size.has_value()) { + std::cerr << "Error: allocator_size is required for linear allocator" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.default_wgs.has_value()) { + std::cerr << "Error: default wgs is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.wave_size.has_value()) { + std::cerr << "Error: wave size is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + if (!cfg.verbosity.has_value()) { + std::cerr << "Error: verbosity is required" << std::endl; + return ConfigStatus::MissedParametrs; + } + + + ConfigStatus status; + + status = check_platform_and_device(*cfg.platform, *cfg.device); + if (status != ConfigStatus::Ok) { + return status; + } + + + if (*cfg.queues <= 0) { + std::cerr << "Error: queues must be > 0 (got " << *cfg.queues << ")" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + + if (*cfg.allocator != "linear" && *cfg.allocator != "general") { + std::cerr << "Error: allocator must be 'linear' or 'general' (got '" << *cfg.allocator << "')" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + if (*cfg.allocator == "linear") { + if (*cfg.allocator_size == 0) { + std::cerr << "Error: allocator_size must be > 0 for linear allocator" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + } + + + if (*cfg.default_wgs <= 0) { + std::cerr << "Error: default_wgs must be > 0 (got " << *cfg.default_wgs << ")" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + if (*cfg.wave_size <= 0) { + std::cerr << "Error: wave_size must be > 0 (got " << *cfg.wave_size << ")" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + + if (*cfg.verbosity < 0 || *cfg.verbosity > 3) { + std::cerr << "Error: verbosity must be between 0 and 3 (got " << *cfg.verbosity << ")" << std::endl; + return ConfigStatus::InvalidConfigParams; + } + + return ConfigStatus::Ok; + } + + ConfigStatus apply(const Config& cfg) { + return ConfigStatus::Ok; + } + + ConfigStatus configure(int argc, char** argv) { + config_user_and_system.reset(); + config_cli_and_env.reset(); + config_final.reset(); + + ConfigStatus status; + + status = parse_cli_and_env(argc, argv, config_cli_and_env); + if (status != ConfigStatus::CliOrEnvParseError) return status; + else { + std::exit(1); + } + + status = parse_system_and_user_conf(config_cli_and_env, config_user_and_system); + if (status != ConfigStatus::UserOrSystemConfParseError) return status; + else { + std::exit(1); + } + + config_final = config_user_and_system; + config_final.merge(config_cli_and_env); + + status = validate(config_final); + if (status != ConfigStatus::Ok) { + std::exit(1); + } + + std::cout << "Configuration parametrs:" << std::endl; + std::cout << "OpenCL platform index: " << config_final.platform.value() << std::endl; + std::cout << "OpenCL device index: " << config_final.device.value() << std::endl; + std::cout << "Queues number: " << config_final.queues.value() << std::endl; + std::cout << "Profiling: " << config_final.profiling.value() << std::endl; + std::cout << "Allocator: " << config_final.allocator.value() << std::endl; + if (config_final.allocator == "linear") std::cout << "Linear allocator size: " << config_final.allocator_size.value() << std::endl; + std::cout << "Wave size: " << config_final.wave_size.value() << std::endl; + std::cout << "Default wgs: " << config_final.default_wgs.value() << std::endl; + std::cout << "Verbosity: " << config_final.verbosity.value() << std::endl; + + status = apply(config_final); + if (status != ConfigStatus::Ok) std::exit(1); + + return ConfigStatus::Ok; + } +}// namespace spla \ No newline at end of file diff --git a/src/opencl/cl_configure.hpp b/src/opencl/cl_configure.hpp new file mode 100644 index 000000000..625037c23 --- /dev/null +++ b/src/opencl/cl_configure.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include "CLI/CLI.hpp" +#include + +#ifdef _WIN32 +#elif defined(__APPLE__) || defined(__linux__) || defined(__unix__) + #include + #include +#endif + +namespace spla { + + struct Config { + std::optional help; + std::optional version; + std::optional system_config_path; + std::optional user_config_path; + std::optional platform; + std::optional device; + std::optional queues; + std::optional profiling; + std::optional allocator; + std::optional allocator_size; + std::optional default_wgs; + std::optional wave_size; + std::optional verbosity; + + void merge(const Config& source); + void reset(); + }; + + enum ConfigStatus { + Ok, + HelpRequested, + VersionRequested, + + CliOrEnvParseError, + UserOrSystemConfParseError, + OpenFileError, + + MissedParametrs, + PlatformNotFound, + DeviceNotFound, + InvalidConfigParams + }; + + extern Config config_user_and_system; + extern Config config_cli_and_env; + extern Config config_final; + + std::string get_home_directory(); + std::string get_default_system_config_path(); + std::string get_default_user_config_path(); + std::string find_first_json_file(const std::string& directory); + ConfigStatus load_from_file(const std::string& path, Config& cfg); + + ConfigStatus parse_system_and_user_conf(const Config& cli_env_config, Config& file_config); + + std::string get_spla_version(); + ConfigStatus parse_cli_and_env(int argc, char** argv, Config& cfg); + + ConfigStatus check_platform_and_device(int platform_index, int device_index); + ConfigStatus validate(const Config& cfg); + ConfigStatus apply(const Config& cfg); + + ConfigStatus configure(int argc, char** argv); + +}// namespace spla \ No newline at end of file