Я столкнулся с проблемой производительности при выполнении операций умножения матриц с использованием библиотек Eigen и ViennaCL на C++. Я сравниваю производительность выполнения этих операций на встроенном графическом процессоре моей системы и на центральном процессоре.
Моя система оснащена встроенным графическим процессором Intel, и я запускаю код на процессоре Intel Core i5 восьмого поколения. К моему удивлению, я обнаружил, что умножение матриц занимает около 200 секунд при выполнении на графическом процессоре с использованием ViennaCL, тогда как при выполнении на процессоре с использованием Eigen оно занимает всего около 20 секунд.
Я озадачен этим несоответствием производительности и хотел бы лучше понять причину этого. Может ли встроенный графический процессор действительно иметь такую низкую производительность по сравнению с процессором при операциях умножения матриц?
я использую премейк
workspace "Project"
configurations { "Debug", "Release" }
location "build"
project "Project"
kind "ConsoleApp"
language "C++"
targetdir "build/bin/%{cfg.buildcfg}"
objdir "build/obj/%{cfg.buildcfg}"
files { "src/*.cpp", "include/*.hpp" }
includedirs { "include", "vendor/*" }
filter "configurations:Debug"
symbols "On"
optimize "On"
filter "configurations:Release"
symbols "Off"
optimize "On"
filter {}
@:~/repos/cpp-projct$ tree -L 1
.
├── build
├── cr.sh
├── include
├── premake.lua
├── src
└── vendor (eigen and viennaCL here, just $ wget and $ tar)
Редактировать:
Код отредактирован с учетом предложений, выводится в файл.txt; Я не проверял предложение использовать целые числа или другой тип, потому что не мог просто поменять тип.
#include <Eigen/Dense>
#include <chrono>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <viennacl/matrix.hpp>
int main() {
const int size = 1500; // Size of the matrices
std::string fileName1 = "./list1.txt";
std::string fileName2 = "./list2.txt";
// Creating two large matrices using ViennaCL
viennacl::matrix<float> matrix1_viennacl(size, size);
viennacl::matrix<float> matrix2_viennacl(size, size);
// Creating two large matrices using Eigen
Eigen::MatrixXf matrix1_eigen(size, size);
Eigen::MatrixXf matrix2_eigen(size, size);
// Initializing the matrices with random values
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
matrix1_viennacl(i, j) = rand() / static_cast<float>(RAND_MAX);
matrix2_viennacl(i, j) = rand() / static_cast<float>(RAND_MAX);
}
}
// Initializing the matrices with the same random values
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
matrix1_eigen(i, j) = matrix1_viennacl(i, j);
matrix2_eigen(i, j) = matrix2_viennacl(i, j);
}
}
//==============================================================
// Performing computation with the matrices using ViennaCL and
// measuring the execution time
auto start_viennacl = std::chrono::steady_clock::now();
viennacl::matrix<float> result_viennacl =
viennacl::linalg::prod(matrix1_viennacl, matrix2_viennacl);
auto end_viennacl = std::chrono::steady_clock::now();
std::chrono::duration<double> time_viennacl = end_viennacl - start_viennacl;
std::ofstream file1(fileName1);
if (!file1.is_open()) {
std::cout << "Err file 1" << std::endl;
return 1;
}
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
file1 << result_viennacl(i, j) << std::endl;
}
}
// Printing the execution time with ViennaCL
std::cout << "Execution time with ViennaCL: " << time_viennacl.count()
<< " seconds" << std::endl;
//=================================================================
// Performing computation with the matrices using Eigen and
// measuring the execution time
auto start_eigen = std::chrono::steady_clock::now();
Eigen::MatrixXf result_eigen = matrix1_eigen * matrix2_eigen;
auto end_eigen = std::chrono::steady_clock::now();
std::chrono::duration<double> time_eigen = end_eigen - start_eigen;
std::ofstream file2(fileName2);
if (!file2.is_open()) {
std::cout << "Err file 2" << std::endl;
return 1;
}
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
file2 << result_eigen(i, j) << std::endl;
}
}
// Printing the execution time with Eigen
std::cout << "Execution time with Eigen: " << time_eigen.count() << " seconds"
<< std::endl;
return 0;
}
@:~/repos/cpp-template$ bash cr.sh
Execution time with ViennaCL: 9.81101 seconds
Execution time with Eigen: 0.68594 seconds
@:~/repos/cpp-template$
Да, оптимизация включена. Я использую premake для управления конфигурациями сборки. Я добавлю в пост премейковый скрипт и структуру проекта.
Хорошо, есть ли какая-то разница, если вы сохраните все 100 результатов каждого измерения в файл, чтобы не было риска что-либо оптимизировать?
Хорошо, отключение оптимизации действительно привело к ужасной производительности Eigen по сравнению с viennaCL после определенного размера матриц. Я неправильно понял ваш предыдущий комментарий, думая, что он должен был включить оптимизацию LOL. Чтобы не оставить вопрос без ответа, я опубликую здесь ответ вместе с тестовыми данными. Спасибо.
Вы правильно поняли с первого раза. Мой второй комментарий был призван предоставить способ предотвратить получение ошибочного результата оптимизации при сравнительном тестировании.
Извините, я изначально не понял. Теперь я удалил предыдущий ответ и обновлю вопрос сценарием согласно вашему предложению. Напишу, что Эйген продолжал решать быстрее, чем ViennaCL
Есть ли в вашей системе драйверы OpenCL? Работа на графическом процессоре без OpenCL означает, что графический процессор будет ужасно медленным. Кроме того, если поискать в Google некоторые характеристики iGPU, некоторые устройства Intel имеют эквивалент 8 ядер с частотой 300 МГц. Любой устаревший процессор справится с этим.
Я понимаю, что слабость графического процессора может быть здесь большой проблемой. Итак, я просто пытаюсь во всем разобраться и убедиться, прежде чем двигаться вперед. Плюс, поскольку это интегрированный графический процессор, он, вероятно, уже многое делает за кулисами. Я все еще учусь, так что даже если это глупость, мне просто интересно убедиться.





