Умножение матриц в SYCL с использованием 2D std::vector

Я новичок в SYCL и C++. Это мое ядро ​​для простого умножения матриц с использованием 2D std::vector.


void MatrixMulParallel(queue& q, 
    const std::vector<std::vector<double>>& a_host,
    const std::vector<std::vector<double>>& b_host,
    std::vector<std::vector<double>>& c_gpu) {
    /*
        To Multiply: C[M][P] = A[M][N] * B[N][P]
    */
    PROFILE_FUNCTION();
    try {
        size_t M = a_host.size();
        size_t N = a_host[0].size();
        size_t P = b_host[0].size();
        // Create device buffers for A, B, C
        buffer a(a_host.data(), range<2>{M, N});
        buffer b(b_host.data(), range<2>{N, P});
        buffer c(c_gpu.data(), range<2>{M, P});

        PROFILE_SCOPE("Starting Multiply on GPU");
        std::cout << "GPU::Multiplying A and B into C.\n";
        auto e = q.submit([&](handler& h) {

            auto A = a.get_access<access::mode::read>(h);
            auto B = b.get_access<access::mode::read>(h);
            auto C = c.get_access<access::mode::write>(h);

            h.parallel_for(range<2>{M, P}, [=](id<2> index) {
                // index[0] allows accessing ROW index, index[1] is column index
                
                int row = index[0];
                int col = index[1];
                auto sum = 0.0;
                for (int i = 0; i < N; i++)
                    sum += A[row][i] * B[i][col]; // Error #1
                C[index] = sum; // Error #2
                });
            });
        e.wait();
    }
    catch (sycl::exception const& e) {
        std::cout << "An exception is caught while multiplying matrices.\n";
        terminate();
    }
}

Я получаю две ошибки, указанные вдоль строк:

  1. Ошибка №1: invalid operands to binary expression ('const std::vector<double, std::allocator<double>>' and 'const std::vector<double, std::allocator<double>>')
  2. Ошибка №2: no viable overloaded '='

Я пробовал искать ошибки, похожие на invalid operands for binary expression (...), но ни один из них, похоже, не помогает отладить мой конкретный случай. Может быть, потому что это не для новичков.

Из того, что я понял до сих пор, a_host.data() показывает тип возврата std::vector<double> (не должно быть std::vector< std::vector<double> >?).

Я пробовал использовать std::array со статически известными размерами, и это работает.

Как я могу сделать это с помощью 2D std::vector?

Любая помощь будет оценена.

2 ответа

Решение

2D std::vector<std::vector<T>> не имеет элементов, хранящихся в памяти непрерывно.

Лучшим способом было бы объявить std::vector<T> с размерами M*N, то есть линейными массивами, и работать с ними как с смежными блоками.

Поскольку вектор назначения C, должен быть 2D, создайте ядро, которое индексирует как строки, так и столбцы. SYCL index фактически заполняется линейно доступными блоками памяти.

Вот что я сделал, чтобы он работал, используя std::vector:

template <typename T>
void MatrixMulParallelNaive(queue& q, 
    const std::vector<T>& a_host,
    const std::vector<T>& b_host,
    std::vector<T>& c_gpu) {
    /*
        To Multiply: C[M][P] = A[M][N] * B[N][P]
    */
    PROFILE_FUNCTION();
    try {
        
        buffer<double, 1> a(a_host.data(), range<1>{a_host.size()}); // 1D
        buffer<double, 1> b(b_host.data(), range<1>{b_host.size()}); // 1D
        buffer<double, 2> c(c_gpu.data(), range<2>{M, P}); // Create 2D buffer
        PROFILE_SCOPE("Starting Multiply on GPU");
        std::cout << "GPU::Multiplying A and B into C.\n";
        auto e = q.submit([&](handler& h) {

            auto A = a.get_access<access::mode::read>(h);
            auto B = b.get_access<access::mode::read>(h);
            auto C = c.get_access<access::mode::write>(h);
            
            h.parallel_for(range<2>{M, P}, [=](id<2> index) {
                // Threading index that iterates over C.
                int row = index[0];
                int col = index[1];
                auto sum = 0.0;
                // Compute result of ONE element of C
                for (int i = 0; i < N; i++)
                    sum += A[row * M + i] * B[i * N + col];
                C[index] = sum;
                });
            });
        e.wait();
    }
    catch (sycl::exception const& e) {
        std::cout << "An exception is caught while multiplying matrices.\n";
        terminate();
    }
}


В более общем плане избегайте некомпактной структуры данных при выполнении HPC. Он менее удобен для иерархии памяти, чем непрерывные элементы массива, а инициализация сложна. Вместо этого используйте вещи, похожие на md_span и md_array (в основном массивы Fortran на стероидах:-)).

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