You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenBLAS自动并行DGEMM效率问题:大公共维度性能下降咨询

问题分析与解决方案

核心原因:并行开销大于收益

你遇到的不是OpenBLAS的局限,而是并行计算的通用问题——当计算任务的粒度太小时,线程调度、数据同步的开销会超过并行带来的性能提升。

具体到你的测试案例:

  • 6×100000 与 100000×6 的矩阵乘法,最终结果是6×6的矩阵。每个线程分到的计算量极小,线程创建、切换,以及数据在缓存间的同步开销,完全抵消甚至超过了多线程带来的计算加速。
  • 而1000×1000的矩阵乘法计算量足够大,并行的收益能覆盖开销,所以表现出性能提升。

OpenBLAS的并行策略

OpenBLAS的DGEMM会根据矩阵尺寸自动决定是否启用并行以及分配线程数。对于这种窄长型矩阵的乘法,它的自动并行判断可能没有最优适配,但这不是Bug,是并行计算的固有特性。

解决方案

1. 手动控制线程数

针对小尺寸矩阵的计算,直接强制使用单线程,避免并行开销:
在代码中根据矩阵尺寸判断,比如当结果矩阵的行数+列数小于某个阈值时,调用 openblas_set_num_threads(1),大尺寸计算再启用多线程。

也可以通过环境变量全局限制小任务的线程数:

export OPENBLAS_THREAD_LIMIT=1

2. 转置矩阵优化计算模式

对于6×100000 × 100000×6的乘法,先转置B矩阵得到BT(6×100000),此时C的每个元素是A的行与BT的行的点积,这种计算模式能更好利用CPU缓存,同时减少并行调度的无效开销。

3. 是否需要转用MKL?

没必要。MKL的自动并行策略可能更精细,但同样会遇到小任务并行开销的问题。除非你的场景中绝大多数是大矩阵计算,否则转用MKL解决不了这个特定问题,手动控制线程数或优化计算模式的性价比更高。

4. 手动实现并行?

完全没必要。OpenBLAS和MKL的DGEMM已经是经过高度优化的工业级实现,手动并行不仅很难达到它们的性能水平,还容易引入线程安全、缓存命中率低等问题。


原测试内容

Fortran测试代码

Program test

    Use, Intrinsic :: iso_fortran_env, Only :  wp => real64, li => int64
    integer, parameter :: dp = selected_real_kind(15, 307)
    
    Real( dp ), Dimension( :, : ), Allocatable :: a
    Real( dp ), Dimension( :, : ), Allocatable :: b
    Real( dp ), Dimension( :, : ), Allocatable :: c

    Integer :: na, nb, nc, m_iter
    Integer :: numthreads
    Integer( li ) :: start, finish, rate
    real(dp) :: sum_time1, alpha, beta

  
    Write( *, * ) 'number of threads'
    Read( *, * ) numthreads

    Write( *, * ) 'na, nb, nc?'
    Read( *, * ) na, nb, nc
    Allocate( a ( 1:na, 1:nb ) ) 
    Allocate( b ( 1:nb, 1:nc ) )  
    Allocate( c( 1:na, 1:nc ) )

  
    Call Random_number( a )
    Call Random_number( b )
    c  = 0.0_dp
 
    alpha = 1.0_dp
    beta = 0.0_dp
    m_iter = 10
  
    write (*,*) 'm_iter average', m_iter 
    !call omp_set_num_threads(1)
    call openblas_set_num_threads(1)
    sum_time1 = 0.0  
    do m = 1, m_iter     
      Call System_clock( start, rate )
      Call dgemm( 'N', 'N', na, nc, nb, alpha, a , na,  b , nb, beta, c, nc )
      Call System_clock( finish, rate )
      sum_time1 = sum_time1 + Real( finish - start, dp ) / rate  
    end do  
    Write( *, * ) 'Time for dgemm-1', sum_time1 / m_iter
      

   ! call omp_set_num_threads(numthreads)  
  !  call mkl_set_num_threads(numthreads)
    call openblas_set_num_threads(numthreads)
    sum_time1 = 0.0  
    do m = 1, m_iter     
      Call System_clock( start, rate )
      Call dgemm( 'N', 'N', na, nc, nb, alpha, a , na,  b , nb, beta, c, nc )
      Call System_clock( finish, rate )
      sum_time1 = sum_time1 + Real( finish - start, dp ) / rate  
    end do  
    Write( *, * ) 'Time for dgemm-auto parallel', sum_time1 / m_iter
 
     deallocate(a, b, c) 

  End

编译命令

gfortran-11 -O3 -fopenmp test.f90 -lopenblas

运行结果

number of threads

2

           2

 na, nb, nc?

6 100000 6

 m_iter average          10
Time for dgemm-1  0.15203639000000002
 Time for dgemm-auto parallel  0.20141369999999997

内容的提问来源于stack exchange,提问作者user235862

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 22:39:55