R 如何在不复制的情况下计算时间差
我想计算两个POSIXct对象之间的秒差,而不复制它们。这应该不是问题,因为它们存储为UNIX时间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 =
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