gtags

global(gtags) 代码标签

global 是代码标签工具,用来辅助编辑器做补全。

基本使用

生成标签

在项目的根目录下执行 gtags 就会生成 GPATH, GRTAGS, GTAGS 三个文件

命令行使用

命令行下可以用

    # 查看定义位置
    $ global func1
    DIR1/fileB.c            # func1() is defined in fileB.c

    # 使用 less 查看定义
    less -t func1

joblib

joblib: 简单的并行

joblib是一个易用的,无依赖的 python 库,主要用来很方便的做并行。

简单的并行

对一个 for 循环并行化,基本上只需要两个函数 Paralleldelayed ,用 delayed 包裹要并行的函数,并用列表推导式生成所有参数,再把这些东西扔给 Parallel 函数,返回的就是并行后的结果了。

基本上相当于 multiprocessing.Pool.map

   from joblib import Parallel, delayed
   Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))

Parallel 函数的参数用来指定并行的后端 backend 、每批次执行的任务数、并行线程数等等

Tips

读取时避免锁

使用 multiprocessing.shared_memory 创建共享内存,并把内存地址名作为参数传递

Rust Optimization

Rust 性能优化

这里所称性能优化实际上就是 Rust 的正确打开方式,因为 Rust 的安全特性导致按照 c/c++ 方式直接写程序有可能速度会比较慢。

避免边界检查

参考资料

  1. 这里 的说明

使用 slice 为编译器提示长度

vec 等类型的参数传递给函数后,在函数内部重新用 slice 的方式定义变量

    fn f_for(a: &Vec<f64>, b: &mut Vec<f64>){
        // 不可变量直接用长度做 slice
        let a = &a[..a.len()];
        // 可变量先定义一个长度再做 slice
        let len_b = b.len();
        let b = &mut b[..len_b];
    }

尽可能使用迭代器而不是指标循环

迭代器中的边界检查被优化掉了

    for (i, ai) in a.iter().enumerate(){
        do_something();
    }

    // 而不是

    for i in 0..n{
        a[i];
    }

不得不用指标获得值时用 unsafe 的非检查方式

当确保指标不会越界时就可以大胆使用 unsafe

    for i in 0..n{
        unsafe {
            a.get_unchecked(i);
        }
    }

    // not 
    a[i];

meson

meson 构建工具

网站 meson-build

配置示例

   cc = meson.get_compiler('c')

   all_deps = []
   thread_dep = dependency('threads')
   all_deps += thread_dep

   src_dir = include_directories('../src')

   exe = executable('name', ['src1.c', 'src2.c'],
           dependencies: all_deps, 
           include_directories: src_dir)

使用 address sanitizer

   CC=clang meson build  -Db_sanitize=address -Db_lundef=false

使用 clang 静态分析

   meson build
   ninja -C build scan-build

自定义外部依赖

参考 Manual/Dependencies

一个外部依赖只需要知道头文件和链接库(静态/动态)

   my_inc = include_directories(...)
   my_lib = static_library(...)
   my_dep = declare_dependency(link_with : my_lib,
                               include_directories : my_inc)

将依赖做子目录

首先在子目录中放入依赖的源码,并且在其中定义子项目的 meson.build 文件

    foo_dep = declare_dependency(...)

之后就可以在主项目中依赖它

    foo_dep = dependency('foo', fallback : ['foo', 'foo_dep'])

子项目

从子项目获取依赖

libsimple_proj = subproject('libsimple')
libsimple_dep = libsimple_proj.get_variable('libsimple_dep')

使用 wrap 管理依赖

subprojects 文件夹中创建 libfoobar.wrap 文件, 其中指定源码位置 [wrap-file] 和提供的依赖 [provide] ,格式参考 这里

对于非 meson-build 项目,还需要一个 meson.build 文件来添加支持,这个额外的文件称作 patch, 这个额外配置文件可以从网络中下载获取(通过 patch_url, 参考这里)

示例 subprojects/cache2.wrap

   [wrap-file]
   directory = Catch2-2.13.3
   source_url = https://github.com/catchorg/Catch2/archive/v2.13.3.zip
   source_filename = Catch2-2.13.3.zip
   source_hash = 1804feb72bc15c0856b4a43aa586c661af9c3685a75973b6a8fc0b950c7cfd13
   patch_directory = catch2

   [provide]
   catch2 = catch2_dep

