‘for’, ‘벡터화 연산’, 그리고 ‘C++의 for’
이 글에서 for문과 벡터화 연산, 그리고 C++의 for문의 속도를 비교한다.
이 과정에서 문항별 정답, 문항별 유형 자료가 있을 때, 유형별 총점을 구하는 방법을 제시한다.
for문 없이 for문 돌리기
다음 행렬 Y에서는 행은 학생, 열은 문항을 나타낸다.
Y
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] ## [1,] 1 1 1 0 1 1 0 1 1 0 ## [2,] 0 1 1 0 1 1 1 0 1 1 ## [3,] 1 1 1 0 1 1 1 1 1 1 ## [4,] 1 1 1 1 1 0 1 1 0 1 ## [5,] 1 0 1 1 1 1 1 1 1 0 ## [6,] 1 0 1 0 0 1 0 1 1 1 ## [7,] 1 1 1 0 1 1 1 0 1 1 ## [8,] 1 0 1 1 1 0 0 0 1 1 ## [9,] 1 0 0 0 1 1 1 0 1 1 ## [10,] 1 1 1 0 0 1 1 1 1 1 ## [11,] 1 1 0 1 1 0 1 1 1 0 ## [12,] 1 0 1 1 1 1 1 1 1 1 ## [13,] 1 0 1 1 0 1 1 1 1 1 ## [14,] 0 1 0 1 0 0 1 0 0 1 ## [15,] 0 1 1 1 1 1 0 1 1 0 ## [16,] 1 1 1 1 1 1 1 1 1 1 ## [17,] 1 1 1 1 1 0 1 1 1 1 ## [18,] 1 0 1 1 1 1 1 0 0 0 ## [19,] 0 0 1 1 1 0 0 1 1 1 ## [20,] 1 1 1 1 1 1 1 1 0 1
Y[3,4]은 3번째 학생의 4번째 문항에 대한 채점 결과를 나타내고 0은 틀렸음을 의미한다.
데이터 프레임 dfQ는 문항의 타입을 나타낸다.
dfQ
## type ## 1 A ## 2 A ## 3 B ## 4 C ## 5 A ## 6 B ## 7 B ## 8 A ## 9 A ## 10 B
dfQ[2, "type"]인 A은 2번째 문항의 type을 알려준다.
만약 모든 학생의 type별 총점을 알고자 한다면 어떻게 계산할 수 있을까?
for
가장 쉽게 떠오르는 해결책은 for일 것이다. 예를 들면 다음과 같다.
for (i in 1:10) {
if (dfQ[i, "type"] == "A") {
}
}
물론 type은 A만 있는 것이 아니니까, 다시 for를 돌려야 한다.
for (itype in c('A', 'B', 'C')) {
for (j in 1:10) {
if (dfQ[j, "type"] == itype) {
}
}
}
행렬 곱셈
행렬 곱셈의 정의를 보면 알겠지만, 행렬 곱셈에는 for( \(\sum\) )가 들어간다.
\[(AB)_{ij} = \sum_{k} a_{ik}b_{kj}\]
위의 공식을 보면 두 행렬의 곱 \(AB\) 의 \((i, j)\) 원소를 구하기 위해서는 행렬 \(A\) 의 \(i\) -번째 행과 행렬 \(B\) 의 \(j\) -번째 열을 차례로 곱한 후 더해 줘야 한다.
K를 행렬 \(A\) 의 행 갯수 또는 행렬 \(B\) 의 열 갯수라고 했을 때, \((AB)_{ij}\) (행렬 \(AB\) 의 \((i,j)\) 원소)는 다음과 같이 프로그래밍한다.
AB[i,j] = 0
for (k in 1:K) {
AB[i,j] = AB[i,j] + A[i,k] * B[k,j]
}
전체 행렬을 구하는 프로그래밍은 다음과 같다.
AB = matrix(0, nrow(A), ncol(B))
stopifnot(ncol(A) == nrow(B))
for (i in 1:nrow(A)) {
for (j in 1:ncol(A)) {
for (k in 1:K) {
AB[i,j] = AB[i,j] + A[i,k] * B[k,j]
}
}
}
하지만 우리는 위의 세 겹 for가 행렬의 곱셈을 구하는 것임을 알고 있기에 다음과 같이 간단하게 쓸 수 있다.
AB = A %*% B
m = 4; n = 5; o = 3
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
AB1 = matrix(0, nrow(A), ncol(B))
stopifnot(ncol(A) == nrow(B))
for (i in 1:nrow(A)) {
for (j in 1:ncol(B)) {
for (k in 1:ncol(A)) {
AB1[i,j] = AB1[i,j] + A[i,k] * B[k,j]
}
}
}
AB1
## [,1] [,2] [,3] ## [1,] 11.207803 9.875562 8.702675 ## [2,] 1.216236 1.293991 -4.197460 ## [3,] 15.968861 6.425821 15.385276 ## [4,] 4.368573 6.328005 -8.642257
AB2 = A %*% B
AB2
## [,1] [,2] [,3] ## [1,] 11.207803 9.875562 8.702675 ## [2,] 1.216236 1.293991 -4.197460 ## [3,] 15.968861 6.425821 15.385276 ## [4,] 4.368573 6.328005 -8.642257
all.equal(AB1, AB2)
## [1] TRUE
다시 문제로
다시 원래 문제로 되돌아가보자.
'만약 모든 학생의 type별 총점을 알고자 한다면 어떻게 계산할 수 있을까?'
먼저 결과를 예상해 보자. 결과는 행렬로 표현할 수 있을 것이며, 행은 학생을 열은 문항 type을 나타낼 것이다.
주어진 자료로 보자. 첫 번째 자료는 행이 학생, 열은 문항이 행렬이다. 따라서 두번째 자료를 행이 문항, 열이 문항 type인 행렬로 나타낼 수 있다면, 두 행렬의 곱셈으로 결과를 표현할 수 있지 않을가?
두번째 자료 dfQ를 행렬로 나타내는 방법은 다음과 같다.
Q <- model.matrix(~ type -1, dfQ)
행렬 Q는 각 문항에 대해 어떤 type에 속하는지를 보여준다.
Q
## typeA typeB typeC ## 1 1 0 0 ## 2 1 0 0 ## 3 0 1 0 ## 4 0 0 1 ## 5 1 0 0 ## 6 0 1 0 ## 7 0 1 0 ## 8 1 0 0 ## 9 1 0 0 ## 10 0 1 0 ## attr(,"assign") ## [1] 1 1 1 ## attr(,"contrasts") ## attr(,"contrasts")$type ## [1] "contr.treatment"
그리고 행렬 Y에 행렬 Q를 곱하면,
Y %*% Q
## typeA typeB typeC ## [1,] 5 2 0 ## [2,] 3 4 0 ## [3,] 5 4 0 ## [4,] 4 3 1 ## [5,] 4 3 1 ## [6,] 3 3 0 ## [7,] 4 4 0 ## [8,] 3 2 1 ## [9,] 3 3 0 ## [10,] 4 4 0 ## [11,] 5 1 1 ## [12,] 4 4 1 ## [13,] 3 4 1 ## [14,] 1 2 1 ## [15,] 4 2 1 ## [16,] 5 4 1 ## [17,] 5 3 1 ## [18,] 2 3 1 ## [19,] 3 2 1 ## [20,] 4 4 1
행렬 곱셈의 한계
하지만 인정해야 한다. 이번 경우는 아주 운이 좋았다.
만약 다음과 같은 for문을 돌려야 한다면 어떨까?
AB = matrix(1, nrow(A), ncol(B))
stopifnot(ncol(A) == nrow(B))
for (i in 1:nrow(A)) {
for (j in 1:ncol(A)) {
for (k in 1:K) {
AB[i,j] = AB[i,j] * min(A[i,k],B[k,j])
}
}
}
앞의 경우와 달라진 부분은 AB[i,j] = AB[i,j]*min(A[i,k],B[k,j])과 AB의 초기화 부분이다.
물론 위의 소스를 사용할 수도 있다. 하지만 속도가 문제이다. R에서 for은 대게 조금 느리다.
다른 방법은 for 대신 vectorized-연산을 사용하는 것이다. 사실 대부분의 R 연산은 vectorised 되어 있다.
예를 들어 pmin은 vectorized-min으로 각 원소끼리 비교한다.
a <- c(1,2,3)
b <- c(3,1,4)
pmin(a, b)
## [1] 1 1 3
위의 결과는 벡터 a의 1번째 원소와 벡터 b의 1 번째 원소를 비교하고, 벡터 a의 2번째 원소와 벡터 b의 2번째 원소를 비교하고, 벡터 a의 3번째 원소와 벡터 b의 3번째 원소를 비교한다.
하지만 위의 for를 어떻게 구체적으로 vectorised 연산으로 대체할 것인가?
행렬 곱셈을 vectorised 덧셈으로
행렬 곱셈이 \((AB)_{ij} = \sum_{k} a_{ik}b_{kj}\) 라면, vectorised 덧셈은 다음과 같이 쓸 수 있다. vectorised 덧셈을 \(\circ\) 로 쓰면,
\[(A\circ B)_{ij} = A_{ij} + B_{ij}\]
만약 만약 vectorised곱셈을 3차원 배열에 적용한다면,
\[(\mathbb{A} \circ \mathbb{B})_{ijk} = \mathbb{A}_{ijk} \times \mathbb{B}_{ijk}\]
문제는 다차원의 행렬을 다루는 것은 쉽지 않다는 점이다. 1차원, 2차원,까지 어떻게든 그려보고, 유추해 볼 수 있지만, 3차원을 넘어서게 되면 어찌할 바를 모르는 것은 당연하다.[1]
[1]: 어쩌면 인공지능은 고차원의 대상에 대해서도 인간이 1, 2차원의 시각적 대상을 다루듯이 쉽게 이해하고, 이해하고, 해석하고, 유추해 낼 수 있을지 모른다.
일찍이 데카르트는 기하학적 대상을 대수학으로 표현하고, 정의하고, 증명하는 방법을 개발했다. 이로써 고차원의 대상을 대수학을 통해 연구할 수 있게 된 것이다.
다차원의 배열도 대수학, 또는 기호의 힘을 빌려 표현할 수 있다.
위의 새로운 행렬곱을 기존의 행렬 곱셈에 잘 녹여보자.
\[(AB)_{ij} = \sum_{k} (\mathbb{A} \circ \mathbb{B})_{ikj}= \sum_{k} (\mathbb{A})_{ikj} \times (\mathbb{B})_{ikj}\]
여기서 \(A\) 와 \(B\) 는 2차원의 행렬이고, \(\mathbb{A}\) 와 \(\mathbb{B}\) 는 3차원의 행렬을 가정했다.
그리고 \((AB)_{ij} = \sum_{k} a_{ik}b_{kj}\) 가 성립되도록 하려면 \(a_{ik} = (\mathbb{A})_{ikj}\) 과 \(b_{kj} = (\mathbb{B})_{ikj}\) 가 되도록 하면 된다.
\(a_{ik}=(\mathbb{A})_{ikj}\) 는 3차원 배열 \(\mathbb{A}\) 의 \((i,k,j)\) -원소를 결정한다.
행렬 A와 3차원 배열 A_를 가정하면,
A_[i,k,] = A[i,k]
여기서 i와 k에 대해 for를 대체하는 방법은 다음과 같다.
cat('**\n** a matrix A\n**\n')
A
A_ = aperm(array(A, c(nrow(A), ncol(A), n)), c(1,3,2))
cat('\n**\n** an 3-dim array A_\n**\n')
A_
## ** ## ** a matrix A ## ** ## [,1] [,2] [,3] [,4] [,5] ## [1,] -2.8506566 -0.5115903 -2.688153 0.3504492 2.504536 ## [2,] 0.3691555 1.1735496 -1.370809 -1.2154017 -1.666281 ## [3,] -2.6397274 -2.7647142 -2.457766 0.3106380 2.733579 ## [4,] -1.0165582 2.2531111 -2.734125 -2.3229661 -2.951444 ## ## ** ## ** an 3-dim array A_ ## ** ## , , 1 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] -2.8506566 -2.8506566 -2.8506566 -2.8506566 -2.8506566 ## [2,] 0.3691555 0.3691555 0.3691555 0.3691555 0.3691555 ## [3,] -2.6397274 -2.6397274 -2.6397274 -2.6397274 -2.6397274 ## [4,] -1.0165582 -1.0165582 -1.0165582 -1.0165582 -1.0165582 ## ## , , 2 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] -0.5115903 -0.5115903 -0.5115903 -0.5115903 -0.5115903 ## [2,] 1.1735496 1.1735496 1.1735496 1.1735496 1.1735496 ## [3,] -2.7647142 -2.7647142 -2.7647142 -2.7647142 -2.7647142 ## [4,] 2.2531111 2.2531111 2.2531111 2.2531111 2.2531111 ## ## , , 3 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] -2.688153 -2.688153 -2.688153 -2.688153 -2.688153 ## [2,] -1.370809 -1.370809 -1.370809 -1.370809 -1.370809 ## [3,] -2.457766 -2.457766 -2.457766 -2.457766 -2.457766 ## [4,] -2.734125 -2.734125 -2.734125 -2.734125 -2.734125 ## ## , , 4 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] 0.3504492 0.3504492 0.3504492 0.3504492 0.3504492 ## [2,] -1.2154017 -1.2154017 -1.2154017 -1.2154017 -1.2154017 ## [3,] 0.3106380 0.3106380 0.3106380 0.3106380 0.3106380 ## [4,] -2.3229661 -2.3229661 -2.3229661 -2.3229661 -2.3229661 ## ## , , 5 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] 2.504536 2.504536 2.504536 2.504536 2.504536 ## [2,] -1.666281 -1.666281 -1.666281 -1.666281 -1.666281 ## [3,] 2.733579 2.733579 2.733579 2.733579 2.733579 ## [4,] -2.951444 -2.951444 -2.951444 -2.951444 -2.951444
\(b_{kj} = (\mathbb{B})_{ikj}\) 에 대해서는 다음과 같이 진행할 수 있다.
cat('**\n** a matrix B\n**\n')
B
B_ = aperm(array(B, c(nrow(B), ncol(B), nrow(A))), c(3,1,2))
cat('\n**\n** an 3-dim array B_\n**\n')
B_
## ** ## ** a matrix B ## ** ## [,1] [,2] [,3] ## [1,] -0.9372064 -2.1983374 0.6764231 ## [2,] -2.3027833 1.0288336 -2.9447463 ## [3,] -2.2234472 -1.8563753 -1.8392334 ## [4,] -2.1741757 2.2554975 0.7446540 ## [5,] 0.8556636 -0.6569917 1.5648881 ## ## ** ## ** an 3-dim array B_ ## ** ## , , 1 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] -0.9372064 -2.302783 -2.223447 -2.174176 0.8556636 ## [2,] -0.9372064 -2.302783 -2.223447 -2.174176 0.8556636 ## [3,] -0.9372064 -2.302783 -2.223447 -2.174176 0.8556636 ## [4,] -0.9372064 -2.302783 -2.223447 -2.174176 0.8556636 ## ## , , 2 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] -2.198337 1.028834 -1.856375 2.255498 -0.6569917 ## [2,] -2.198337 1.028834 -1.856375 2.255498 -0.6569917 ## [3,] -2.198337 1.028834 -1.856375 2.255498 -0.6569917 ## [4,] -2.198337 1.028834 -1.856375 2.255498 -0.6569917 ## ## , , 3 ## ## [,1] [,2] [,3] [,4] [,5] ## [1,] 0.6764231 -2.944746 -1.839233 0.744654 1.564888 ## [2,] 0.6764231 -2.944746 -1.839233 0.744654 1.564888 ## [3,] 0.6764231 -2.944746 -1.839233 0.744654 1.564888 ## [4,] 0.6764231 -2.944746 -1.839233 0.744654 1.564888
그리고 이를 활용하여 새로운 행렬 곱 AB[i,j] = AB[i,j] * min(A[i,k],B[k,j])은 다음과 같이 쓴다.
A_ = aperm(array(A, c(nrow(A), ncol(A), ncol(B))), c(1,2,3))
B_ = aperm(array(B, c(nrow(B), ncol(B), nrow(A))), c(3,1,2))
apply(pmin(A_, B_), c(1,3), prod)
## [,1] [,2] [,3] ## [1,] 32.82839 0.9026223 -12.375282 ## [2,] -17.38433 8.5030215 4.049134 ## [3,] 33.36933 3.6607009 -9.287211 ## [4,] -43.88145 42.3970082 -56.114596
Vectorised-연산의 장점
더 복잡한가? 왜 이 난리인가? 백문이 불여일견. 한 번 시뮬레이션을 해보자.
func1 = function(A, B) {
AB = matrix(1, nrow(A), ncol(B))
stopifnot(ncol(A) == nrow(B))
for (i in 1:nrow(A)) {
for (j in 1:ncol(B)) {
for (k in 1:ncol(A)) {
AB[i,j] = AB[i,j] * min(A[i,k],B[k,j])
}
}
}
return(AB)
}
func2 = function(A, B) {
A_ = aperm(array(A, c(nrow(A), ncol(A), ncol(B))), c(1,2,3))
B_ = aperm(array(B, c(nrow(B), ncol(B), nrow(A))), c(3,1,2))
apply(pmin(A_, B_), c(1,3), prod)
}
속도를 측정한다.
m = 30; n = 50; o = 40
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
all.equal(func1(A,B), func2(A,B))
## [1] TRUE
library(rbenchmark)
benchmark(
func1 = {
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
func1(A, B)
},
func2={
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
func2(A, B)
})
## test replications elapsed relative user.self sys.self user.child ## 1 func1 100 23.33 38.246 19.31 0.07 NA ## 2 func2 100 0.61 1.000 0.60 0.02 NA ## sys.child ## 1 NA ## 2 NA
Rcpp
아는 사람만 알겠지만 Rcpp 패키지는 R에서 속도 향상을 위해 꽤나 자주 쓰이는 패키지이다. C++을 알아야 한다는 제약이 있긴 하지만, 컴퓨터 언어란 대부분은 비슷하지 않은가 말이다.
Rcpp 행렬 곱셈
행렬 곱셈을 C++로 구현한 후, Rcpp 패키지를 통해 R에서 사용할 수 있는 함수로 만들어 보았다.
library(Rcpp)
cppFunction('
NumericMatrix AB(NumericMatrix A, NumericMatrix B) {
int nrow = A.nrow(), ncol = B.ncol(), K=A.ncol();
NumericMatrix out(nrow, ncol);
for (int i = 0; i < nrow; i++) {
for (int j = 0; j < ncol; j++) {
double tot = 0;
for (int k = 0; k < K; k++) {
tot += A(i,k)*B(k,j);
}
out(i,j) = tot;
}
}
return out;
}
')
R과 다른 C++의 특징을 살펴보면 다음과 같다.
- 함수 정의 :
output_type func_name(input_type1 input_variable1, input_type2 input_variable2, ) { }- 함수를 정의할 때, 입력 받는 변수의 타입과 출력값의 타입을 명시해 주어야 한다.
- 변수 정의 :
variable_type variable_name,variable_type variable_name=initial_value- 변수를 정의할 때에도 변수의 타입을 정의해야 하며, 타입은 변경할 수 없다.
- 실수 행렬 :
NumericMatrix - 행렬의 원소 :
mat(i,j)- R의
mat[i,j]가 아니라mat(i,j)표기를 사용한다.
- R의
- 명령문의 마지막에 항상
;를 붙인다.
위에서 Rcpp로 정의한 행렬곱셈(AB)과 R의 행렬 곱셈(%*%)의 속도를 비교해보자.
m=400; n=500; o=300
benchmark(
func1 = {
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
AB(A,B)
},
func2={
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
A %*% B
})
## test replications elapsed relative user.self sys.self user.child ## 1 func1 100 9.70 2.165 9.03 0.09 NA ## 2 func2 100 4.48 1.000 4.15 0.13 NA ## sys.child ## 1 NA ## 2 NA
Rcpp로 새로운 행렬 곱 구현하기
cppFunction('
NumericMatrix AB2(NumericMatrix A, NumericMatrix B) {
int nrow = A.nrow(), ncol = B.ncol(), K=A.ncol();
NumericMatrix out(nrow, ncol);
for (int i = 0; i < nrow; i++) {
for (int j = 0; j < ncol; j++) {
double tot = 1;
for (int k = 0; k < K; k++) {
tot *= ((A(i,k)) < (B(k,j)) ? (A(i,k)) : (B(k,j)));
}
out(i,j) = tot;
}
}
return out;
}
')
먼저 결과를 확인하고,
m=40; n=50; o=30
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
all.equal(AB2(A,B), func2(A,B))
## [1] TRUE
앞에서 정의한 함수들과 속도를 비교해보자.
bm <- benchmark(
AB2 = {
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
AB2(A,B)
},
func2={
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
func2(A,B)
},
func1={
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
func1(A,B)
}
)
bm
## test replications elapsed relative user.self sys.self user.child ## 1 AB2 100 0.05 1.0 0.03 0.00 NA ## 3 func1 100 19.47 389.4 18.13 0.03 NA ## 2 func2 100 1.03 20.6 0.70 0.09 NA ## sys.child ## 1 NA ## 3 NA ## 2 NA
100배의 차이는 다음과 같이 생각해 볼 수 있다.
-
1초 걸릴 일이라면 100초가 걸린다. 1분 40초는 짧지는 않지만 그렇다고 잠깐 화장실 다녀오기에는 짧은 시간이다.
-
1분이 걸릴 일이라면 100분이 걸린다. 1시간 40분은 영화 한 편 보기에 적정한 시간이다.
-
1시간이 걸릴 일이라면 100시간이 걸린다. 100시간은 4일 정도 된다.
-
1일이 걸릴 일이라면 100일이 걸린다. 3달이다!
마무리
이 글에서는 R의 for, 벡터화 연산, 그리고 C++의 for를 비교해보았다.
작은 실험 결과, 속도 향상 효과는 벡터화 연산 20.6배, C++ for는 389.4배로 측정되었다!
여기서는 C++으로 작성한 행렬 곱셈의 예를 보여주었다. 비슷한 행렬곱이 필요한 경우 copy-and-paste로 쉽게 옮겨 적은 후 약간 수정을 하면 될 것이다.
사실 위에서 설명한 데이터 처리(문항 유형별 점수 구하기나 prod(pmin(,)))는 개념적으로는 매우 간단하다. 하지만 이를 실제 처리하기 위해 신속한 프로그램을 짜려면 초심자가 하기에 다소 버거운 것도 사실이다.
참고 자료
-
C++ 컴파일러 설치
- 윈도우즈: Rtools
- 맥 : Xcode
- 리눅스 :
sudo apt-get install r-base-dev
부록 : RcppArmadillo
library(RcppArmadillo)
cppFunction("arma::mat AB3(arma::mat A, arma::mat B) {
return A * B ;
}
",
depends="RcppArmadillo")
패키지 RcppArmadillo는 다음과 같다.
'Armadillo' is a templated C++ linear algebra library (by Conrad Sanderson) that aims towards a good balance between speed and ease of use. Integer, floating point and complex numbers are supported, as well as a subset of trigonometric and statistics functions. Various matrix decompositions are provided through optional integration with LAPACK and ATLAS libraries.
RcppAramdillo를 사용하면 행렬 곱셈는 그냥 *로 처리할 수 있기에 편리하다.
bm <- benchmark(
AB3 = {
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
AB3(A,B)
},
"%*%"={
A = matrix(runif(m*n,-3,3), m, n)
B = matrix(runif(n*o,-3,3), n, o)
A %*% B
}
)
bm
## test replications elapsed relative user.self sys.self user.child ## 2 %*% 100 0.01 1 0.02 0 NA ## 1 AB3 100 0.01 1 0.01 0 NA ## sys.child ## 2 NA ## 1 NA
Leave a comment