From 141247456bc1e8203a4a19def9b6b509e8384e4e Mon Sep 17 00:00:00 2001 From: simiyutin Date: Mon, 16 Sep 2024 23:49:53 +0300 Subject: [PATCH] added task01 --- .figures/clion_edit_configurations.png | Bin 0 -> 10089 bytes .figures/clion_working_directory.png | Bin 0 -> 48681 bytes .github/workflows/cmake.yml | 2 +- CMakeLists.txt | 4 +- README.md | 109 ++------------ libs/CMakeLists.txt | 1 + src/cl/aplusb.cl | 23 +++ src/cl/clion_defines.cl | 73 ++++++++++ src/main.cpp | 187 ++++++++++++++++++------- 9 files changed, 242 insertions(+), 157 deletions(-) create mode 100644 .figures/clion_edit_configurations.png create mode 100644 .figures/clion_working_directory.png create mode 100644 src/cl/aplusb.cl create mode 100644 src/cl/clion_defines.cl diff --git a/.figures/clion_edit_configurations.png b/.figures/clion_edit_configurations.png new file mode 100644 index 0000000000000000000000000000000000000000..bd179b97adc69ce2a5733cc89561e219129923fc GIT binary patch literal 10089 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 F{{YPI%m@Gg literal 0 HcmV?d00001 diff --git a/.figures/clion_working_directory.png b/.figures/clion_working_directory.png new file mode 100644 index 0000000000000000000000000000000000000000..8b8e732b2e60d0bdb09a75345367b674d5467b85 GIT binary patch literal 48681 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 (например, ``nvidia-384`` или ``nvidia-535``) - -AMD: [скачав](https://www.amd.com/en/support) и установив amdgpu-pro драйвер - -Проверка окружения и начало выполнения задания -============================================== - -Про работу под Windows см. в секции [Как работать под windows](#%D0%9A%D0%B0%D0%BA-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D1%82%D1%8C-%D0%BF%D0%BE%D0%B4-windows). - -1. Сделайте fork этого репозитория -2. ``git clone ВАШ_ФОРК_РЕПОЗИТОРИЯ`` -3. ``cd GPGPUTasks2024`` -4. ``git checkout task00`` -5. ``mkdir build`` -6. ``cd build`` -7. ``cmake ..`` -8. ``make -j4`` -9. ``./enumDevices`` должно увидеть хотя бы одну OpenCL-платформу: - -``` -Number of OpenCL platforms: 1 -Platform #1/1 - Platform name: -``` - -Если же вы видите ошибку: -``` -terminate called after throwing an instance of 'std::runtime_error' - what(): Can't init OpenCL driver! -Aborted (Core dumped) -``` -То попробуйте установить ```sudo apt install ocl-icd-libopencl1``` и выполнить ``./enumDevices`` снова. - -Если вы видите ошибку: -``` -: CommandLine Error: Option 'polly' registered more than once! -LLVM ERROR: inconsistency in registered CommandLine options -``` -То, наоборот, может помочь удалить пакет ```sudo apt remove ocl-icd-libopencl1``` и попробовать выполнить ``./enumDevices`` снова. - -Если ``./enumDevices`` не показывает хотя бы одну платформу - создайте **Issue** с перечислением: - - - OS, процессор и видеокарта - - Успешно ли прошла установка Intel-CPU драйвера - - Какое было поведение до установки пакета ``ocl-icd-libopencl1`` и какое поведение стало после - - Вывод ``./enumDevices`` +[![Build Status](https://github.com/GPGPUCourse/GPGPUTasks2024/actions/workflows/cmake.yml/badge.svg?branch=task01&event=push)](https://github.com/GPGPUCourse/GPGPUTasks2024/actions/workflows/cmake.yml) Задание ======= 0. Сделать fork проекта -1. Прочитать все комментарии подряд и выполнить все **TODO** в файле ``src/main.cpp``. Для разработки под Linux рекомендуется использовать CLion. Под Windows рекомендуется использовать CLion+MSVC. Также под Windows можно использовать Visual Studio Community. -2. Отправить **Pull-request** с названием ```Task00 <Имя> <Фамилия> <Аффиляция>```. **Аффиляция** - SPbU/HSE/ITMO. -3. В тексте **PR** укажите вывод программы при исполнении на сервере Github CI (Github Actions) и на вашем компьютере (в **pre**-тэгах, чтобы сохранить форматирование, см. [пример](https://raw.githubusercontent.com/GPGPUCourse/GPGPUTasks2024/task00/.github/pull_request_example.md)). И ваш бранч должен называться так же, как и у меня - **task00**. -4. Убедиться что Github CI (Github Actions) смог скомпилировать ваш код и что все хорошо (если нет - то поправить, пожалуйста, не используйте C++ из будущего, о котором не знает GCC 5.5) -5. Ждать комментарии проверки +1. Прочитать все комментарии подряд и выполнить все **TODO** в файле ``src/main.cpp`` и ``src/cl/aplusb.cl`` +2. Отправить **Pull-request** с названием```Task01 <Имя> <Фамилия> <Аффиляция>``` (добавив в описании вывод работы программы в **pre**-тэгах - см. [пример](https://raw.githubusercontent.com/GPGPUCourse/GPGPUTasks2024/task01/.github/pull_request_example.md)) -**Дедлайн**: 23:59 10 сентября. Но убедиться, что хотя бы одно OpenCL-устройство у вас обнаруживается, лучше как можно раньше, желательно, до начала лекции 8 сентября, чтобы было больше времени на решение проблем если они возникнут (см. **Проверка окружения** выше). +**Дедлайн**: 23:59 22 сентября. -Как работать под Windows -======================== +Комментарии +========== -1. Используйте **64-битный компилятор**, т.е. [amd64](/.figures/clion_msvc_settings.png), а не x86. (Если при запуске видите ``Invalid Parameter - 100``, то вы все еще используете 32-битный компилятор) -2. Рекомендуется использовать CLion+MSVC. -3. Можно использовать Visual Studio 2017 Community или новее, она поддерживает CMake-проекты (``File`` -> ``Open`` -> ``Cmake...``). Разве что передавать аргументы запускаемой программе [неудобно](https://docs.microsoft.com/en-us/cpp/ide/cmake-tools-for-visual-cpp?view=vs-2017#configure-cmake-debugging-sessions). +Т.к. в ``TODO 6`` исходники кернела считываются по относительному пути ``src/cl/aplusb.cl``, то нужно правильно настроить working directory. Например в случае CLion нужно открыть ``Edit configurations`` -> и указать ``Working directory: .../НАЗВАНИЕПАПКИПРОЕКТА`` (см. [подробнее](https://github.com/GPGPUCourse/GPGPUTasks2024/tree/task01/.figures)) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index d50565b2..0b4ea7e9 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(clew) +add_subdirectory(utils) diff --git a/src/cl/aplusb.cl b/src/cl/aplusb.cl new file mode 100644 index 00000000..479624ac --- /dev/null +++ b/src/cl/aplusb.cl @@ -0,0 +1,23 @@ +#ifdef __CLION_IDE__ + // Этот include виден только для CLion парсера, это позволяет IDE "знать" ключевые слова вроде __kernel, __global + // а также уметь подсказывать OpenCL методы, описанные в данном инклюде (такие как get_global_id(...) и get_local_id(...)) + #include "clion_defines.cl" +#endif + +#line 8// Седьмая строчка теперь восьмая (при ошибках компиляции в логе компиляции будут указаны корректные строчки благодаря этой директиве) + +// TODO 5 реализуйте кернел: +// - От обычной функции кернел отличается модификатором __kernel и тем, что возвращаемый тип всегда void +// - На вход дано три массива float чисел; единственное, чем они отличаются от обычных указателей - модификатором __global, т.к. это глобальная память устройства (видеопамять) +// - Четвертым и последним аргументом должно быть передано количество элементов в каждом массиве (unsigned int, главное, чтобы тип был согласован с типом в соответствующем clSetKernelArg в T0D0 10) + +__kernel void aplusb(...) { + // Узнать, какой workItem выполняется в этом потоке поможет функция get_global_id + // см. в документации https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/ + // OpenCL Compiler -> Built-in Functions -> Work-Item Functions + + // P.S. В общем случае количество элементов для сложения может быть некратно размеру WorkGroup, тогда размер рабочего пространства округлен вверх от числа элементов до кратности на размер WorkGroup + // и в таком случае, если сделать обращение к массиву просто по индексу=get_global_id(0), будет undefined behaviour (вплоть до повисания ОС) + // поэтому нужно либо дополнить массив данных длиной до кратности размеру рабочей группы, + // либо сделать return в кернеле до обращения к данным в тех WorkItems, где get_global_id(0) выходит за границы данных (явной проверкой) +} diff --git a/src/cl/clion_defines.cl b/src/cl/clion_defines.cl new file mode 100644 index 00000000..709ccae5 --- /dev/null +++ b/src/cl/clion_defines.cl @@ -0,0 +1,73 @@ +#ifndef clion_defines_cl // pragma once +#define clion_defines_cl + +#ifdef __CLION_IDE__ + +#define __kernel +#define __global +#define __local +#define __constant +#define __private + +#define half float + +struct float2 { float x; }; +struct float3 { float x, y, z; }; +struct float4 { float x, y, z, w; }; + +// https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/commonFunctions.html +#define gentype float +gentype clamp (gentype x, float minval, float maxval); +gentype degrees (gentype radians); +gentype max (gentype x, gentype y); +gentype min (gentype x, gentype y); +gentype mix (gentype x, gentype y, gentype a); +gentype radians (gentype degrees); +gentype sign (gentype x); +gentype smoothstep (gentype edge0, gentype edge1, gentype x); +gentype step (gentype edge, gentype x); +#undef gentype + +// https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/barrier.html +enum cl_mem_fence_flags +{ + CLK_LOCAL_MEM_FENCE, + CLK_GLOBAL_MEM_FENCE +}; +void barrier(cl_mem_fence_flags flags); + +// https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/vectorDataLoadandStoreFunctions.html +#define gentype float +#define gentypen float4 +gentypen vload4 (size_t offset, const gentype *p); +void vstore4 (gentypen data, size_t offset, gentype *p); +void vstore4 (gentypen data, size_t offset, gentype *p); +#undef gentypen +#undef gentype +float vload_half (size_t offset, const half *p); +float4 vload_half4 (size_t offset, const half *p); +void vstore_half (float data, size_t offset, half *p); +void vstore_half4 (float4 data, size_t offset, half *p); +float4 vloada_half4 (size_t offset, const half *p); +void vstorea_half4 (float4 data, size_t offset, half *p); + +// https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/workItemFunctions.html +uint get_work_dim (); +size_t get_global_size (uint dimindx); +size_t get_global_id (uint dimindx); +size_t get_local_size (uint dimindx); +size_t get_local_id (uint dimindx); +size_t get_num_groups (uint dimindx); +size_t get_group_id (uint dimindx); +size_t get_global_offset (uint dimindx); + +#ifndef STATIC_KEYWORD +#define STATIC_KEYWORD static +#endif + +#endif + +// 64 for AMD, 32 for NVidia, 8 for intel GPUs, 1 for CPU +#define WARP_SIZE 64 + +#endif // pragma once diff --git a/src/main.cpp b/src/main.cpp index e1acfd41..527791cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -29,64 +33,141 @@ void reportError(cl_int err, const std::string &filename, int line) { int main() { - // Пытаемся слинковаться с символами OpenCL API в runtime (через библиотеку libs/clew) + // Пытаемся слинковаться с символами OpenCL API в runtime (через библиотеку clew) if (!ocl_init()) throw std::runtime_error("Can't init OpenCL driver!"); - // Откройте - // https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/ - // Нажмите слева: "OpenCL Runtime" -> "Query Platform Info" -> "clGetPlatformIDs" - // Прочитайте документацию clGetPlatformIDs и убедитесь, что этот способ узнать, сколько есть платформ, соответствует документации: - cl_uint platformsCount = 0; - OCL_SAFE_CALL(clGetPlatformIDs(0, nullptr, &platformsCount)); - std::cout << "Number of OpenCL platforms: " << platformsCount << std::endl; - - // Тот же метод используется для того, чтобы получить идентификаторы всех платформ - сверьтесь с документацией, что это сделано верно: - std::vector platforms(platformsCount); - OCL_SAFE_CALL(clGetPlatformIDs(platformsCount, platforms.data(), nullptr)); - - for (int platformIndex = 0; platformIndex < platformsCount; ++platformIndex) { - std::cout << "Platform #" << (platformIndex + 1) << "/" << platformsCount << std::endl; - cl_platform_id platform = platforms[platformIndex]; - - // Откройте документацию по "OpenCL Runtime" -> "Query Platform Info" -> "clGetPlatformInfo" - // Не забывайте проверять коды ошибок с помощью макроса OCL_SAFE_CALL - size_t platformNameSize = 0; - OCL_SAFE_CALL(clGetPlatformInfo(platform, CL_PLATFORM_NAME, 0, nullptr, &platformNameSize)); - // TODO 1.1 - // Попробуйте вместо CL_PLATFORM_NAME передать какое-нибудь случайное число - например 239 - // Т.к. это некорректный идентификатор параметра платформы - то метод вернет код ошибки - // Макрос OCL_SAFE_CALL заметит это, и кинет ошибку с кодом - // Откройте таблицу с кодами ошибок: - // libs/clew/CL/cl.h:103 - // P.S. Быстрый переход к файлу в CLion: Ctrl+Shift+N -> cl.h (или даже с номером строки: cl.h:103) -> Enter - // Найдите там нужный код ошибки и ее название - // Затем откройте документацию по clGetPlatformInfo и в секции Errors найдите ошибку, с которой столкнулись - // в документации подробно объясняется, какой ситуации соответствует данная ошибка, и это позволит, проверив код, понять, чем же вызвана данная ошибка (некорректным аргументом param_name) - // Обратите внимание, что в этом же libs/clew/CL/cl.h файле указаны всевоможные defines, такие как CL_DEVICE_TYPE_GPU и т.п. - - // TODO 1.2 - // Аналогично тому, как был запрошен список идентификаторов всех платформ - так и с названием платформы, теперь, когда известна длина названия - его можно запросить: - std::vector platformName(platformNameSize, 0); - // clGetPlatformInfo(...); - std::cout << " Platform name: " << platformName.data() << std::endl; - - // TODO 1.3 - // Запросите и напечатайте так же в консоль вендора данной платформы - - // TODO 2.1 - // Запросите число доступных устройств данной платформы (аналогично тому, как это было сделано для запроса числа доступных платформ - см. секцию "OpenCL Runtime" -> "Query Devices") - cl_uint devicesCount = 0; - - for (int deviceIndex = 0; deviceIndex < devicesCount; ++deviceIndex) { - // TODO 2.2 - // Запросите и напечатайте в консоль: - // - Название устройства - // - Тип устройства (видеокарта/процессор/что-то странное) - // - Размер памяти устройства в мегабайтах - // - Еще пару или более свойств устройства, которые вам покажутся наиболее интересными + // TODO 1 По аналогии с предыдущим заданием узнайте, какие есть устройства, и выберите из них какое-нибудь + // (если в списке устройств есть хоть одна видеокарта - выберите ее, если нету - выбирайте процессор) + + // TODO 2 Создайте контекст с выбранным устройством + // См. документацию https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/ -> OpenCL Runtime -> Contexts -> clCreateContext + // Не забывайте проверять все возвращаемые коды на успешность (обратите внимание, что в данном случае метод возвращает + // код по переданному аргументом errcode_ret указателю) + + // Контекст и все остальные ресурсы следует освобождать с помощью clReleaseContext/clReleaseQueue/clReleaseMemObject... (да, не очень RAII, но это лишь пример) + + // TODO 3 Создайте очередь выполняемых команд в рамках выбранного контекста и устройства + // См. документацию https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/ -> OpenCL Runtime -> Runtime APIs -> Command Queues -> clCreateCommandQueue + // Убедитесь, что в соответствии с документацией вы создали in-order очередь задач + + unsigned int n = 1000 * 1000; + // Создаем два массива псевдослучайных данных для сложения и массив для будущего хранения результата + std::vector as(n, 0); + std::vector bs(n, 0); + std::vector cs(n, 0); + FastRandom r(n); + for (unsigned int i = 0; i < n; ++i) { + as[i] = r.nextf(); + bs[i] = r.nextf(); + } + std::cout << "Data generated for n=" << n << "!" << std::endl; + + // TODO 4 Создайте три буфера в памяти устройства (в случае видеокарты - в видеопамяти - VRAM) - для двух суммируемых массивов as и bs (они read-only) и для массива с результатом cs (он write-only) + // См. Buffer Objects -> clCreateBuffer + // Размер в байтах соответственно можно вычислить через sizeof(float)=4 и тот факт, что чисел в каждом массиве n штук + // Данные в as и bs можно прогрузить этим же методом, скопировав данные из host_ptr=as.data() (и не забыв про битовый флаг, на это указывающий) + // или же через метод Buffer Objects -> clEnqueueWriteBuffer + + // TODO 6 Выполните TODO 5 (реализуйте кернел в src/cl/aplusb.cl) + // затем убедитесь, что выходит загрузить его с диска (убедитесь что Working directory выставлена правильно - см. описание задания), + // напечатав исходники в консоль (if проверяет, что удалось считать хоть что-то) + std::string kernel_sources; + { + std::ifstream file("src/cl/aplusb.cl"); + kernel_sources = std::string(std::istreambuf_iterator(file), std::istreambuf_iterator()); + if (kernel_sources.size() == 0) { + throw std::runtime_error("Empty source file! May be you forgot to configure working directory properly?"); + } + // std::cout << kernel_sources << std::endl; + } + + // TODO 7 Создайте OpenCL-подпрограмму с исходниками кернела + // см. Runtime APIs -> Program Objects -> clCreateProgramWithSource + // у string есть метод c_str(), но обратите внимание, что передать вам нужно указатель на указатель + + // TODO 8 Теперь скомпилируйте программу и напечатайте в консоль лог компиляции + // см. clBuildProgram + + // А также напечатайте лог компиляции (он будет очень полезен, если в кернеле есть синтаксические ошибки - т.е. когда clBuildProgram вернет CL_BUILD_PROGRAM_FAILURE) + // Обратите внимание, что при компиляции на процессоре через Intel OpenCL драйвер - в логе указывается, какой ширины векторизацию получилось выполнить для кернела + // см. clGetProgramBuildInfo + // size_t log_size = 0; + // std::vector log(log_size, 0); + // if (log_size > 1) { + // std::cout << "Log:" << std::endl; + // std::cout << log.data() << std::endl; + // } + + // TODO 9 Создайте OpenCL-kernel в созданной подпрограмме (в одной подпрограмме может быть несколько кернелов, но в данном случае кернел один) + // см. подходящую функцию в Runtime APIs -> Program Objects -> Kernel Objects + + // TODO 10 Выставите все аргументы в кернеле через clSetKernelArg (as_gpu, bs_gpu, cs_gpu и число значений, убедитесь, что тип количества элементов такой же в кернеле) + { + // unsigned int i = 0; + // clSetKernelArg(kernel, i++, ..., ...); + // clSetKernelArg(kernel, i++, ..., ...); + // clSetKernelArg(kernel, i++, ..., ...); + // clSetKernelArg(kernel, i++, ..., ...); + } + + // TODO 11 Выше увеличьте n с 1000*1000 до 100*1000*1000 (чтобы дальнейшие замеры были ближе к реальности) + + // TODO 12 Запустите выполнения кернела: + // - С одномерной рабочей группой размера 128 + // - В одномерном рабочем пространстве размера roundedUpN, где roundedUpN - наименьшее число, кратное 128 и при этом не меньшее n + // - см. clEnqueueNDRangeKernel + // - Обратите внимание, что, чтобы дождаться окончания вычислений (чтобы знать, когда можно смотреть результаты в cs_gpu) нужно: + // - Сохранить событие "кернел запущен" (см. аргумент "cl_event *event") + // - Дождаться завершения полунного события - см. в документации подходящий метод среди Event Objects + { + size_t workGroupSize = 128; + size_t global_work_size = (n + workGroupSize - 1) / workGroupSize * workGroupSize; + timer t;// Это вспомогательный секундомер, он замеряет время своего создания и позволяет усреднять время нескольких замеров + for (unsigned int i = 0; i < 20; ++i) { + // clEnqueueNDRangeKernel... + // clWaitForEvents... + t.nextLap();// При вызове nextLap секундомер запоминает текущий замер (текущий круг) и начинает замерять время следующего круга } + // Среднее время круга (вычисления кернела) на самом деле считается не по всем замерам, а лишь с 20%-перцентайля по 80%-перцентайль (как и стандартное отклонение) + // подробнее об этом - см. timer.lapsFiltered + // P.S. чтобы в CLion быстро перейти к символу (функции/классу/много чему еще), достаточно нажать Ctrl+Shift+Alt+N -> lapsFiltered -> Enter + std::cout << "Kernel average time: " << t.lapAvg() << "+-" << t.lapStd() << " s" << std::endl; + + // TODO 13 Рассчитайте достигнутые гигафлопcы: + // - Всего элементов в массивах по n штук + // - Всего выполняется операций: операция a+b выполняется n раз + // - Флопс - это число операций с плавающей точкой в секунду + // - В гигафлопсе 10^9 флопсов + // - Среднее время выполнения кернела равно t.lapAvg() секунд + std::cout << "GFlops: " << 0 << std::endl; + + // TODO 14 Рассчитайте используемую пропускную способность обращений к видеопамяти (в гигабайтах в секунду) + // - Всего элементов в массивах по n штук + // - Размер каждого элемента sizeof(float)=4 байта + // - Обращений к видеопамяти 2*n*sizeof(float) байт на чтение и 1*n*sizeof(float) байт на запись, т.е. итого 3*n*sizeof(float) байт + // - В гигабайте 1024*1024*1024 байт + // - Среднее время выполнения кернела равно t.lapAvg() секунд + std::cout << "VRAM bandwidth: " << 0 << " GB/s" << std::endl; } + // TODO 15 Скачайте результаты вычислений из видеопамяти (VRAM) в оперативную память (RAM) - из cs_gpu в cs (и рассчитайте скорость трансфера данных в гигабайтах в секунду) + { + timer t; + for (unsigned int i = 0; i < 20; ++i) { + // clEnqueueReadBuffer... + t.nextLap(); + } + std::cout << "Result data transfer time: " << t.lapAvg() << "+-" << t.lapStd() << " s" << std::endl; + std::cout << "VRAM -> RAM bandwidth: " << 0 << " GB/s" << std::endl; + } + + // TODO 16 Сверьте результаты вычислений со сложением чисел на процессоре (и убедитесь, что если в кернеле сделать намеренную ошибку, то эта проверка поймает ошибку) + // for (unsigned int i = 0; i < n; ++i) { + // if (cs[i] != as[i] + bs[i]) { + // throw std::runtime_error("CPU and GPU results differ!"); + // } + // } + return 0; }