跳到主要内容

OpenBLAS RVV

OpenBLAS 简介

OpenBLAS 是一个开源的 BLAS(Basic Linear Algebra Subprograms)和部分 LAPACK 实现,提供矩阵乘法、向量运算、线性方程求解、矩阵分解等基础数值计算能力。OpenBLAS 被广泛用于 NumPy、SciPy、Eigen、OpenCV、机器学习推理框架以及机器人算法中的线性代数加速场景。

OpenBLAS 的性能与目标 CPU 架构、缓存参数、线程模型和核心算子实现密切相关。对于 GEMM(General Matrix Multiply,通用矩阵乘法)这类计算密集型算子,是否针对具体处理器实现向量化微内核通常会直接影响吞吐。K3 平台搭载的 SpacemiT X100 处理器支持 RISC-V Vector Extension(RVV 1.0),因此在大规模矩阵乘法、矩阵分解等场景中,可以通过 RVV 优化库进一步释放硬件性能。

RVV 加速

K3 系统中可对比两套 OpenBLAS:

对比项系统 OpenBLAS(libopenblas-pthread-devSpacemiT OpenBLAS(openblas-spacemit
版本OpenBLAS 0.3.32OpenBLAS 0.3.32
安装方式sudo apt install libopenblas-pthread-devsudo apt install openblas-spacemit
头文件路径/usr/include/riscv64-linux-gnu/openblas-pthread/opt/openblas-spacemit/include
库文件路径/usr/lib/riscv64-linux-gnu/openblas-pthread/opt/openblas-spacemit/lib
目标核心genericx100
优化方向通用 riscv64 OpenBLAS pthread 实现面向 SpacemiT X100 的 RVV 优化实现
典型用途兼容系统默认 BLAS/LAPACK 依赖在 K3 上进行高性能矩阵计算和算法加速

本文后续主要通过相同的 BLAS 接口(如 cblas_sgemm / cblas_dgemm)对比 libopenblas-pthread-devopenblas-spacemit 的性能差异。测试时仅切换头文件与链接库路径,并通过 OPENBLAS_NUM_THREADStaskset 等方式控制线程数和 CPU 亲和性,从而观察 RVV 优化库在 K3 平台上的加速效果。

使用示例

软硬件环境

  • SpacemiT RISCV64 X100 CPU(2.4Ghz)
  • Bianbu 4.0.1 操作系统
  • 内存:32GB

安装必要依赖

sudo apt update
sudo apt install libopenblas-pthread-dev openblas-spacemit

测试代码

目录结构:

openblas_bench/
├── CMakeLists.txt
└── blas_sgemm_check.cpp

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(openblas_simple_check LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(openblas_sgemm_check blas_sgemm_check.cpp)

find_package(OpenBLAS REQUIRED)

if(_OpenBLAS_ROOT_DIR STREQUAL "/usr/lib")
set(OpenBLAS_LIB /usr/lib/riscv64-linux-gnu/openblas-pthread/libopenblas.so)
else()
set(OpenBLAS_LIB ${OpenBLAS_LIBRARIES})
endif()

message(STATUS "_OpenBLAS_ROOT_DIR: ${_OpenBLAS_ROOT_DIR}")
message(STATUS "OpenBLAS include dir: ${OpenBLAS_INCLUDE_DIRS}")
message(STATUS "OpenBLAS library: ${OpenBLAS_LIB}")

target_include_directories(openblas_sgemm_check PRIVATE ${OpenBLAS_INCLUDE_DIRS})
target_link_libraries(openblas_sgemm_check PRIVATE ${OpenBLAS_LIB})

blas_sgemm_check.cpp

// Test the installed BLAS ABI library by calling cblas_sgemm.
// This validates that the target BLAS library can be linked and used.

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

#include <cblas.h>

namespace {

float value_for(int row, int col, int rows) {
return static_cast<float>(((row + 1) * 17 + (col + 1) * 31 + rows) % 101) / 101.0f;
}

void fill_col_major(std::vector<float>& matrix, int rows, int cols) {
for (int col = 0; col < cols; ++col) {
for (int row = 0; row < rows; ++row) {
matrix[row + col * rows] = value_for(row, col, rows);
}
}
}

std::vector<float> reference_gemm(const std::vector<float>& a, const std::vector<float>& b,
int m, int n, int k, float alpha, float beta) {
std::vector<float> c(static_cast<size_t>(m) * n, 1.0f);
for (int col = 0; col < n; ++col) {
for (int row = 0; row < m; ++row) {
float acc = 0.0f;
for (int inner = 0; inner < k; ++inner) {
acc += a[row + inner * m] * b[inner + col * k];
}
c[row + col * m] = alpha * acc + beta * c[row + col * m];
}
}
return c;
}

double max_abs_error(const std::vector<float>& x, const std::vector<float>& y) {
double err = 0.0;
for (size_t i = 0; i < x.size(); ++i) {
err = std::max(err, std::abs(static_cast<double>(x[i]) - static_cast<double>(y[i])));
}
return err;
}

double checksum(const std::vector<float>& x) {
double sum = 0.0;
for (float v : x) sum += v;
return sum;
}

} // namespace

int main(int argc, char** argv) {
int m = 512;
int n = 512;
int k = 512;
int repeats = 20;

if (argc > 1) m = std::atoi(argv[1]);
if (argc > 2) n = std::atoi(argv[2]);
if (argc > 3) k = std::atoi(argv[3]);
if (argc > 4) repeats = std::atoi(argv[4]);

if (m <= 0 || n <= 0 || k <= 0 || repeats <= 0) {
std::cerr << "usage: " << argv[0] << " [m n k repeats]\n";
return 2;
}

std::vector<float> a(static_cast<size_t>(m) * k);
std::vector<float> b(static_cast<size_t>(k) * n);
std::vector<float> c(static_cast<size_t>(m) * n, 1.0f);

fill_col_major(a, m, k);
fill_col_major(b, k, n);

float alpha = 1.25f;
float beta = 0.5f;
int lda = m;
int ldb = k;
int ldc = m;

const auto expected = reference_gemm(a, b, m, n, k, alpha, beta);

cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, a.data(), lda, b.data(), ldb,
beta, c.data(), ldc);

const double err = max_abs_error(c, expected);
const double tolerance = 1e-3 * static_cast<double>(k);

std::cout << std::fixed << std::setprecision(6);
std::cout << "BLAS symbol: cblas_sgemm\n";
std::cout << "Problem: m=" << m << ", n=" << n << ", k=" << k << ", repeats=" << repeats << '\n';
std::cout << "Correctness max_abs_error=" << err << ", tolerance=" << tolerance << '\n';
std::cout << "Checksum=" << checksum(c) << '\n';

if (err > tolerance) {
std::cerr << "BLAS cblas_sgemm result: FAIL - result mismatch\n";
return 1;
}

std::fill(c.begin(), c.end(), 1.0f);
const auto start = std::chrono::steady_clock::now();
for (int r = 0; r < repeats; ++r) {
cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, a.data(), lda, b.data(), ldb,
beta, c.data(), ldc);
}
const auto end = std::chrono::steady_clock::now();
const double seconds = std::chrono::duration<double>(end - start).count();
const double avg_seconds = seconds / repeats;
const double gflops = (2.0 * m * n * k * repeats) / seconds / 1.0e9;

std::cout << "Time seconds=" << seconds << '\n';
std::cout << "Average time per SGEMM seconds=" << avg_seconds << '\n';
std::cout << "Approx GFLOP/s=" << gflops << '\n';
std::cout << "BLAS cblas_sgemm result: PASS - linked OpenBLAS and computed correctly.\n";
return 0;
}

编译

# 使用libopenblas-pthread-dev
cmake -S . -B build_system -DCMAKE_BUILD_TYPE=Release
cmake --build build_system

# 使用 openblas-spacemit
cmake -S . -B build_spacemit -DCMAKE_BUILD_TYPE=Release -DOpenBLAS_DIR=/opt/openblas-spacemit/lib/cmake/openblas
cmake --build build_spacemit

终端输出:

测试对比

单核测试

taskset -c 0 ./build_system/openblas_sgemm_check # 系统库
taskset -c 0 ./build_spacemit/openblas_sgemm_check # openblas-spacemit

输出:

可以看出,RVV加速后的sgemm算子性能有所提升(10.18ms -> 8.05ms)

八核测试

taskset -c 0-7 ./build_system/openblas_sgemm_check
taskset -c 0-7 ./build_spacemit/openblas_sgemm_check

输出:

更多性能测试数据

  • 使用 taskset -c 限制核数
  • 单次测试的计时策略是跑 50 次取平均值,预热10次。本测试重复10次单次测试,取其指标的平均值并计算标准差
  • mean 表示 10 次测试的均值,sd 为标准差

测试结果简要说明

本次测评聚焦系统默认 libopenblas-pthread-dev 与面向 SpacemiT X100 处理器优化的 openblas-spacemit,在单核与八核条件下量化 RVV 加速收益,测试均采用统一的计时与重复策略以保证可比性。

主要结论如下:

  • BLAS Level-3(矩阵-矩阵运算)openblas-spacemit 显著提升核心算子性能。单核 sgemm 吞吐由 26.48 GFLOPS 提升至 33.47 GFLOPS(+26.4%),八核 sgemm 吞吐由 150.52 GFLOPS 提升至 183.40 GFLOPS(+22.5%)。ssymmstrmm 等算子普遍获得 15%~23% 的加速;双精度场景(如 dgemm)亦有 5%~10% 的增益。
  • BLAS Level-1(向量运算):数据搬运与缩放类算子(如 scopydcopysscaldscal)性能提升 20%~40%;归约类算子(如 sdotddotnrm2)改进有限;部分算子(如 sasumdasum)仍略逊于系统库。
  • BLAS Level-2(矩阵-向量运算):整体优势不明显,sgemvdgemv 等算子与系统库相比低约 20%~30%,主要受制于内存带宽及较低的运算密度。

综合来看,openblas-spacemit 在计算密集型矩阵运算中优势突出,适用于机器人感知、SLAM、机器学习推理及科学计算等高度依赖 GEMM 的工作负载;而对内存访问主导的向量与矩阵-向量算子需结合具体场景评估其收益。

单核测试

modulefunctionapiexpressiondtypeinput_sizeoutput_sizelibopenblas-pthread-dev avg_ms mean±sdopenblas-spacemit avg_ms mean±sdlibopenblas-pthread-dev avg_gflops mean±sdopenblas-spacemit avg_gflops mean±sdspeedup mean±sdimprovement mean±sd
blas1sasumcblas_sasumsum(abs(x))float32262144x11x10.0460 ± 0.00060.0516 ± 0.00035.7036 ± 0.07455.0760 ± 0.03370.89x ± 0.01x-10.96% ± 1.22%
blas1dasumcblas_dasumsum(abs(x))float64262144x11x10.0934 ± 0.00220.1066 ± 0.00592.8081 ± 0.06252.4654 ± 0.12870.88x ± 0.04x-12.18% ± 4.22%
blas1saxpycblas_saxpyz = alpha*x + zfloat32262144x1262144x10.0951 ± 0.00390.0978 ± 0.00345.5200 ± 0.20505.3671 ± 0.17740.97x ± 0.05x-2.59% ± 5.36%
blas1daxpycblas_daxpyz = alpha*x + zfloat64262144x1262144x10.4546 ± 0.02080.4214 ± 0.01701.1555 ± 0.05231.2461 ± 0.04931.08x ± 0.07x8.09% ± 7.46%
blas1scopycblas_scopyz = xfloat32262144x1262144x10.0734 ± 0.00030.0593 ± 0.00033.5713 ± 0.01404.4216 ± 0.02251.24x ± 0.01x23.80% ± 0.70%
blas1dcopycblas_dcopyz = xfloat64262144x1262144x10.1481 ± 0.00120.1239 ± 0.00731.7699 ± 0.01452.1222 ± 0.11671.20x ± 0.06x19.88% ± 6.03%
blas1sdotcblas_sdotdot(x, y)float32262144x11x10.0771 ± 0.00220.0824 ± 0.00736.8064 ± 0.19346.4044 ± 0.53760.94x ± 0.08x-5.88% ± 7.95%
blas1ddotcblas_ddotdot(x, y)float64262144x11x10.3765 ± 0.01030.3628 ± 0.00801.3936 ± 0.03871.4458 ± 0.03181.04x ± 0.03x3.80% ± 3.00%
blas1snrm2cblas_snrm2sqrt(dot(x, x))float32262144x11x10.6717 ± 0.00140.6233 ± 0.00120.7805 ± 0.00160.8411 ± 0.00161.08x ± 0.00x7.77% ± 0.32%
blas1dnrm2cblas_dnrm2sqrt(dot(x, x))float64262144x11x12.1654 ± 0.00102.0484 ± 0.00340.2421 ± 0.00010.2560 ± 0.00041.06x ± 0.00x5.71% ± 0.15%
blas1sscalcblas_sscalz = alpha*zfloat32262144x1262144x10.0757 ± 0.00030.0588 ± 0.00043.4646 ± 0.01164.4540 ± 0.02551.29x ± 0.01x28.61% ± 0.97%
blas1dscalcblas_dscalz = alpha*zfloat64262144x1262144x10.1662 ± 0.00300.1273 ± 0.01101.5775 ± 0.02792.0720 ± 0.17441.31x ± 0.11x31.39% ± 11.17%
blas2sgemvcblas_sgemvz = alphaAx + beta*zfloat32512x512512x10.0788 ± 0.00020.1139 ± 0.00056.6556 ± 0.01804.6013 ± 0.01980.69x ± 0.00x-30.86% ± 0.29%
blas2dgemvcblas_dgemvz = alphaAx + beta*zfloat64512x512512x10.1611 ± 0.00770.2247 ± 0.01003.2618 ± 0.15452.3368 ± 0.09440.72x ± 0.05x-28.18% ± 4.99%
blas2ssymvcblas_ssymvz = alpha*Sym(A)x + betazfloat32512x512512x10.0694 ± 0.00060.0817 ± 0.00027.5572 ± 0.06346.4155 ± 0.01580.85x ± 0.01x-15.11% ± 0.72%
blas2dsymvcblas_dsymvz = alpha*Sym(A)x + betazfloat64512x512512x10.1168 ± 0.00170.1331 ± 0.00184.4887 ± 0.06403.9409 ± 0.05040.88x ± 0.01x-12.18% ± 1.34%
blas2stbmvcblas_stbmvz = TriBand(A)*zfloat32512x512512x10.0109 ± 0.00010.0138 ± 0.00030.2338 ± 0.00260.1857 ± 0.00430.79x ± 0.02x-20.67% ± 2.02%
blas2dtbmvcblas_dtbmvz = TriBand(A)*zfloat64512x512512x10.0112 ± 0.00000.0134 ± 0.00060.2295 ± 0.00000.1914 ± 0.00860.84x ± 0.04x-16.21% ± 3.71%
blas2stpmvcblas_stpmvz = PackedTri(A)*zfloat32512x512512x10.0501 ± 0.00030.0499 ± 0.00215.2366 ± 0.03545.2574 ± 0.21151.00x ± 0.04x0.43% ± 4.11%
blas2dtpmvcblas_dtpmvz = PackedTri(A)*zfloat64512x512512x10.0946 ± 0.00030.0889 ± 0.00232.7708 ± 0.01022.9493 ± 0.07301.06x ± 0.02x6.44% ± 2.37%
blas2strmvcblas_strmvz = Tri(A)*zfloat32512x512512x10.0671 ± 0.00020.0732 ± 0.00283.9054 ± 0.01113.5857 ± 0.13270.92x ± 0.03x-8.21% ± 3.46%
blas2dtrmvcblas_dtrmvz = Tri(A)*zfloat64512x512512x10.1083 ± 0.00120.1266 ± 0.00302.4214 ± 0.02652.0718 ± 0.04810.86x ± 0.02x-14.43% ± 2.07%
blas3sgemmcblas_sgemmC = alphaAB + beta*Cfloat32512x512512x51210.1364 ± 0.06598.0202 ± 0.018826.4833 ± 0.171633.4701 ± 0.07861.26x ± 0.01x26.39% ± 0.79%
blas3dgemmcblas_dgemmC = alphaAB + beta*Cfloat64512x512512x51223.3091 ± 0.079721.2694 ± 0.050511.5165 ± 0.039412.6208 ± 0.03001.10x ± 0.00x9.59% ± 0.41%
blas3ssymmcblas_ssymmC = alpha*Sym(A)B + betaCfloat32512x512512x51210.1916 ± 0.03558.2806 ± 0.027026.3392 ± 0.091832.4179 ± 0.10551.23x ± 0.01x23.08% ± 0.57%
blas3dsymmcblas_dsymmC = alpha*Sym(A)B + betaCfloat64512x512512x51223.0906 ± 0.082122.5162 ± 0.058511.6255 ± 0.041411.9220 ± 0.03101.03x ± 0.00x2.55% ± 0.48%
blas3strmmcblas_strmmC = alpha*Tri(A)*Cfloat32512x512512x5125.2380 ± 0.01764.5287 ± 0.017225.6241 ± 0.085729.6377 ± 0.11261.16x ± 0.01x15.66% ± 0.51%
blas3dtrmmcblas_dtrmmC = alpha*Tri(A)*Cfloat64512x512512x51211.3021 ± 0.100410.1598 ± 0.056711.8763 ± 0.105213.2111 ± 0.07361.11x ± 0.01x11.25% ± 0.97%

八核测试

modulefunctionapiexpressiondtypeinput_sizeoutput_sizelibopenblas-pthread-dev avg_ms mean±sdopenblas-spacemit avg_ms mean±sdlibopenblas-pthread-dev avg_gflops mean±sdopenblas-spacemit avg_gflops mean±sdspeedup mean±sdimprovement mean±sd
blas1sasumcblas_sasumsum(abs(x))float32262144x11x10.0653 ± 0.00630.0637 ± 0.00934.0463 ± 0.36534.1903 ± 0.58121.04x ± 0.18x4.42% ± 17.54%
blas1dasumcblas_dasumsum(abs(x))float64262144x11x10.1100 ± 0.01490.1533 ± 0.00972.4195 ± 0.29591.7163 ± 0.10500.72x ± 0.09x-28.18% ± 8.91%
blas1saxpycblas_saxpyz = alpha*x + zfloat32262144x1262144x10.0369 ± 0.00530.0350 ± 0.006114.4811 ± 2.232115.4264 ± 2.85041.09x ± 0.28x9.37% ± 28.30%
blas1daxpycblas_daxpyz = alpha*x + zfloat64262144x1262144x10.0605 ± 0.00790.0616 ± 0.00728.7922 ± 1.05838.6122 ± 0.98701.00x ± 0.19x-0.27% ± 19.23%
blas1scopycblas_scopyz = xfloat32262144x1262144x10.0733 ± 0.00030.0592 ± 0.00043.5765 ± 0.01474.4264 ± 0.02801.24x ± 0.01x23.78% ± 1.09%
blas1dcopycblas_dcopyz = xfloat64262144x1262144x10.1706 ± 0.06270.1236 ± 0.00601.6419 ± 0.31942.1261 ± 0.10031.39x ± 0.55x39.07% ± 55.23%
blas1sdotcblas_sdotdot(x, y)float32262144x11x10.1068 ± 0.03600.0952 ± 0.00555.2162 ± 1.04225.5213 ± 0.31141.12x ± 0.35x11.96% ± 34.66%
blas1ddotcblas_ddotdot(x, y)float64262144x11x10.2790 ± 0.01940.3266 ± 0.09281.8873 ± 0.13181.6914 ± 0.34330.90x ± 0.21x-9.77% ± 20.61%
blas1snrm2cblas_snrm2sqrt(dot(x, x))float32262144x11x10.6748 ± 0.00760.6434 ± 0.06550.7771 ± 0.00860.8209 ± 0.06641.06x ± 0.08x5.58% ± 7.69%
blas1dnrm2cblas_dnrm2sqrt(dot(x, x))float64262144x11x12.1886 ± 0.05272.0510 ± 0.00560.2397 ± 0.00550.2556 ± 0.00071.07x ± 0.03x6.71% ± 2.61%
blas1sscalcblas_sscalz = alpha*zfloat32262144x1262144x10.0754 ± 0.00010.0590 ± 0.00053.4741 ± 0.00384.4415 ± 0.03611.28x ± 0.01x27.83% ± 1.18%
blas1dscalcblas_dscalz = alpha*zfloat64262144x1262144x10.1682 ± 0.00570.1188 ± 0.00131.5604 ± 0.05082.2066 ± 0.02331.42x ± 0.05x41.55% ± 5.00%
blas2sgemvcblas_sgemvz = alphaAx + beta*zfloat32512x512512x10.0789 ± 0.00020.1140 ± 0.00036.6473 ± 0.01374.5987 ± 0.01410.69x ± 0.00x-30.81% ± 0.18%
blas2dgemvcblas_dgemvz = alphaAx + beta*zfloat64512x512512x10.1570 ± 0.00640.2266 ± 0.01443.3446 ± 0.12962.3206 ± 0.12790.70x ± 0.05x-30.49% ± 5.15%
blas2ssymvcblas_ssymvz = alpha*Sym(A)x + betazfloat32512x512512x10.0196 ± 0.00040.0242 ± 0.000726.8242 ± 0.492421.6891 ± 0.59640.81x ± 0.03x-19.09% ± 2.75%
blas2dsymvcblas_dsymvz = alpha*Sym(A)x + betazfloat64512x512512x10.0310 ± 0.00390.0346 ± 0.000717.1231 ± 1.644515.1380 ± 0.30150.89x ± 0.12x-10.52% ± 11.79%
blas2stbmvcblas_stbmvz = TriBand(A)*zfloat32512x512512x10.0056 ± 0.00030.0083 ± 0.00050.4546 ± 0.02050.3088 ± 0.01760.68x ± 0.05x-31.88% ± 4.96%
blas2dtbmvcblas_dtbmvz = TriBand(A)*zfloat64512x512512x10.0075 ± 0.00030.0079 ± 0.00060.3409 ± 0.01150.3265 ± 0.02250.96x ± 0.07x-4.26% ± 7.18%
blas2stpmvcblas_stpmvz = PackedTri(A)*zfloat32512x512512x10.0133 ± 0.00320.0124 ± 0.000320.4058 ± 3.077921.2088 ± 0.42541.07x ± 0.25x7.29% ± 24.85%
blas2dtpmvcblas_dtpmvz = PackedTri(A)*zfloat64512x512512x10.0227 ± 0.00300.0211 ± 0.000811.6867 ± 1.200312.4503 ± 0.46391.07x ± 0.11x7.46% ± 11.20%
blas2strmvcblas_strmvz = Tri(A)*zfloat32512x512512x10.0312 ± 0.00350.0337 ± 0.00158.4915 ± 0.85597.7875 ± 0.36440.93x ± 0.11x-7.31% ± 10.84%
blas2dtrmvcblas_dtrmvz = Tri(A)*zfloat64512x512512x10.0465 ± 0.00550.0661 ± 0.00455.7243 ± 0.83693.9807 ± 0.28230.71x ± 0.10x-29.33% ± 9.75%
blas3sgemmcblas_sgemmC = alphaAB + beta*Cfloat32512x512512x5121.7920 ± 0.13111.4639 ± 0.0175150.5191 ± 11.0008183.4007 ± 2.16391.22x ± 0.09x22.46% ± 9.41%
blas3dgemmcblas_dgemmC = alphaAB + beta*Cfloat64512x512512x5123.7447 ± 0.15203.5579 ± 0.155071.7921 ± 2.934775.5774 ± 3.31641.06x ± 0.08x5.50% ± 7.52%
blas3ssymmcblas_ssymmC = alpha*Sym(A)B + betaCfloat32512x512512x5121.7756 ± 0.02351.7004 ± 0.1035151.2034 ± 1.9713158.3544 ± 8.97241.05x ± 0.05x4.71% ± 5.47%
blas3dsymmcblas_dsymmC = alpha*Sym(A)B + betaCfloat64512x512512x5123.7062 ± 0.09673.9785 ± 0.118472.4741 ± 1.882767.5256 ± 2.01290.93x ± 0.04x-6.76% ± 3.91%
blas3strmmcblas_strmmC = alpha*Tri(A)*Cfloat32512x512512x5121.2341 ± 0.06191.4279 ± 0.0935108.9822 ± 4.905194.3393 ± 5.79670.87x ± 0.08x-13.20% ± 7.73%
blas3dtrmmcblas_dtrmmC = alpha*Tri(A)*Cfloat64512x512512x5122.4169 ± 0.18002.3975 ± 0.024455.7853 ± 3.792355.9882 ± 0.56701.01x ± 0.08x0.83% ± 7.66%