一.安装/配置PETSc PETSc需要MPI和BLAS库。还有gcc等基础包。mpich可以直接apt-get安装:1 sudo apt-get install mpich
如果不确定有没有安装BLAS/mpich,可以apt list —installed | grep blas找一下。
有了这两个之后下载 petsc,解压后执行./configure
,接下来按照给出的提示完成安装就可以了。如果没有mpi或者blas,可以在后面加--download-mpich --download-fblaslapack
。
PETSC有两个可设置的环境变量,分别是:
1 2 PETSC_DIR=/path/to/petsc PETSC_ARCH=/
PETSC这个环境变量的作用是指定特定的构建和配置,一般来说,在一般机器上会构建多个PETSC版本。默认情况下(按照上面的直接安装)是针对调试使用的,其路径为arch-linux-c-debug。还可以构建一个针对性能优化的版本(例如arch-linux-opt)。
以上已经编译了一个调试使用的版本,接下来构建一个arch-linux-opt配置的版本,和上面是类似的,运行以下命令,然后根据给出的提示完成安装配置:
1 ./configure PETSC_ARCH=arch-linux-opt --with-debugging=no COPTFLAGS="-O3 -march=armv8-a" CXXOPTFLAGS="-O3 -march=armv8-a" FOPTFLAGS="-O3 --march=armv8-a"
二.编译/运行PETSc程序 上述工作后,PETSc就编译好了。编写PETSC程序时引入头文件,就可以使用相应的API。编译PETSC程序前,都要保证环境变量中有PETSC_DIR和PETSC_ARCH,可以在编译前进行export,也可以直接写入~/.bashrc。然后使用以下命令编译程序:
1 mpicxx -o main main.cpp -I${PETSC_DIR} /include -I${PETSC_DIR} /${PETSC_ARCH} /include -L${PETSC_DIR} /${PETSC_ARCH} /lib -lpetsc
运行的时候可能会发现报error while loading shared libraries: libpetsc.so.3.20: cannot open shared object file: No such file or directory
,PETSC动态库没有链接上,遇到这种情况可以将其添加到动态库路径:
1 2 3 find / -name libpetsc.so.3.20 export LD_LIBRARY_PATH=/root/petsc-3.20.5/arch-linux-opt/lib:LD_LIBRARY_PATH
然后就可以运行程序了:
三.PETSc的API PETSc的API可以在petsc.org 找到,文档编写的非常详细。下面以用PETSc解决SpMV问题为例,为了方便没有使用PetscCall()处理可能的错误,而且是编写SpMV测试时使用的,因此值是从定义好的矩阵格式获取再SetValue插入的,PETSc还支持SetValues直接插入数组来填充矩阵值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 void petsc_evaluation (SparseMatrixCOO *ordered_coo, char *output_path) { Mat A; PetscMPIInt rank; double *vecx = (double *)malloc (sizeof (double )*ordered_coo->ncolumns); struct timespec start, end; long long unsigned int elapsed; PetscFunctionBeginUser; PetscInitialize (NULL , NULL , NULL , NULL ); MPI_Comm_rank (PETSC_COMM_WORLD, &rank); MatCreate (PETSC_COMM_WORLD, &A); MatSetType (A, MATMPIAIJ); MatSetSizes (A, PETSC_DECIDE, PETSC_DECIDE, ordered_coo->nrows, ordered_coo->ncolumns); MatSetFromOptions (A); MatSetUp (A); for (int i=0 ;i<ordered_coo->nnz;i++){ MatSetValue (A, (PetscInt)ordered_coo->row_index[i], (PetscInt)ordered_coo->col_index[i], (PetscScalar)ordered_coo->values[i], INSERT_VALUES); } MatAssemblyBegin (A, MAT_FINAL_ASSEMBLY); MatAssemblyEnd (A, MAT_FINAL_ASSEMBLY); Vec x; VecCreate (PETSC_COMM_WORLD, &x); VecSetSizes (x, PETSC_DECIDE, ordered_coo->ncolumns); VecSetFromOptions (x); generate_x (vecx, ordered_coo->ncolumns); for (int i=0 ;i<ordered_coo->ncolumns;i++) VecSetValue (x, (PetscInt)i, (PetscScalar)vecx[i], INSERT_VALUES); VecAssemblyBegin (x); VecAssemblyEnd (x); Vec y; VecCreate (PETSC_COMM_WORLD, &y); VecSetSizes (y, PETSC_DECIDE, ordered_coo->ncolumns); VecSetFromOptions (y); VecAssemblyBegin (y); VecAssemblyEnd (y); int iterations; if (ordered_coo->nnz>150000 ) iterations = 5000 ; else if (ordered_coo->nnz>300000 ) iterations = 1000 ; else iterations = 50000 ; #ifdef DEBUG iterations = 1 ; #endif clock_gettime (CLOCK_MONOTONIC_RAW, &start); for (int i=0 ; i<iterations; i++) { MatMult (A, x, y); PetscBarrier (NULL ); } clock_gettime (CLOCK_MONOTONIC_RAW, &end); elapsed = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000 ; clock_gettime (CLOCK_MONOTONIC_RAW, &start); MatMult (A, x, y); PetscBarrier (NULL ); clock_gettime (CLOCK_MONOTONIC_RAW, &end); double flops_count = ordered_coo->nnz*2 ; double sec = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9 ; double GFLOPS = flops_count / (sec*1e9 ); if (rank==0 ){ FILE *fp; fp = fopen (output_path, "a" ); if (fp == NULL ) { printf ("无法打开文件 %s\n" , output_path); } fprintf (fp, "%s %llu %lf\n" , "petsc" , elapsed, GFLOPS); fclose (fp); } free (vecx); VecDestroy (&x); VecDestroy (&y); MatDestroy (&A); PetscFinalize (); }