1. 简介
mcBLAS库是基于MetaX MXMACA® 运行时的基本线性代数子程序(Basic Linear Algebra Subprograms,BLAS)的实现。它允许用户访问MetaX GPU的计算资源。
要使用mcBLAS API,应用程序必须在GPU内存空间中分配所需的矩阵和向量并使用数据填充,调用所需mcBLAS函数的序列,然后将结果从GPU内存空间上传回主机。mcBLAS API还提供了用于从GPU写入和检索数据的辅助函数。
mcBLAS提供了与传统BLAS功能相似的高性能健壮实现,适用于在GPU上运行。
mcBLAS以C++14和MXMACA编写。它使用MetaX MXMACA运行时以在GPU设备上运行。
mcBLAS API是一个使用沙漏模式(Hourglass Pattern)的精简C99 API。它包含:
[Helper]、[ Level1]、[ Level2]、[ Level3 ]和[BLAS-Like] BLAS函数,具有批处理(batched)版本和跨步批处理(strided-batched)版本。
1.1. 安装mcBLAS
mcBLAS库随MXMACA工具包一起发布。MXMACA工具包的安装,参见《曦云系列通用GPU 快速上手指南》。安装后,确保设置了环境变量 MACA_PATH。
export MACA_PATH=/opt/maca
这里的 /opt/maca 路径为MXMACA工具包的默认安装路径。如果安装时选择了其它路径,则将 MACA_PATH 设置为该路径。
下面列出了与mcBLAS API相关的文件。
#header location:
${MACA_PATH}/include/mcblas
#lib location:
${MACA_PATH}/lib/libmcblas.so
1.2. Hello mcBLAS
有关示例代码参考,请参见下面的两个示例。它们展示了使用有两种索引样式的mcBLAS库API和C语言编写应用程序(示例1.“使用C和mcBLAS的应用程序:基于1的索引”和示例2.“使用C和mcBLAS的应用程序:基于0的索引”)。
#CMakeLists.txt
cmake_minimum_required(VERSION 3.5.0)
set(PROJECT_NAME example)
project(${PROJECT_NAME} VERSION 0.1.0)
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_STANDARD 14)
INCLUDE_DIRECTORIES(ENV{MACA_PATH}/include/mcr)
INCLUDE_DIRECTORIES(ENV{MACA_PATH}/include/mcblas)
INCLUDE_DIRECTORIES(ENV{MACA_PATH}/include)
LINK_DIRECTORIES(ENV{MACA_PATH}/lib)
file(GLOB EXAMPLE_SRCS1 "example1.cpp")
file(GLOB EXAMPLE_SRCS2 "example2.cpp")
add_executable("example1" ${EXAMPLE_SRCS1})
add_executable("example2" ${EXAMPLE_SRCS2})
target_link_libraries("example1" mcblas mcruntime)
target_link_libraries("example2" mcblas mcruntime)
//示例1.使用C和mcBLAS的应用程序:基于1的索引
//example1.cpp
//-----------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <mc_runtime.h>
#include "mcblas.h"
#define M 6
#define N 5
#define IDX2F(i,j,ld) ((((j)-1)*(ld))+((i)-1))
static __inline__ void modify (mcblasHandle_t handle, float *m, int ldm, int n, int p, int q, float alpha, float beta){
mcblasSscal (handle, n-q+1, &alpha, &m[IDX2F(p,q,ldm)], ldm);
mcblasSscal (handle, ldm-p+1, &beta, &m[IDX2F(p,q,ldm)], 1);
}
int main (void){
int mcStat;
mcblasStatus_t stat;
mcblasHandle_t handle;
int i, j;
float* devPtrA;
float* a = 0;
a = (float *)malloc (M * N * sizeof (*a));
if (!a) {
printf ("host memory allocation failed");
return EXIT_FAILURE;
}
for (j = 1; j <= N; j++) {
for (i = 1; i <= M; i++) {
a[IDX2F(i,j,M)] = (float)((i-1) * N + j);
}
}
mcStat = mcMalloc ((void**)&devPtrA, M*N*sizeof(*a));
if (mcStat != 0) {
printf ("device memory allocation failed");
return EXIT_FAILURE;
}
stat = mcblasCreate(&handle);
if (stat != MCBLAS_STATUS_SUCCESS) {
printf ("mcBLAS initialization failed\n");
return EXIT_FAILURE;
}
stat = mcblasSetMatrix (M, N, sizeof(*a), a, M, devPtrA, M);
if (stat != MCBLAS_STATUS_SUCCESS) {
printf ("data download failed");
mcFree (devPtrA);
mcblasDestroy(handle);
return EXIT_FAILURE;
}
modify (handle, devPtrA, M, N, 2, 3, 16.0f, 12.0f);
stat = mcblasGetMatrix (M, N, sizeof(*a), devPtrA, M, a, M);
if (stat != MCBLAS_STATUS_SUCCESS) {
printf ("data upload failed");
mcFree (devPtrA);
mcblasDestroy(handle);
return EXIT_FAILURE;
}
mcFree (devPtrA);
mcblasDestroy(handle);
for (j = 1; j <= N; j++) {
for (i = 1; i <= M; i++) {
printf ("%7.0f", a[IDX2F(i,j,M)]);
}
printf ("\n");
}
free(a);
return 0;
}
//示例2.使用C和mcBLAS的应用程序:基于0的索引
//example2.cpp
//-----------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <mc_runtime.h>
#include "mcblas.h"
#define M 6
#define N 5
#define IDX2C(i,j,ld) (((j)*(ld))+(i))
static __inline__ void modify (mcblasHandle_t handle, float *m, int ldm, int n, int p, int q, float alpha, float beta){
mcblasSscal (handle, n-q, &alpha, &m[IDX2C(p,q,ldm)], ldm);
mcblasSscal (handle, ldm-p, &beta, &m[IDX2C(p,q,ldm)], 1);
}
int main (void){
int mcStat;
mcblasStatus_t stat;
mcblasHandle_t handle;
int i, j;
float* devPtrA;
float* a = 0;
a = (float *)malloc (M * N * sizeof (*a));
if (!a) {
printf ("host memory allocation failed");
return EXIT_FAILURE;
}
for (j = 0; j < N; j++) {
for (i = 0; i < M; i++) {
a[IDX2C(i,j,M)] = (float)(i * N + j + 1);
}
}
mcStat = mcMalloc ((void**)&devPtrA, M*N*sizeof(*a));
if (mcStat != 0) {
printf ("device memory allocation failed");
return EXIT_FAILURE;
}
stat = mcblasCreate(&handle);
if (stat != MCBLAS_STATUS_SUCCESS) {
printf ("mcBLAS initialization failed\n");
return EXIT_FAILURE;
}
stat = mcblasSetMatrix (M, N, sizeof(*a), a, M, devPtrA, M);
if (stat != MCBLAS_STATUS_SUCCESS) {
printf ("data download failed");
mcFree (devPtrA);
mcblasDestroy(handle);
return EXIT_FAILURE;
}
modify (handle, devPtrA, M, N, 1, 2, 16.0f, 12.0f);
stat = mcblasGetMatrix (M, N, sizeof(*a), devPtrA, M, a, M);
if (stat != MCBLAS_STATUS_SUCCESS) {
printf ("data upload failed");
mcFree (devPtrA);
mcblasDestroy(handle);
return EXIT_FAILURE;
}
mcFree (devPtrA);
mcblasDestroy(handle);
for (j = 0; j < N; j++) {
for (i = 0; i < M; i++) {
printf ("%7.0f", a[IDX2C(i,j,M)]);
}
printf ("\n");
}
free(a);
return EXIT_SUCCESS;
}
1.3. 数据布局
为了最大程度地兼容现有的Fortran环境,mcBLAS库使用列主(column-major)格式存储和基于1的索引。由于C和C++使用行主(row-major)格式存储,因此用这些语言编写的应用程序不能对二维数组使用原生数组语义。相反,应定义宏或内联函数(inline function)以基于一维数组实现矩阵。对于以机械方式移植到C的Fortran代码,可以选择保留基于1的索引,以避免变换循环(loop)。在这种情况下,可以通过以下的宏来计算行“i”和列“j”中矩阵元素的数组索引。
#define IDX2F(i,j,ld) ((((j)-1)*(ld))+((i)-1))
在这里,ld是指矩阵的前导维度(leading dimension),在列主存储的情况下,它是已分配矩阵(即使只使用其子矩阵)的行数。对于原生编写的C和C++代码,最有可能选择基于0的索引,在这种情况下,可以通过以下的宏来计算行“i”和列“j”中矩阵元素的数组索引。
#define IDX2C(i,j,ld) (((j)*(ld))+(i))