自己编写的配置文件应该放在 subprojects/packagefiles/libfoobar/meson.build 中,并设置 patch_directory

示例 subprojects/packagefiles/cache2/meson.build

   project('catch2',
       'cpp',
       version : '2.13.3',
       license : 'Boost'
   )

   catch2_dep = declare_dependency(
       include_directories : include_directories('single_include')
   )

LAPACK

LAPACK

主页在 https://www.netlib.org/lapack/, 文档在 https://www.netlib.org/lapack/explore-html/.

安装说明

  1. liblapack 是本体
  2. liblapacke 是 c 的绑定

dsyevd: 实对称矩阵本征值

参数说明

  1. jobz 计算任务类型, jobz = "N" 只计算本征值, jobz = "V" 计算本征值和本征矢
  2. UPLO 矩阵的存储方式, UPLO = "U" A 中存了上三角矩阵, UPLO = "L" 下三角矩阵
  3. N 矩阵维数
  4. A 输入矩阵 LDA * N 维,计算后会被破坏,或者保存为本征矢
  5. LDA 矩阵维数
  6. W 本征值向量 N 维,升序排列
  7. WORK 工作向量,~LWORK~ 维, WORK[0] 是最优的 LWORK
  8. LWORK 工作向量维数, LWORK = -1 计算最优的 LWORK
  9. IWORK 工作向量, LIWORK 维, IWORK[0] 是最优的 LIWORK
  10. LIWORK 工作向量维数, LIWORK = -1 计算最优的 LIWORK
  11. INFO 错误信息, info = 0 成功

arpack

ARPACK

新版本在 arpack-ng , 文档在 rice - ARPACK

函数说明

头文件 arpack.h 中的函数有

  1. <?>neupd_c 计算一般矩阵的本征值, <?> = sdcz
  2. <?>naupd_c 计算一般矩阵的反向通信接口, <?> = sdcz
  3. <?>seupd_c 计算对称矩阵的本征值, <?> = sd
  4. <?>saupd_c 计算对称矩阵的反向通信接口, <?> = sd

arpack 中没有计算复厄米矩阵的函数,复矩阵一律用一般矩阵的计算函数

XYaupd 参数说明

  1. ido 反向通信标志,第一次运行 ido=0
  2. bmat 问题类型标志,标准本征值问题 bmat="I" , 扩展本征值问题 bmat="G"
  3. n 矩阵维数
  4. which Ritz值的位置 which="LA", "SA", "LM", "SM", "BE"
  5. nev 计算本征值的个数 0 < nev < n
  6. tol 浮点精度
  7. resid 残差向量
  8. ncv Lanczos 向量个数
  9. v Lanczos 向量, ncv
  10. ldv Lanczos 向量的首列长度
  11. iparam int[11] 的数组,保存了 11 个设置参数
    1. iparam[0]=ishift 选择隐式移动的方法, ishift=0 由用户指定, ishift=1 由约化三对角矩阵决定
    2. iparam[1]=levec 没用
    3. iparam[2]=mxiter 最大 Arnoldi 更新迭代次数
    4. iparam[3]=nb 块大小,只能取 nb=1
    5. iparam[4]=nconv 收敛了的 Ritz 值个数
    6. iparam[5]=iupd 没用
    7. iparam[6]=mode 指定本征值问题的类型只能取 mode = 1,2,3,4,5
    8. iparam[7]=np ido=3 时有用
    9. iparam[8]=numop OP*x 计算的次数
    10. iparam[9]=numob B*x 计算的次数
    11. iparam[10]=numreo 重正交化的次数
  12. ipntr int[11] 的数组,保存指向 workl, workd 中起始位置的指针
  13. workd 3*N 长的数组,算法内部使用
  14. workl lworkl 长的数组,算法内部使用
  15. lworkl workl 的长度,至少 ncv**2 + 8*ncv
  16. info 运行提示和错误信息

XYeupd 参数说明

  1. rvec 指定是否产生本征矢 rvec=true 产生本征矢, rvec=false 只产生本征值
  2. howmny 指定产生本征矢的个数 howmny="A" 计算 nev 个, howmny="P" 计算 nev Schur 本征矢, howmny="S" 计算一部分本征值,由 select 指定
  3. select 指定计算的本征矢
  4. dr Ritz 值的实部
  5. di Ritz 值的虚部
  6. z Ritz 向量
  7. ldz Ritz 向量的首列维数
  8. sigmar shift 的实部, iparam[6]=3 or 4 时有效
  9. sigmai shift 的虚部
  10. workev 3*ncv 的内部数组
  11. bmat 以下参数与 XYaupd 相同
  12. n
  13. which
  14. nev
  15. tol
  16. resid
  17. ncv
  18. v
  19. ldv
  20. iparam
  21. ipntr
  22. workd
  23. workl
  24. lworkl
  25. info

