Несоответствие производительности между графическим процессором и процессором при умножении матриц: Eigen против ViennaCL

Я столкнулся с проблемой производительности при выполнении операций умножения матриц с использованием библиотек 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$ 

Теоретически оба расчета можно полностью оптимизировать, поскольку вы не используете результаты. Вы компилируете с включенной оптимизацией, верно?

Ted Lyngmo 06.05.2024 17:58

Да, оптимизация включена. Я использую premake для управления конфигурациями сборки. Я добавлю в пост премейковый скрипт и структуру проекта.

Francisco Ossian 06.05.2024 18:18

Хорошо, есть ли какая-то разница, если вы сохраните все 100 результатов каждого измерения в файл, чтобы не было риска что-либо оптимизировать?

Ted Lyngmo 06.05.2024 18:33

Хорошо, отключение оптимизации действительно привело к ужасной производительности Eigen по сравнению с viennaCL после определенного размера матриц. Я неправильно понял ваш предыдущий комментарий, думая, что он должен был включить оптимизацию LOL. Чтобы не оставить вопрос без ответа, я опубликую здесь ответ вместе с тестовыми данными. Спасибо.

Francisco Ossian 06.05.2024 19:28

Вы правильно поняли с первого раза. Мой второй комментарий был призван предоставить способ предотвратить получение ошибочного результата оптимизации при сравнительном тестировании.

Ted Lyngmo 06.05.2024 21:14

Извините, я изначально не понял. Теперь я удалил предыдущий ответ и обновлю вопрос сценарием согласно вашему предложению. Напишу, что Эйген продолжал решать быстрее, чем ViennaCL

Francisco Ossian 07.05.2024 04:40

Есть ли в вашей системе драйверы OpenCL? Работа на графическом процессоре без OpenCL означает, что графический процессор будет ужасно медленным. Кроме того, если поискать в Google некоторые характеристики iGPU, некоторые устройства Intel имеют эквивалент 8 ядер с частотой 300 МГц. Любой устаревший процессор справится с этим.

Matt 07.05.2024 05:23

Я понимаю, что слабость графического процессора может быть здесь большой проблемой. Итак, я просто пытаюсь во всем разобраться и убедиться, прежде чем двигаться вперед. Плюс, поскольку это интегрированный графический процессор, он, вероятно, уже многое делает за кулисами. Я все еще учусь, так что даже если это глупость, мне просто интересно убедиться.

Francisco Ossian 07.05.2024 08:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
117
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я верю, что понял это!

Я неправильно настроил 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

Другие вопросы по теме