воскресенье, 20 июля 2025 г.

Новые возможности C++20: std::execution::par


Поспрашивал тут у ChatGPT что нового у мире C++ и тут завезли очень классную штуку: std::execution::par. Никак не напрягаясь дополнительно, мы просто делаем тот же самый sort, но благодаря дополнительному параметру он сам распарралеливает все на несколько ядер/потоков. Тот же ЧатЖПТ сам мне дал код примера, который отлично скомпилировался в моей Visual Studio 2022:

#include <execution> // параллельные политики #include <algorithm> // std::sort #include <vector> #include <random> #include <chrono> #include <iostream> int main() { const size_t N = 5'000'000; // Количество элементов // --- 1. Генерация случайных данных --- std::vector<int> data(N); std::mt19937 rng(42); std::uniform_int_distribution<int> dist(0, 1'000'000); std::generate(data.begin(), data.end(), [&] { return dist(rng); }); // Создаём копии для честного сравнения auto data_seq = data; auto data_par = data; // --- 2. Последовательная сортировка --- auto t1 = std::chrono::high_resolution_clock::now(); std::sort(data_seq.begin(), data_seq.end()); auto t2 = std::chrono::high_resolution_clock::now(); std::cout << "std::sort (seq): " << std::chrono::duration<double>(t2 - t1).count() << " s\n"; // --- 3. Параллельная сортировка --- auto t3 = std::chrono::high_resolution_clock::now(); std::sort(std::execution::par, data_par.begin(), data_par.end()); auto t4 = std::chrono::high_resolution_clock::now(); std::cout << "std::sort (par): " << std::chrono::duration<double>(t4 - t3).count() << " s\n"; // --- 4. Проверка корректности --- std::cout << "First elements: seq=" << data_seq[0] << ", par=" << data_par[0] << "\n"; return 0; }

Результаты такие:

std::sort (seq): 0.371002 s
std::sort (par): 0.0917933 s

Запущено на AMD Ryzen 7 7730U (8c/16t).

Нуу, очень неплохо: добавлением всего одного параметра увеличиваем скорость в 4 раза!

Чтобы это работало, надо не забыть оказать версию C++ в Visual Studio:




четверг, 19 июня 2025 г.

