R 如何在不复制的情况下计算时间差

R 如何在不复制的情况下计算时间差,r,datetime,dispatch,R,Datetime,Dispatch,我想计算两个POSIXct对象之间的秒差,而不复制它们。这应该不是问题,因为它们存储为UNIX时间numericUNIX时间,所以只需要简单的减法运算。问题是-操作符根据对象的类进行调度,并调用difftime。difftime函数将每个输入向量复制两次: > a <- as.POSIXct(runif(1e6, 0, 1000), origin = '1970-01-01') > b <- as.POSIXct(runif(1e6, 0, 1000), origin =

我想计算两个POSIXct对象之间的秒差,而不复制它们。这应该不是问题,因为它们存储为UNIX时间
numeric
UNIX时间,所以只需要简单的减法运算。问题是
-
操作符根据对象的类进行调度,并调用
difftime
difftime
函数将每个输入向量复制两次:

> a <- as.POSIXct(runif(1e6, 0, 1000), origin = '1970-01-01')
> b <- as.POSIXct(runif(1e6, 0, 1000), origin = '1970-01-01')
> a_trace <- tracemem(a)
> b_trace <- tracemem(b)
> z <- a - b
tracemem[0x000000004c082470 -> 0x000007fff54e0010]: difftime -.POSIXt 
tracemem[0x000007fff8c80010 -> 0x000007ffe9490010]: difftime -.POSIXt 
tracemem[0x000007ffe9490010 -> 0x000007ffe8530010]: structure .difftime difftime -.POSIXt 
tracemem[0x000007ffe8530010 -> 0x000007ffe7d80010]: structure .difftime difftime -.POSIXt 
此外,生成的对象是类
difftime
,而不是普通的
numeric
。使用base R,需要添加结果副本以消除
difftime
类:

> z_trace <- tracemem(z)
> class(z) <- NULL
tracemem[0x000007ffb28e0010 -> 0x000007ffb2130010]:
这样可以避免复制,而且速度更快:

> microbenchmark::microbenchmark(fast_difftime(a, b), as.numeric(difftime(a, b, units = "secs")))
Unit: milliseconds
                                       expr      min        lq     mean    median        uq      max neval cld
                        fast_difftime(a, b) 1.728555  4.213836  5.97520  4.392592  6.365763 127.1690   100  a 
 as.numeric(difftime(a, b, units = "secs")) 6.643092 19.352806 24.54938 19.861066 23.298505 137.0776   100   b

然而,我不喜欢这样的事实,我必须修改输入向量的属性,只是为了避免方法分派。有更好的方法吗?

Rcpp是一个选项,因为您可以忽略class属性:

library(Rcpp)
cppFunction(
  'NumericVector mydiff(const NumericVector x, const NumericVector y) {
       return x - y;
   }
  ')


microbenchmark::microbenchmark(fast_difftime(a, b), mydiff(a, b))
#Unit: milliseconds
#                expr      min       lq     mean   median       uq      max neval cld
# fast_difftime(a, b) 2.248841 2.291861 3.489386 2.326559 2.379951 46.69430   100   a
#        mydiff(a, b) 2.165105 2.209661 3.089114 2.229380 2.272144 10.96047   100   a

z
-
不是正常的S3泛型。如果您不能直接调用S3方法,也不能对numerics方法执行此操作,则无法避免方法分派。您的解决方案将复制
a
b
和生成的向量
unclass(a)-unclass(b)
每次一次。通过使用
as.numeric
而不是
unclass
,您可以放弃对
c
的调用。但是对于输入向量,我们仍然是一个副本。我没有看到
c
的副本。啊,糟了。这比
setattr
骗局简单得多。我现在意识到我不应该把两个问题纠缠在一起:(1)如何在不复制的情况下减去日期,(2)如果可能的话,如何使用原语控制调度)。但这是(1)的一个很好的答案。我将对问题进行编辑,只关注(1)。
> microbenchmark::microbenchmark(fast_difftime(a, b), as.numeric(difftime(a, b, units = "secs")))
Unit: milliseconds
                                       expr      min        lq     mean    median        uq      max neval cld
                        fast_difftime(a, b) 1.728555  4.213836  5.97520  4.392592  6.365763 127.1690   100  a 
 as.numeric(difftime(a, b, units = "secs")) 6.643092 19.352806 24.54938 19.861066 23.298505 137.0776   100   b
library(Rcpp)
cppFunction(
  'NumericVector mydiff(const NumericVector x, const NumericVector y) {
       return x - y;
   }
  ')


microbenchmark::microbenchmark(fast_difftime(a, b), mydiff(a, b))
#Unit: milliseconds
#                expr      min       lq     mean   median       uq      max neval cld
# fast_difftime(a, b) 2.248841 2.291861 3.489386 2.326559 2.379951 46.69430   100   a
#        mydiff(a, b) 2.165105 2.209661 3.089114 2.229380 2.272144 10.96047   100   a