分组是否在data.table 1.12.0中并行化?

时间:2019-01-28 02:10:41

标签: r data.table openmp

data.table v1.12.0的变更日志中,我注意到以下内容:

  

子集,排序和分组现在使用更多的并行性

我测试了是否可以加快分组速度,但是没有成功。我做了几个不同的测试,并且我总是得到相同的结果。分组实际上是并行的吗?也许我没有正确使用线程选项?如您所见,data.table已使用openmp进行了编译,否则setDTthread打印一条消息,告诉用户不支持openmp。这是我的一项测试的可复制示例。

library(data.table)

n = 5e6
k = 1e4

DT = data.table(x = runif(n), y = runif(n), grp = sample(1:k, n, TRUE))

# Any function not too fast
f = function(x,y) as.list(eigen(cov(cbind(x,y)), only.values = TRUE)$value)

setDTthreads(1)
getDTthreads()
#> [1] 1

system.time(DT[ , f(x,y), by = grp])
#> utilisateur     système      écoulé 
#>       3.365       0.008       3.374

setDTthreads(0)
getDTthreads(T)
#> omp_get_max_threads() = 4
#> omp_get_thread_limit() = 2147483647
#> DTthreads = 0
#> RestoreAfterFork = true
#> [1] 4

system.time(DT[ , f(x,y), by = grp])
#> utilisateur     système      écoulé 
#>       3.324       0.029       3.238

reprex package(v0.2.1)于2019-01-27创建

2 个答案:

答案 0 :(得分:4)

是的,分组在v 1.12.0中是并行的

您的基准测试有点麻烦。如果要隔离分组速度,则需要 fast f(x, y)。使用示例的基数,但使用一个简单的函数,我们得到:

library(data.table)
  packageVersion("data.table")
#> [1] '1.12.0'

n = 5e6
N <- n
k = 1e4

print(getDTthreads())
#> [1] 12

DT = data.table(x = rep_len(runif(n), N),
                y = rep_len(runif(n), N),
                grp = rep_len(sample(1:k, n, TRUE), N))
bench::system_time(DT[, .(a = 1L), by = "grp"])
#>   process      real 
#> 250.000ms  72.029ms

setDTthreads(1)

bench::system_time(DT[, .(a = 1L), by = "grp"])
#>   process      real 
#> 125.000ms 126.385ms

reprex package(v0.2.1)于2019-02-01创建

也就是说,在并行情况下,我们的速度稍快一些,但仅增加了约50毫秒,与函数的3 s相比可以忽略不计。

如果我们扩大DT的大小,我们会发现更大的差异:

library(data.table)
  packageVersion("data.table")
#> [1] '1.12.0'

n = 5e6
N <- 1e9
k = 1e4

print(getDTthreads())
#> [1] 12

DT = data.table(x = rep_len(runif(n), N),
                y = rep_len(runif(n), N),
                grp = rep_len(sample(1:k, n, TRUE), N))
bench::system_time(DT[, .(a = 1L), by = "grp"])
#> process    real 
#> 45.719s 14.485s

setDTthreads(1)

bench::system_time(DT[, .(a = 1L), by = "grp"])
#> process    real 
#> 24.859s 24.890s
sessioninfo::session_info()
#> - Session info ----------------------------------------------------------
#>  setting  value                       
#>  version  R version 3.5.2 (2018-12-20)
#>  os       Windows 10 x64              
#>  system   x86_64, mingw32             
#>  ui       RTerm                       
#>  language (EN)                        
#>  collate  English_Australia.1252      
#>  ctype    English_Australia.1252      
#>  tz       Australia/Sydney            
#>  date     2019-02-01                  
#> 

reprex package(v0.2.1)于2019-02-01创建

答案 1 :(得分:1)

根据我对github存储库中的data.table问题的探索,这里的答案没有得到任何权威机构(例如data.table团队的成员)的验证。

从问题#3042开始,我了解summean已优化。我们可以对其进行基准测试以验证其正确性:

library(data.table)
n = 1e7 ; k = 1e5
DT = data.table(x = runif(n), y = runif(n), grp = sample(1:k, n, TRUE))

setDTthreads(1)
system.time(DT[ , mean(x), by = grp]) #> 0.8 s
setDTthreads(0)
system.time(DT[ , mean(x), by = grp]) #> 0.4 s

但是在同一期#3042中,马特·道尔(Matt Dowle)写道:

  

要扩展到其他gforce函数和对任意函数进行分组还有很多工作要做

sritchie73在#3130中写道

  

这里值得一提的是R函数本质上不是线程安全的,例如因此无法通过Rcpp将它们传递给多线程C ++代码。

因此,看来用户定义函数的并行化并非一件容易的事,data.table中目前没有实现。