OpenCL: добавление поддержки видеокарт AMD в CMakeLists.txt

 Вот этот пример мы модифицируем так, чтобы он мог компилироваться для AMD видеокарт тоже. CMakeLists.txt получился вот такой:

  1. #
  2. cmake_minimum_required (VERSION 3.8)
  3. project ("MemakePrj")
  4. # Add source to this project's executable.
  5. add_executable (MemakePrj "main.cpp" "Memake/Memake.cpp" "Memake/Vector2d.cpp")
  6. # SDL2 headers
  7. target_include_directories(MemakePrj PRIVATE "SDL2-2.0.14/include")
  8. # gpu vendor: amd, nvidia
  9. set(gpu_vendor "amd")
  10. #
  11. if("${gpu_vendor}" STREQUAL "amd")
  12. message("gpu_vendor is 'amd'")
  13. # set define
  14. target_compile_definitions(MemakePrj PUBLIC GPU_VENDOR_IS_AMD)
  15. # OpenCL library
  16. set(opencl_lib_folder "$ENV{OCL_ROOT}/lib")
  17. # OpenCL headers
  18. target_include_directories(MemakePrj PRIVATE "$ENV{OCL_ROOT}/include")
  19. elseif("${gpu_vendor}" STREQUAL "nvidia")
  20. message("gpu_vendor is 'nvidia'")
  21. # set define
  22. target_compile_definitions(MemakePrj PUBLIC GPU_VENDOR_IS_NVIDIA)
  23. # OpencCL headers
  24. target_include_directories(MemakePrj PRIVATE "$ENV{CUDA_PATH}/include")
  25. # OpenCL library
  26. set(opencl_lib_folder "$ENV{CUDA_PATH}/lib")
  27. else()
  28. message("gpu_vendor is 'UNKNOWN'")
  29. endif()

  30. # add SDL_MAIN_HANDLED definition to avoid
  31. # "LNK2019 unresolved external symbol SDL_main referenced in function main_getcmdline"
  32. add_definitions( -DSDL_MAIN_HANDLED )
  33. # SDL library folder
  34. set(SDL2_lib_folder "${PROJECT_SOURCE_DIR}/SDL2-2.0.14/lib")
  35. message(${CMAKE_BUILD_TYPE})
  36. # check 32 or 64 bits
  37. if(CMAKE_SIZEOF_VOID_P EQUAL 8)
  38. # 64 bits
  39. set(SDL2_lib_folder "${SDL2_lib_folder}/x64")
  40. if("${gpu_vendor}" STREQUAL "amd")
  41. set(opencl_lib_folder "${opencl_lib_folder}")
  42. elseif("${gpu_vendor}" STREQUAL "nvidia")
  43. set(opencl_lib_folder "${opencl_lib_folder}/x64")
  44. else()
  45. message("gpu_vendor is 'UNKNOWN'")
  46. endif()
  47. elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
  48. # 32 bits
  49. set(SDL2_lib_folder "${SDL2_lib_folder}/x86")
  50. if("${gpu_vendor}" STREQUAL "amd")
  51. message("do not want to support 32-bit version for amd")
  52. elseif("${gpu_vendor}" STREQUAL "nvidia")
  53. set(opencl_lib_folder "${opencl_lib_folder}/Win32")
  54. else()
  55. message("gpu_vendor is 'UNKNOWN'")
  56. endif()
  57. endif()
  58. # link SDL2 static lib
  59. target_link_libraries(MemakePrj ${SDL2_lib_folder}/SDL2.lib)
  60. target_link_libraries(MemakePrj ${SDL2_lib_folder}/SDL2main.lib)
  61. # link OpenCL library
  62. target_link_libraries(MemakePrj ${opencl_lib_folder}/OpenCL.lib)
  63. # copy dynamic lib to folder with executable file
  64. file(COPY ${SDL2_lib_folder}/SDL2.dll DESTINATION ${PROJECT_BINARY_DIR})

 Получилось гораздо более громоздко и неудобочитаемо, чем было. В строке 12 мы указываем, какой у нас производитель видеокарты (пока что их два: AMD, Nvidia) IF внутри IF - это не то, что способствует облегчению чтения подобного кода, но это, тем не менее, позволяет собрать проект для двух и более производителей видеокарт, а это главное. Позже подумаю, как правильно разбить CMakeLists.txt на части.

В коде же это выглядит так:



вторник, 17 июня 2025 г.

Настройка OpenCL для видеокарт AMD

Прежде, чем начать, отмечу, что компания AMD на данный момент не поддерживает свой OpenCL SDK, к сожалению. Причины мне непонятны, хотя они предлагают вместо него какой-то свой собственный аналог CUDA у NVidia. Последним, к слову, наличие CUDA никак не помешало поддерживать тот же OpenCL. Такое отношение к OpenCL не может радовать, конечно же. Тот набор инструментов, что используется ниже - он поддерживается энтузиастами (за что им огромное спасибо). 

Скачать SDK можно, например, тут. После установки, установим переменную окружения OCL_ROOT и проверим, что она правильно установлена: 


Далее все делаем так же, как мы делали раньше и в тех же местах.



Тут настройка среды закончена и для запуска примера осталось только чуть подредактировать код, вместо заголовка cl.hpp мы возьмем cl2.hpp и добавим определение CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY перед заголовочным файлом для совместимости со старым кодом:


После этого можно запустить пример и убедиться, что он работает:


Заодно проверим как работает перемножение матриц в наивной реализации для CPU и GPU:


Запускалалось на Ryzen 7 7730U with Vega 8 iGPU

Заодно сравним производительность ноутбучного процессора и встроенной видеокарты с результатами настольного ПК: