Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
R 加速代码:减少';用户';时间_R_Performance_Rcpp - Fatal编程技术网

R 加速代码:减少';用户';时间

R 加速代码:减少';用户';时间,r,performance,rcpp,R,Performance,Rcpp,我有一个我将大量调用的函数(对于一些不同的实验,在优化中每次迭代大约10^11次)。我已经实现了一个快速版本,但我很难看到如何提高性能。“系统”时间很短,用户时间很长 这是代码,它接受一个整数并返回一个向量,表示该整数是一个不同的基数计数系统(例如base=2表示二进制,base=10表示返回标准结果)。参数k给出了要返回的向量的长度,因此前面可能有许多零 正如您将看到的,这些函数运行需要5或7秒,但没有一个是系统时间。我想了解原因,以及是否有加快速度的方法。我对其他函数也有同样的问题(99%的

我有一个我将大量调用的函数(对于一些不同的实验,在优化中每次迭代大约10^11次)。我已经实现了一个快速版本,但我很难看到如何提高性能。“系统”时间很短,用户时间很长

这是代码,它接受一个整数并返回一个向量,表示该整数是一个不同的基数计数系统(例如base=2表示二进制,base=10表示返回标准结果)。参数k给出了要返回的向量的长度,因此前面可能有许多零

正如您将看到的,这些函数运行需要5或7秒,但没有一个是系统时间。我想了解原因,以及是否有加快速度的方法。我对其他函数也有同样的问题(99%的时间花在一个循环中的一个函数上,但将其加速200倍只会使运行时间减少一半),但为了清晰起见,我展示了这个函数

library(Rcpp)
library(microbenchmark)

# Rcpp version of the function
cppFunction('
NumericVector convert10tobase_c(double number_b10, int k, int base = 4){
    if(number_b10 >= pow(base, k)){
        stop("k is not large enough to contain the number in this base");
    }
    NumericVector ret(k);
    if(k == 1){
        return number_b10;
    }
    for (int i = 1 ;i < k; i++){
        double entry = floor(number_b10 / pow(base, (k - i)));
        ret[i-1] = entry;
        number_b10 = number_b10 - entry * pow(base, (k - i));
    }
    ret[k-1] = number_b10;
    return ret;
}')

# R version of the function
convert10tobase <- function(number_b10, k, base = 5){
    if(number_b10 >= base ^ k){
        stop("k is not large enough to contain the number in this base")
    }
    ret <- rep(0, k)
    if(k == 1){
        return(number_b10)
    }
    for (i in 1:(k - 1)){
        entry <- floor(number_b10 / base^(k-i))
        ret[i] <- entry
        number_b10 <- number_b10 - entry * (base ^ (k - i))
    }
    ret[k] <- number_b10
    return(ret)
}

# generate test data one hundred, thousand and million integers
set.seed(1)
base <- 4
k <- 8
ints_short <- floor(runif(1e2) * (base^k))
ints_long <- floor(runif(1e4) * (base^k))
ints_v_long <- floor(runif(1e6) * (base^k))

# benchmark the Rcpp version
microbenchmark(
    one = convert10tobase_c(ints_short[1], k, base),
    hundred = sapply(1:length(ints_short), function(i) convert10tobase_c(ints_short[i], k, base)),
    ten_thous = sapply(1:length(ints_long), function(i) convert10tobase_c(ints_long[i], k, base)),
    times = 100)

# test R and Rcpp times
r_start <- proc.time()
t <- sapply(1:length(ints_v_long), function(i) convert10tobase(ints_v_long[i], k, base))
r_stop <- proc.time()

c_start <- proc.time()
t <- sapply(1:length(ints_v_long), function(i) convert10tobase_c(ints_v_long[i], k, base))
c_stop <- proc.time()

# results - little time in 'system'
r_stop - r_start
c_stop - c_start
库(Rcpp)
图书馆(微基准)
#函数的Rcpp版本
CPP函数('
数字向量转换器10tobase_c(双倍数字b10,整数k,整数基=4){
如果(数字=功率(基数,k)){
停止(“k不足以包含此基数中的数字”);
}
数值向量ret(k);
如果(k==1){
返回编号_b10;
}
对于(int i=1;iretR非常擅长对多个相似的事情高效地做同一件事。因此,如果您在做某件事之前将类似的事情组合在一起,那么您的代码会更加高效。这可能会有点棘手,尤其是当您来自另一个编码背景时

这里是一个解决方案,其中你的函数在R中被矢量化(不确定它如何与C++循环有关,可能是内部的)。它可能可以进一步优化,但对于每个数字,它比使用

sapply
快100倍。它返回一个矩阵,每个数字对应一行,每个条目对应一列。当一个数字大于
base^k
时,返回一行NA。在进一步处理输出时,可以很容易地识别这一行

convert10tobase_v <- function(number_b10, k, base = 5){
  orig_b10 <- number_b10 #save original for check after
  if(k == 1){
    return(number_b10)
  }
  #initialize matrix to store results
  ret <- matrix(0, ncol=k, nrow=length(number_b10))
  #tiny-forloop, won't influenc performance and makes
  #storing results/modifying number_b10 easier
  for (i in 1:(k - 1)){
    entry <- floor(number_b10 / base^(k-i))
    ret[,i] <- entry
    number_b10 <- number_b10 - entry * (base ^ (k - i))
  }
  ret[,k] <- number_b10
  ret[orig_b10 >= base ^ k,] <- NA #set 'too large' numbers to missing
  return(ret)
}

对函数进行矢量化,而不是对要检查的每个整数单独调用函数,可能会给您带来好处。重用
pow
值而不是计算两次,可能会带来一些小的好处。还要注意,通过注意
i
i+1
之间的“除以
base
”关系,可以减少求幂次数。大部分时间都花在调用函数的开销上。如果你用C++循环代替<代码> SpIs//Cube,你可以实现一个戏剧性的性能改进,即用@ HeloA指出的函数矢量化。如果太大的数字被返回为NA?@ Helka,我已经实现了你的建议,这确实是个问题,它确实是重复的函数调用,使它慢下来。感谢您的观察,这将对我有很大帮助。矢量化是一个C级
for
循环。感谢完整的示例,加上您和@Roland的评论,这对我帮助很大。不客气,感谢您提出的数据、代码和清晰的问题陈述。
Unit: microseconds
              expr        min          lq         mean     median          uq        max neval cld
        one_single     20.216     25.1910     31.94323     29.079     37.6310     58.469   100 a  
   hundred_singles   2217.461   2317.9145   2499.23338   2386.336   2498.4525   4436.476   100  b 
 ten_thous_singles 240467.874 246613.1635 253205.12598 249890.060 252432.2090 307050.155   100   c
             one_v     22.703     26.5910     33.09706     30.323     36.3875     62.823   100 a  
         hundred_v     53.181     56.9135     68.05703     61.889     75.5740    129.066   100 a  
       ten_thous_v   2641.359   2707.2920   2806.83843   2744.613   2827.9620   4645.160   100  b