Performance R中数据的快速边界

Performance R中数据的快速边界,performance,r,data.table,bigdata,rcpp,Performance,R,Data.table,Bigdata,Rcpp,假设我有一个向量,vec,它很长(从1E8条目开始),并且希望将它绑定到范围[a,b]。我当然可以编码vec[vecb]=b,但这需要对数据进行两次传递,并为临时指示符向量分配一个大的RAM(~800MB,两次)。这两个过程都会消耗时间,因为如果我们只将数据从主存复制到本地缓存一次,我们可以做得更好(对主存的调用是错误的,缓存未命中也是错误的)。谁知道多线程可以改善多少,但我们不要贪心。:) base R或我忽略的某个包中是否有一个很好的实现,或者这是Rcpp(或我的老朋友data.table)

假设我有一个向量,
vec
,它很长(从1E8条目开始),并且希望将它绑定到范围
[a,b]
。我当然可以编码
vec[vec
vec[vec>b]=b
,但这需要对数据进行两次传递,并为临时指示符向量分配一个大的RAM(~800MB,两次)。这两个过程都会消耗时间,因为如果我们只将数据从主存复制到本地缓存一次,我们可以做得更好(对主存的调用是错误的,缓存未命中也是错误的)。谁知道多线程可以改善多少,但我们不要贪心。:)


base R或我忽略的某个包中是否有一个很好的实现,或者这是Rcpp(或我的老朋友
data.table
)的工作?

只是开始:您的解决方案和
pmin
/
pmax
解决方案之间没有太大区别(因为我不耐烦,尝试使用n=1e7而不是n=1e8)--
pmin
/
pmax
实际上稍微慢一点

fun1 <- function(x,a,b) {x[x<a] <- a; x[x>b] <- b; x}
fun2 <- function(x,a,b) pmin(pmax(x,a),b)
library(rbenchmark)
z <- runif(1e7)

benchmark(fun1(z,0.25,0.75),fun2(z,0.25,0.75),rep=50)

                 test replications elapsed relative user.self sys.self
1 fun1(z, 0.25, 0.75)           10  21.607  1.00000     6.556   15.001
2 fun2(z, 0.25, 0.75)           10  23.336  1.08002     5.656   17.605
fun1一个简单的C解决方案是

library(inline)

fun4 <-
    cfunction(c(x="numeric", a="numeric", b="numeric"), body4,
              language="C")
body4 <- "
    R_len_t len = Rf_length(x);
    SEXP result = Rf_allocVector(REALSXP, len);
    const double aa = REAL(a)[0], bb = REAL(b)[0], *xp = REAL(x);
    double *rp = REAL(result);

    for (int i = 0; i < len; ++i)
        if (xp[i] < aa)
            rp[i] = aa;
        else if (xp[i] > bb)
            rp[i] = bb;
        else
            rp[i] = xp[i];

    return result;
"
fun4 <-
    cfunction(c(x="numeric", a="numeric", b="numeric"), body4,
              language="C")
和基准

> z <- runif(1e7)
> benchmark(fun1(z,0.25,0.75), fun4(z, .25, .75), fun5(z, .25, .75),
+           replications=10)
                 test replications elapsed  relative user.self sys.self
1 fun1(z, 0.25, 0.75)           10   9.087 14.609325     8.335    0.739
2 fun4(z, 0.25, 0.75)           10   1.505  2.419614     1.305    0.198
3 fun5(z, 0.25, 0.75)           10   0.622  1.000000     2.156    0.320
  user.child sys.child
1          0         0
2          0         0
3          0         0
> identical(res1 <- fun1(z,0.25,0.75), fun4(z,0.25,0.75))
[1] TRUE
> identical(res1, fun5(z, 0.25, 0.75))
[1] TRUE
>z基准测试(fun1(z,0.25,0.75),fun4(z,25,75),fun5(z,25,75),
+复制次数=10次)
相对user.self sys.self测试复制已用时间
1 fun1(z,0.25,0.75)109.08714.6093258.3350.739
2 fun4(z,0.25,0.75)101.5052.419614 1.305 0.198
3 fun5(z,0.25,0.75)10 0.622 1.0000002.156 0.320
user.child sys.child
1          0         0
2          0         0
3          0         0
>相同(res1相同(res1,fun5(z,0.25,0.75))
[1] 真的

在我的四核笔记本电脑上。假设数字输入,没有错误检查,NA处理等。有趣的是。我希望这会更快,但似乎没有这样的运气。
fun2
在R版本2.15.0补丁(2012-05-01 r59304)平台上对我来说大约快20%:x86_64-unknown-linux-gnu(64位)用CFLAGS=-O0编译;hack
.Internal(pmin(FALSE,x,a))
etc比
fun1
+1快30%左右,我想在核心R中使用这个函数,叫做
clamp(x,low,high)
…人们总是希望OpenMP使用+1,对吧;-),但我认为需要修改PKG\u CFLAGS etc来获得
-fopenmp
。或者您是在其他地方这样做的,例如在
~/.R/Makevars
?@DirkEddelbuettel R的configure.ac检测OpenMP
-fopenmp
在R_HOME/etc/Makeconf中设置。不在我的机器上,我从您的示例中得到
警告:忽略35; pragma omp parallel[-Wunknown pragmas]
。尽管我在
/etc/R/Makeconf
中有
-fopenmp
(这是一个指向
R\u HOME
下面位置的符号链接)。@DirkEddelbuettel是的,你是对的,~/.R/Makevars包含
CFLAGS=-fopenmp
> z <- runif(1e7)
> benchmark(fun1(z,0.25,0.75), fun4(z, .25, .75), fun5(z, .25, .75),
+           replications=10)
                 test replications elapsed  relative user.self sys.self
1 fun1(z, 0.25, 0.75)           10   9.087 14.609325     8.335    0.739
2 fun4(z, 0.25, 0.75)           10   1.505  2.419614     1.305    0.198
3 fun5(z, 0.25, 0.75)           10   0.622  1.000000     2.156    0.320
  user.child sys.child
1          0         0
2          0         0
3          0         0
> identical(res1 <- fun1(z,0.25,0.75), fun4(z,0.25,0.75))
[1] TRUE
> identical(res1, fun5(z, 0.25, 0.75))
[1] TRUE