为何t(mat1) %*% mat2比crossprod(mat1, mat2)运行更快?
t(mat1) %*% mat2 比 crossprod(mat1, mat2) 更快的原因分析
你观察到的性能差异主要由以下几个因素导致:
1. 内存存储布局的影响
R中的矩阵采用列优先存储:矩阵的每一列元素在内存中是连续的,而行元素则分散在不同列的内存位置中。
t(mat1)会生成一个新的转置矩阵,原mat1的每一行变为新矩阵的一列,因此转置后的矩阵列是连续的内存块,完美匹配BLAS矩阵乘法的最优内存访问模式。crossprod(mat1, mat2)直接使用原mat1的行(非连续内存)与mat2的行计算内积,此时内存访问的缓存命中率极低,大量时间消耗在等待内存数据加载上。虽然t(mat1)有额外的内存分配和转置开销,但后续连续内存的乘法效率提升完全抵消了这部分开销,最终整体速度更快。
2. 默认BLAS的局限性
你的环境使用R内置的参考BLAS(从extSoftVersion()输出中BLAS字段为空可确认),该BLAS实现对连续内存的矩阵乘法优化较好,但对非连续内存的访问效率很低。如果换成优化过的BLAS库(如OpenBLAS、Intel MKL),这种性能差异会显著缩小甚至反转——优化BLAS会通过内存预取、多线程等技术掩盖缓存延迟,智能处理非连续内存的计算。
3. 函数内部实现细节
虽然crossprod(mat1, mat2)和t(mat1) %*% mat2数学等价,但两者的底层调用逻辑有细微差异:
crossprod在处理非方阵的交叉乘积时,内部通过设置转置标志直接调用BLAS的矩阵乘法,没有显式创建转置矩阵;- 显式调用
t(mat1)后再做乘法,相当于提前完成了内存布局的调整,让BLAS可以直接以最优模式执行计算。在参考BLAS的限制下,这种提前调整带来的收益远大于转置的开销。
测试代码与结果
mat1 <- array(rnorm(100 * 600), dim = c(100, 600)) mat2 <- array(rnorm(100 * 800), dim = c(100, 800)) microbenchmark::microbenchmark(crossprod(mat1, mat2), t(mat1) %*% mat2)
运行结果:
Unit: milliseconds expr min lq mean median uq max neval crossprod(mat1, mat2) 37.1905 44.35975 48.49262 47.61035 50.99725 76.1250 100 t(mat1) %*% mat2 25.0813 31.68405 35.46811 35.18380 39.16565 49.8131 100
本地环境信息
version
_ platform x86_64-w64-mingw32 arch x86_64 os mingw32 crt ucrt system x86_64, mingw32 status major 4 minor 4.1 year 2024 month 06 day 14 svn rev 86737 language R version.string R version 4.4.1 (2024-06-14 ucrt) nickname Race for Your Life
补充环境信息:
sessionInfo() Sys.getenv("R_BLAS_LIBS") Sys.getenv("R_LAPACK_LIBS") getOption("matprod") extSoftVersion()
R version 4.4.1 (2024-06-14 ucrt) Platform: x86_64-w64-mingw32/x64 Running under: Windows 11 x64 (build 22631) Matrix products: default [1] "" [1] "" [1] "default" zlib bzlib xz "1.3.1" "1.0.8, 13-Jul-2019" "5.4.6" libdeflate PCRE ICU "1.19" "10.43 2024-02-16" "74.2" TRE iconv readline "TRE 0.8.0 R_fixes (BSD)" "win_iconv" "" BLAS ""
内容的提问来源于stack exchange,提问作者Turdle
相关产品推荐
相关产品推荐