编译

基本的编译过程

    sh bootstrap
    ./configure
    make
    make check
    make install

ILP64: int64 支持

  • LP64 在 MKL 中表示 int 做指标
  • ILP64 表示 long long int 做指标

arpack 沿用了 MKL 的叫法。

配置:

  • 编译时定义 INTERFACE64
  • 使用 arpackdef.h 头文件
  • 使用 a_int 类型 (architecture int)
    INTERFACE64=1 ./configure --enable-mpi --enable-icb --with-blas=openblas64

icb: 生成 c 头文件

arpack-ng 使用 fortran 的 isocbinding(icb) 生成 c 头文件

    ./configure --enable-icb
    cmake -D ICB=ON

parpack: MPI 支持

    ./configure --enable-mpi

使用 cmake

  • BLA_VENDOR=Intel10_64ilp 使用 ilp64 的 MKL
  • CMAKE_POSITION_INDEPENDENT_CODE=ON 使用 -fPIC
    CC=gcc FC=gfortran cmake -D MPI=ON -D ICB=ON  -DCMAKE_INSTALL_PREFIX=$HOME/app/arpack -DBLA_VENDOR=Intel10_64ilp -DBUILD_SHARED_LIBS=OFF -DINTERFACE64=1  -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..

BLAS

BLAS

BLAS 的命名规则

BLAS 中的函数名一般规则是 <character><name><mode> ()

  • <character> 指明数据类型
    • s real, single precision
    • c complex, single precision
    • d real, double precision
    • z complex, double precision
  • <name> 指明矩阵类型 BLAS 2 and 3
    • ge general matrix
    • gb general band matrix
    • sy symmetric matrix
    • sp symmetric matrix (packed storage)
    • sb symmetric band matrix
    • he hermitian matrix
    • hp hermitian matrix (packed storage)
    • hb hermitian band matrix
    • tr triangular matrix
    • tp triangular matrix (packed storage)
    • tb triangular band matrix
  • <mod> 指明操作的细节
    • c conjugated vector
    • u unconjugated vector
    • g Givens rotation construction
    • m modified Givens rotation
    • mg modified Givens rotation construction
    • mv matrix-vector product
    • sv solving a system of linear equations with a single unknown vector
    • r rank-1 update of matrix
    • r2 rank-2 update of matrix
    • mm matrix-matrix product
    • sm solving a system of linear equations with multiple unkonwn vectors
    • rk rank-k update of matrix
    • r2k rank-2k update of a matrix

CBLAS 约定

CBLAS 是 BLAS 的 c 接口, BLAS 是 fortran 标准。

CBLAS 函数都遵循以下约定

  • 输入参数是 const 指针
  • 非复数标量输入参数传值
  • 复标量参数传 void 指针
  • 数组参数传地址
  • BLAS 特征参数由适当的枚举类型代替
  • level 2 和 level 3 需要一个额外的 CBLAS_LAYOUT 类型的参数来指定矩阵是行优先 CblasRowMajor 还是列优先 CblasColMajor

枚举类型

   enum CBLAS_LAYOUT {
      CblasRowMajor=101,    /* row-major arrays */
      CblasColMajor=102};   /* column-major arrays */

   enum CBLAS_TRANSPOSE {
      CblasNoTrans=111,     /* trans='N' */
      CblasTrans=112,       /* trans='T' */
      CblasConjTrans=113};  /* trans='C' */

   enum CBLAS_UPLO {
      CblasUpper=121,        /* uplo ='U' */
      CblasLower=122};       /* uplo ='L' */

   enum CBLAS_DIAG {
      CblasNonUnit=131,      /* diag ='N' */
      CblasUnit=132};        /* diag ='U' */

   enum CBLAS_SIDE {
      CblasLeft=141,         /* side ='L' */
      CblasRight=142};       /* side ='R' */

矩阵存储方式

  1. 完全存储矩阵 \(A_{ij}_{}\) 在二维数组列存储 a[i + j*lda] 行存储 a[i*lda + j]
  2. packed storage 可以用更有效的方式存储矩阵