R Language
कठिन-से-वेक्टराइज़ कोड को गति देना
खोज…
Rcpp के साथ छोरों के लिए कठिन-से-सदिश गति
लूप के लिए निम्नलिखित कठिन-से-वेक्टराइज़ पर विचार करें, जो लंबाई के len
का एक वेक्टर बनाता है जहां पहला तत्व निर्दिष्ट किया जाता है ( first
) और प्रत्येक तत्व x_i
cos(x_{i-1} + 1)
बराबर है cos(x_{i-1} + 1)
:
repeatedCosPlusOne <- function(first, len) {
x <- numeric(len)
x[1] <- first
for (i in 2:len) {
x[i] <- cos(x[i-1] + 1)
}
return(x)
}
इस कोड में एक तेज़ ऑपरेशन ( cos(x[i-1]+1)
) के साथ लूप शामिल है, जो अक्सर वैश्वीकरण से लाभान्वित होते हैं। हालांकि, इस ऑपरेशन को आधार आर के साथ वेक्टर करना तुच्छ नहीं है, क्योंकि आर में "x + 1" फ़ंक्शन का संचयी कोसाइन नहीं है।
इस फ़ंक्शन को गति देने के लिए एक संभव तरीका आरसीपी पैकेज का उपयोग करके सी ++ में इसे लागू करना होगा:
library(Rcpp)
cppFunction("NumericVector repeatedCosPlusOneRcpp(double first, int len) {
NumericVector x(len);
x[0] = first;
for (int i=1; i < len; ++i) {
x[i] = cos(x[i-1]+1);
}
return x;
}")
सटीक परिणाम देते समय यह अक्सर बड़े कम्प्यूटेशन के लिए महत्वपूर्ण स्पीडअप प्रदान करता है:
all.equal(repeatedCosPlusOne(1, 1e6), repeatedCosPlusOneRcpp(1, 1e6))
# [1] TRUE
system.time(repeatedCosPlusOne(1, 1e6))
# user system elapsed
# 1.274 0.015 1.310
system.time(repeatedCosPlusOneRcpp(1, 1e6))
# user system elapsed
# 0.028 0.001 0.030
इस स्थिति में, Rcpp कोड, बेस R दृष्टिकोण के साथ 1.31 सेकंड के बजाय 0.03 सेकंड में 1 मिलियन लंबाई का वेक्टर उत्पन्न करता है।
बाइट संकलन द्वारा लूप्स के लिए कठिन-से-वेक्टराइज़ करना
इस दस्तावेज़ प्रविष्टि में Rcpp उदाहरण के बाद, निम्नलिखित कठिन-से-वेक्टराइज़ फ़ंक्शन पर विचार करें, जो लंबाई के len
का एक वेक्टर बनाता है जहां पहला तत्व निर्दिष्ट किया गया है ( first
) और प्रत्येक तत्व x_i
cos(x_{i-1} + 1)
बराबर है cos(x_{i-1} + 1)
:
repeatedCosPlusOne <- function(first, len) {
x <- numeric(len)
x[1] <- first
for (i in 2:len) {
x[i] <- cos(x[i-1] + 1)
}
return(x)
}
कोड की एक पंक्ति को फिर से लिखने के बिना इस तरह के एक कार्य को तेज करने के लिए एक सरल दृष्टिकोण आर संकलन का उपयोग करके कोड को संकलित करना है:
library(compiler)
repeatedCosPlusOneCompiled <- cmpfun(repeatedCosPlusOne)
परिणामी फ़ंक्शन अक्सर उसी परिणाम को वापस करते समय काफी तेज होगा:
all.equal(repeatedCosPlusOne(1, 1e6), repeatedCosPlusOneCompiled(1, 1e6))
# [1] TRUE
system.time(repeatedCosPlusOne(1, 1e6))
# user system elapsed
# 1.175 0.014 1.201
system.time(repeatedCosPlusOneCompiled(1, 1e6))
# user system elapsed
# 0.339 0.002 0.341
इस मामले में, बाइट संकलन ने कठिन-से-वेक्टराइज़ ऑपरेशन को 1.20 सेकंड से 0.34 सेकंड की लंबाई के 1 मिलियन वेक्टर पर फैलाया।
टिप्पणी
repeatedCosPlusOne
फ़ंक्शन का सार, एकल फ़ंक्शन के संचयी अनुप्रयोग के रूप में, Reduce
साथ अधिक पारदर्शी रूप से व्यक्त किया जा सकता है:
iterFunc <- function(init, n, func) {
funcs <- replicate(n, func)
Reduce(function(., f) f(.), funcs, init = init, accumulate = TRUE)
}
repeatedCosPlusOne_vec <- function(first, len) {
iterFunc(first, len - 1, function(.) cos(. + 1))
}
repeatedCosPlusOne_vec
कैकसप्लसओने_वीच को repeatedCosPlusOne
बारप्लसओप्लेन के "वैश्वीकरण" के रूप में माना जा सकता है। हालांकि, यह 2 के कारक से धीमा होने की उम्मीद की जा सकती है:
library(microbenchmark)
microbenchmark(
repeatedCosPlusOne(1, 1e4),
repeatedCosPlusOne_vec(1, 1e4)
)
#> Unit: milliseconds
#> expr min lq mean median uq max neval cld
#> repeatedCosPlusOne(1, 10000) 8.349261 9.216724 10.22715 10.23095 11.10817 14.33763 100 a
#> repeatedCosPlusOne_vec(1, 10000) 14.406291 16.236153 17.55571 17.22295 18.59085 24.37059 100 b