为什么
t(mat1) %*% mat2
比crossprod(mat1, mat2)
工作得更快。后者的全部意义不就是它调用了更高效的低级例程吗?
r$> mat1 <- array(rnorm(100 * 600), dim = c(100, 600))
r$> mat2 <- array(rnorm(100 * 800), dim = c(100, 800))
r$> 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
继续奔跑
r$> 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
编辑:本地机器环境
r$> sessionInfo()
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
r$> Sys.getenv("R_BLAS_LIBS")
Sys.getenv("R_LAPACK_LIBS")
[1] ""
[1] ""
r$> getOption("matprod")
[1] "default"
r$> extSoftVersion()
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
""
这不是答案,但评论太长了:
crossprod
和简单矩阵乘法都可以执行简单的强力三重循环,或使用适当的参数调用BLAS例程gemm
[“通用矩阵-矩阵(乘法)”],具体取决于(1)R如何已配置以及 (2) 参数中是否有任何 NA
值。因此,鉴于 t(x) %*% y
必然涉及分配空间来保存转置矩阵和进行值的实际转置的中间步骤,人们会期望 t(x) %*% y
和 crossprod
都会使用 BLAS,或者都不使用 BLAS ,因此crossprod
永远不会比转置+矩阵乘法慢;但是,如果转置步骤相对于乘法中涉及的所有浮点运算非常快,则差异可能会在噪声中丢失。
随着矩阵变大,进行转置的成本(
O(n^2)
,不计算分配时间)相对于乘法的成本会缩小(O(n^3)
,如果是愚蠢的,看起来BLAS使用Strassen的算法,这是有道理的 即 O(n^2.8)
),因此 crossprod
的优势应该会缩小。 (此外,转置只需移动内容/分配值,而矩阵乘法必须执行实际算术,因此矩阵乘法应该具有更大的常数/更昂贵,所有其他条件都相同)。评论中@ThomasIsCoding 链接的博客文章指出了这种模式。但这仍然不能解释为什么 crossprod
应该做更糟 ... 如果你想深入研究代码,你可以找到例如
R_xlen_t NRX = nrx, NRY = nry, NCX = ncx;
for (int i = 0; i < ncx; i++)
for (int k = 0; k < ncy; k++) {
sum = 0.0;
for (int j = 0; j < nrx; j++)
sum += x[j + i * NRX] * y[j + k * NRY];
z[i + k * NCX] = (double) sum;
}
crossprod
有类似的逻辑)。
here,here,但似乎都没有解释这种模式;后者(比我比较转置和乘法成本做得更好)解释了为什么在某些情况下 t(x) %*% y
可能会更快当
y
包含大量零时- 不是这里的情况......
评论也太长了:
由于我使用flexiblas,我能够相对轻松地进行跨 BLAS 基准测试。
矩阵 m1、m2 是随着 缩放因子 的增加而创建的,m1
是通过 dim = [100, 6*fac] 创建的,
m2
是用 dim = [100, 8*fac] 创建的, 其中大小因子 fac = [1, 8, 64, 512]。 使用 NETLIB,
t(mat1) %*% mat2
明显快于
crossprod(mat1, mat2)
(fac=64;d=15.959;p
crossprod()
<0.0001). In contrast, for all other BLAS libraries, 始终优于
t(mat1) %*% mat2
,其中 OPENBLAS-SERIAL 显示出最快的性能(fac=64;d=-0.460) ;<0.0001) among single-threaded BLAS libraries compared to NETLIB. Notably, OPENBLAS-OPENMP, being multi-threaded, showed minimal difference between the two operations (fac=64; d=-0.145; p=0.3206).结论:
结果表明 NETLIB 处理 crossprod()
的效率可能较低。总体而言,内部效应规模减小,这支持了
@Ben Bolker'对换位成本的机械解释。 在下面的结果表中,wth
指的是BLAS内,btw指BLAS与NETLIB之间的BLAS作为参考BLAS。 d 指 Cohen'd d,p 指 p 值。使用 microbenchmark()
执行基准测试,每种配置重复 100 次。
library expr fct mean_time median_time sd_time p_wth d_wth p_btw d_btw
NETLIBv3.9.0* crossprod(m1, m2) 1 0.008 0.008 0.001 1.430e-08 -0.616 NA NA
NETLIBv3.9.0* t(m1) %*% m2 1 0.011 0.011 0.006 1.430e-08 -0.616 NA NA
ATLASv3.10.3 crossprod(m1, m2) 1 0.005 0.005 0.001 8.934e-13 -0.148 1.550e-57 -3.382
ATLASv3.10.3 t(m1) %*% m2 1 0.011 0.010 0.008 8.934e-13 -0.148 4.034e-01 -0.006
BLISv0.9.0 crossprod(m1, m2) 1 0.011 0.011 0.001 4.456e-03 -0.215 4.203e-77 6.260
BLISv0.9.0 t(m1) %*% m2 1 0.021 0.017 0.032 4.456e-03 -0.215 7.246e-04 0.020
OPENBLASv0.3.21-OPENMP crossprod(m1, m2) 1 0.004 0.004 0.001 6.543e-10 -0.351 5.952e-70 -4.883
OPENBLASv0.3.21-OPENMP t(m1) %*% m2 1 0.010 0.009 0.010 6.543e-10 -0.351 9.297e-03 -0.042
OPENBLASv0.3.21-SERIAL crossprod(m1, m2) 1 0.004 0.004 0.001 1.850e-09 -0.164 1.577e-71 -4.749
OPENBLASv0.3.21-SERIAL t(m1) %*% m2 1 0.011 0.009 0.011 1.850e-09 -0.164 1.557e-01 -0.019
缩放系数8
library expr fct mean_time median_time sd_time p_wth d_wth p_btw d_btw
NETLIBv3.9.0* crossprod(m1, m2) 8 0.437 0.420 0.079 9.181e-22 1.464 NA NA
NETLIBv3.9.0* t(m1) %*% m2 8 0.332 0.324 0.063 9.181e-22 1.464 NA NA
ATLASv3.10.3 crossprod(m1, m2) 8 0.142 0.142 0.009 6.875e-47 -3.022 1.083e-60 -4.984
ATLASv3.10.3 t(m1) %*% m2 8 0.171 0.173 0.010 6.875e-47 -3.022 1.872e-46 -3.210
BLISv0.9.0 crossprod(m1, m2) 8 0.071 0.066 0.039 1.535e-10 -0.946 2.060e-65 -5.887
BLISv0.9.0 t(m1) %*% m2 8 0.099 0.099 0.015 1.535e-10 -0.946 1.614e-60 -4.769
OPENBLASv0.3.21-OPENMP crossprod(m1, m2) 8 0.088 0.072 0.167 2.691e-01 -0.129 2.640e-35 -2.665
OPENBLASv0.3.21-OPENMP t(m1) %*% m2 8 0.106 0.107 0.022 2.691e-01 -0.129 1.186e-57 -4.709
OPENBLASv0.3.21-SERIAL crossprod(m1, m2) 8 0.054 0.053 0.012 2.108e-40 -2.472 1.146e-71 -6.384
OPENBLASv0.3.21-SERIAL t(m1) %*% m2 8 0.082 0.085 0.011 2.108e-40 -2.472 7.725e-64 -4.982
缩放因子64
library expr fct mean_time median_time sd_time p_wth d_wth p_btw d_btw
NETLIBv3.9.0* crossprod(m1, m2) 64 26.041 25.967 0.412 5.163e-104 15.959 NA NA
NETLIBv3.9.0* t(m1) %*% m2 64 15.590 15.529 0.841 5.163e-104 15.959 NA NA
ATLASv3.10.3 crossprod(m1, m2) 64 7.569 7.443 0.283 8.863e-05 -0.511 3.913e-182 -48.035
ATLASv3.10.3 t(m1) %*% m2 64 7.755 7.587 0.422 8.863e-05 -0.511 7.258e-98 -11.433
BLISv0.9.0 crossprod(m1, m2) 64 2.258 2.073 0.347 4.938e-10 -0.743 1.732e-195 -60.983
BLISv0.9.0 t(m1) %*% m2 64 2.552 2.366 0.433 4.938e-10 -0.743 1.217e-119 -18.932
OPENBLASv0.3.21-OPENMP crossprod(m1, m2) 64 1.421 0.836 1.063 2.079e-01 -0.145 4.644e-148 -22.831
OPENBLASv0.3.21-OPENMP t(m1) %*% m2 64 1.658 1.012 1.948 2.079e-01 -0.145 4.666e-84 -9.233
OPENBLASv0.3.21-SERIAL crossprod(m1, m2) 64 2.168 2.050 0.277 1.798e-04 -0.460 5.601e-198 -59.534
OPENBLASv0.3.21-SERIAL t(m1) %*% m2 64 2.330 2.173 0.406 1.798e-04 -0.460 4.006e-121 -19.346
缩放因子512
library expr fct mean_time median_time sd_time p_wth d_wth p_btw d_btw
NETLIBv3.9.0* crossprod(m1, m2) 512 1733.759 1728.830 16.205 5.592e-129 25.400 NA NA
NETLIBv3.9.0* t(m1) %*% m2 512 1241.799 1247.721 21.981 5.592e-129 25.400 NA NA
ATLASv3.10.3 crossprod(m1, m2) 512 489.746 486.333 10.618 1.794e-01 -0.153 4.427e-181 -90.790
ATLASv3.10.3 t(m1) %*% m2 512 491.527 486.131 12.480 1.794e-01 -0.153 6.680e-149 -41.802
BLISv0.9.0 crossprod(m1, m2) 512 147.600 146.746 4.762 1.699e-17 -1.431 3.411e-204 -109.626
BLISv0.9.0 t(m1) %*% m2 512 152.862 152.876 1.994 1.699e-17 -1.431 1.190e-170 -61.923
OPENBLASv0.3.21-OPENMP crossprod(m1, m2) 512 100.148 96.742 14.906 3.206e-01 -0.136 2.568e-189 -104.908
OPENBLASv0.3.21-OPENMP t(m1) %*% m2 512 102.031 101.905 12.570 3.206e-01 -0.136 1.421e-162 -64.349
OPENBLASv0.3.21-SERIAL crossprod(m1, m2) 512 139.406 137.633 6.863 4.804e-03 -0.324 1.387e-202 -118.417
OPENBLASv0.3.21-SERIAL t(m1) %*% m2 512 141.396 139.434 5.187 4.804e-03 -0.324 8.653e-169 -69.579
NETLIB 是之间(顺便说一句)比较的参考。 注意
R version 4.4.1 (2024-06-14)
Platform: x86_64-redhat-linux-gnu
Running under: AlmaLinux 9.4 (Seafoam Ocelot)
CPUs: 8