perf(math): route ordinary products through MKL and preserve arithmetic
This commit is contained in:
@@ -8,11 +8,49 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <mkl.h>
|
||||
|
||||
#include "fesa/fem/dof_manager.h"
|
||||
#include "math/dense_blas_internal.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
/// @brief Checks the actual private backend integer width before conversion.
|
||||
MKL_INT SparseSize(const std::size_t size) {
|
||||
const auto converted = dense_blas_internal::ToMklSize(size);
|
||||
if (!converted.HasValue()) {
|
||||
throw std::length_error{"Sparse size exceeds the MKL integer range."};
|
||||
}
|
||||
return converted.Value();
|
||||
}
|
||||
|
||||
/// @brief Releases a call-local sparse handle before its borrowed arrays die.
|
||||
class SparseHandle {
|
||||
public:
|
||||
SparseHandle() = default;
|
||||
SparseHandle(const SparseHandle&) = delete;
|
||||
SparseHandle& operator=(const SparseHandle&) = delete;
|
||||
~SparseHandle() {
|
||||
if (handle != nullptr) static_cast<void>(mkl_sparse_destroy(handle));
|
||||
}
|
||||
/// @brief Reports normal release failure before publishing the candidate.
|
||||
sparse_status_t Destroy() {
|
||||
const auto owned = handle;
|
||||
handle = nullptr;
|
||||
return mkl_sparse_destroy(owned);
|
||||
}
|
||||
sparse_matrix_t handle{nullptr};
|
||||
};
|
||||
|
||||
/// @brief Fails explicitly instead of substituting a scalar backend.
|
||||
void CheckSparseStatus(const sparse_status_t status, const char* operation) {
|
||||
if (status != SPARSE_STATUS_SUCCESS) {
|
||||
throw std::runtime_error{std::string{"Sparse backend "} + operation +
|
||||
" failed."};
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Builds a structured sparse-matrix model failure.
|
||||
Status SparseFailure(const std::string& code, const std::string& identity,
|
||||
const std::string& message) {
|
||||
@@ -170,16 +208,43 @@ Vector SparseMatrix::Multiply(const Vector& rhs) const {
|
||||
throw std::invalid_argument{
|
||||
"Sparse matrix-vector multiplication has incompatible dimensions."};
|
||||
}
|
||||
|
||||
Vector result{rows_};
|
||||
for (std::size_t row = 0U; row < rows_; ++row) {
|
||||
double value = 0.0;
|
||||
for (std::size_t position = row_offsets_[row];
|
||||
position < row_offsets_[row + 1U]; ++position) {
|
||||
value += values_[position] * rhs[column_indices_[position]];
|
||||
}
|
||||
result[row] = value;
|
||||
if (!Validate().IsOk()) {
|
||||
throw std::invalid_argument{"Sparse multiplication requires valid CSR."};
|
||||
}
|
||||
const MKL_INT rows = SparseSize(rows_);
|
||||
const MKL_INT columns = SparseSize(columns_);
|
||||
static_cast<void>(SparseSize(values_.size()));
|
||||
Vector result{rows_};
|
||||
if (rows == 0 || columns == 0 || values_.empty()) return result;
|
||||
|
||||
// Each const call owns its converted arrays and handle. Neither copy/move
|
||||
// nor concurrent calls retain pointers into another call's storage.
|
||||
std::vector<MKL_INT> row_offsets;
|
||||
std::vector<MKL_INT> column_indices;
|
||||
row_offsets.reserve(row_offsets_.size());
|
||||
column_indices.reserve(column_indices_.size());
|
||||
for (const auto offset : row_offsets_) {
|
||||
row_offsets.push_back(SparseSize(offset));
|
||||
}
|
||||
for (const auto column : column_indices_) {
|
||||
column_indices.push_back(SparseSize(column));
|
||||
}
|
||||
auto values = values_;
|
||||
SparseHandle sparse;
|
||||
CheckSparseStatus(
|
||||
mkl_sparse_d_create_csr(&sparse.handle, SPARSE_INDEX_BASE_ZERO, rows,
|
||||
columns, row_offsets.data(),
|
||||
row_offsets.data() + 1, column_indices.data(),
|
||||
values.data()),
|
||||
"create CSR");
|
||||
const matrix_descr descriptor{SPARSE_MATRIX_TYPE_GENERAL,
|
||||
SPARSE_FILL_MODE_FULL,
|
||||
SPARSE_DIAG_NON_UNIT};
|
||||
CheckSparseStatus(
|
||||
mkl_sparse_d_mv(SPARSE_OPERATION_NON_TRANSPOSE, 1.0, sparse.handle,
|
||||
descriptor, rhs.Data(), 0.0, result.Data()),
|
||||
"multiply");
|
||||
CheckSparseStatus(sparse.Destroy(), "destroy CSR");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user