Я верю, что понял это!
Я неправильно настроил ViennaCL с OpenCL. По умолчанию ViennaCL больше не использует OpenCL, поэтому мне нужно явно включить его.
workspace "MeuProjeto"
configurations { "Debug", "Release" }
location "build"
project "MeuProjeto"
kind "ConsoleApp"
language "C++"
targetdir "build/bin/%{cfg.buildcfg}"
objdir "build/obj/%{cfg.buildcfg}"
files { "src/*.cpp", "include/*.hpp" }
includedirs { "include", "vendor/eigen", "vendor/viennaCL", "vendor/viennaCL/CL" }
defines { "VIENNACL_WITH_OPENCL" }
filter "configurations:Debug"
symbols "On"
optimize "Off"
filter "configurations:Release"
symbols "Off"
optimize "On"
filter {}
links { "OpenCL" }
В случае каких-либо сомнений команда компиляции с g++ будет выглядеть так:
g++ -I./vendor/eigen -I./vendor/viennaCL -I.vendor/viennaCL/CL -DVIENNACL_WITH_OPENCL -O3 src/main.cpp -o my_program -lOpenCL
Кроме того, я установил некоторые зависимости OpenCL, например Intel SDK. Однако неясно, были ли они необходимы. Например, intel-opencl-icd.
И я по ошибке удалил цикл, который повторял вычисления несколько раз, что изначально снижало производительность графического процессора. Однако его восстановление показало явное преимущество использования графического процессора.
calc_time();
viennacl::matrix<float> result_viennacl =
viennacl::linalg::prod(matrix1_viennacl, matrix2_viennacl);
loop(100)
viennacl::matrix<float> result_viennacl =
viennacl::linalg::prod(matrix1_viennacl, matrix2_viennacl);
calc_time();
calc_time();
loop(100) Eigen::MatrixXf result_eigen = matrix1_eigen * matrix2_eigen;
calc_time();
Результат:
@:~/repos/cpp-proj$ bash cr.sh (using release / optimize = true)
Execution time with ViennaCL: 14.5453 seconds
Execution time with Eigen: 99.242 seconds
Теоретически оба расчета можно полностью оптимизировать, поскольку вы не используете результаты. Вы компилируете с включенной оптимизацией, верно?