‘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