为什么range()函数比min和max的组合慢?

Jon*_*nas 10 performance r range max min

我遇到了R的range功能。它肯定是有用的工具,并使代码更具可读性,但是通过将其替换为简单的单行代码包括min和可以使其速度提高一倍max

我做了一些基准测试,范围函数的“糟糕”表现令我感到惊讶。为了进行比较,我编写了一个名为range2min和max 的函数(请参见代码)。除了速度之外,如果可以通过简单的单线(也易于阅读)来执行此功能,那么是否有任何原因为什么存在此功能?

require(microbenchmark)

range2 <- function(x) c(min(x),max(x))  

n <- 1000000
x <- rnorm(n)
microbenchmark(range(x), range2(x))
#Unit: milliseconds
#  expr      min       lq     mean   median       uq     max neval cld
# range(x) 4.696101 4.734751 5.321603 4.796301 4.814751 23.0646   100   b
#range2(x) 2.477602 2.516101 2.542540 2.535051 2.544052  3.7636   100  a 

n <- 10000000
x <- rnorm(n)
microbenchmark(range(x), range2(x))
# Unit: milliseconds
#  expr     min      lq     mean   median       uq      max neval cld
# range(x) 47.3246 47.9498 58.27992 55.25795 61.98205 146.5100   100   b
#range2(x) 24.7063 25.5021 25.59192 25.55245 25.63515  27.1088   100  a
Run Code Online (Sandbox Code Playgroud)

可以肯定的是,这并不是要摆脱的第一个瓶颈,因为我们正在谈论的是具有10,000,000个条目的向量的毫秒数,但是我希望range速度会更快。我的直觉是:

range一次遍历数据并同时搜索最大值和最小值,而我的range2函数两次遍历数据:一次查找最小值,一次查找最大值。

也许有人可以提供有关实施的背景信息。也许原因是minmax是用C实现的,range不是吗?

另外:我已经和我的一个朋友谈论过这个问题,他只是通过以下方式在C ++中实现了该功能,从而使该功能更快:

#include <Rcpp.h>
#include <float.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector range3(NumericVector x) {
  int xs = x.size();
  double minValue = FLT_MAX;
  double maxValue = FLT_MIN;
  for (int i =0; i < xs; i++) {
    if (x[i] < minValue) minValue = x[i];
    if (x[i] > maxValue) maxValue = x[i];
  }
  Rcpp::NumericVector result(2);
  result[0] = minValue;
  result[1] = maxValue;
  return result;
}
Run Code Online (Sandbox Code Playgroud)

这提供了以下基准:

n <- 10000000
x <- rnorm(n)
microbenchmark(range(x), range2(x) ,range3(x))
#Unit: milliseconds
#      expr     min       lq     mean  median       uq      max neval cld
#  range(x) 47.8583 48.30355 58.12575 55.3135 62.10295 149.9648   100   c
# range2(x) 24.8211 25.53615 25.90920 25.6176 25.79175  42.4659   100  b 
# range3(x) 13.2458 13.30385 13.47175 13.3797 13.65410  14.3487   100 a
Run Code Online (Sandbox Code Playgroud)

MrF*_*ick 3

range.default这是(运行 R 3.6.1)的源代码

 > range.default
function (..., na.rm = FALSE, finite = FALSE) 
{
    x <- c(..., recursive = TRUE)
    if (is.numeric(x)) {
        if (finite) 
            x <- x[is.finite(x)]
        else if (na.rm) 
            x <- x[!is.na(x)]
        c(min(x), max(x))
    }
    else {
        if (finite) 
            na.rm <- TRUE
        c(min(x, na.rm = na.rm), max(x, na.rm = na.rm))
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到它在调用自身之前做了一些额外的检查c(min(x), max(x))。它没有针对速度进行优化。这只是一个用户友好的功能。这些毫秒差异似乎不太可能成为性能瓶颈的根源。