Data.table: 加入操作几乎慢了 2 倍

创建于 2019-10-02  ·  29评论  ·  资料来源: Rdatatable/data.table

你好,我在测试
data.table_1.10.4-3 + R 版本 3.4.0 (2017-04-21)
对比
data.table_1.12.2 + R 版本 3.6.1 (2019-07-05)

并注意到新版本 data.table (R?) 中的连接操作几乎慢了 2 倍
我认为主要取决于 data.table 的版本

rm(list=ls())

library(data.table)
library(tictoc)


aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

#aa[,  a1 := as.character(a)]
#bb[,  a1 := as.character(a)]

setindex(aa, a)
setindex(bb, a)

tic()
for(i in c(1:1000)) {
 # aa[bb, b := i.b, on=.(a, a1)] # test1
  aa[bb, b := i.b, on=.(a)] # test2
}
toc()

# test1
# 3.6.1: 5.5 sec with index
# 3.6.1: 6.87 sec without index
# 3.4.0: 3.02 sec (no index)

# test2
# 3.6.1: 3.82 sec with index
# 3.6.1: 4.44 sec without index
# 3.4.0: 2.48 sec (no index)
High joins performance regression

所有29条评论

已运行以下命令:

# run_join_time.sh
echo 'hash,time' > 'join_timings.csv'

for HASH in $(git rev-list --ancestry-path 1.10.0..1.12.4 | awk 'NR == 1 || NR % 10 == 0');
do git checkout $HASH && R CMD INSTALL . && Rscript time_join.R $HASH;
done
# time_join.R
library(data.table)
library(microbenchmark)

hash = commandArgs(trailingOnly = TRUE)

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

fwrite(data.table(
  hash = hash,
  time = microbenchmark(times = 1000L, aa[bb, b := i.b, on=.(a)])$time
), 'join_timings.csv', append = TRUE)

然后执行以下操作来创建此图

Screenshot 2019-10-03 at 6 59 11 PM

library(data.table)
times = fread('join_timings.csv')

times[ , date := as.POSIXct(system(sprintf("git show --no-patch --no-notes --pretty='%%cd' %s", .BY$hash), intern = TRUE), by = hash]
times[ , date := as.POSIXct(date, format = '%a %b %d %T %Y %z', tz = 'UTC')]
times[ , date := as.IDate(date)]
setorder(times, date)

tags = system('
for TAG in $(git tag);
  do git show --no-patch --no-notes --pretty="$TAG\t%cd" $TAG;
done
', intern = TRUE)

tags = setDT(tstrsplit(tags, '\t'))
setnames(tags, c('tag', 'date'))
tags[ , date := as.POSIXct(date, format = '%a %b %d %T %Y %z', tz = 'UTC')]
tags[ , date := as.IDate(date)]

times[ , {
  par(mar = c(7.1, 4.1, 2.1, 2.1))
  y = boxplot(I(time/1e6) ~ date, log = 'y', notch = TRUE, pch = '.',
              las = 2L, xlab = '', ylab = 'Execution time (ms)', cex.axis = .8)
  x = unique(date)
  idx = findInterval(tags$date, x)
  abline(v = idx[idx>0L], lty = 2L, col = 'red')
  text(idx[idx>0L], .7*10^(par('usr')[4L]), tags$tag[idx>0L], 
       col = 'red', srt = 90)
  NULL
}]


开始后我意识到我可以通过使用来简化这一点

echo 'hash,time,date' > 'join_timings.csv'

for HASH in $(git rev-list --ancestry-path 1.10.0..1.12.4 | awk 'NR == 1 || NR % 10 == 0');
do git checkout $HASH && R CMD INSTALL . --preclean && Rscript time_join.R $HASH $(git show --no-patch --no-notes --pretty='%cd' $HASH);
done
library(data.table)
library(microbenchmark)

args = commandArgs(trailingOnly = TRUE)
hash = args[1L]
date = as.POSIXct(args[2L], format = '%a %b %d %T %Y %z', tz = 'UTC'))

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

fwrite(data.table(
  hash = hash,
  date = date,
  time = microbenchmark(times = 1000L, aa[bb, b := i.b, on=.(a)])$time
), 'join_timings.csv', append = TRUE)

将提交时间直接添加到基准测试中

join_timings.txt

嗨 MichaelChirico,我已经读过两遍了
但从 2016 年到 2019 年的时间表到底是什么意思? 它是数据表的版本吗?

不仅是版本,还有特定的提交。 让我尝试添加一些版本划分,一个好主意,我忘了添加

@MichaelChirico您的图表看起来非常酷,缺少的是对此的解释:) 通过猜测,我会说新的并行 forder 对于长度为 100 的输入比以前慢。 哪个 IMO 很好,因为这些仍然是微秒。 减少开销当然是值得的,但优先级应该是在最常见的使用场景中减少资源。 在长度为 100 的输入上循环 10000 次绝对不是其中之一。

在查看 1.12.0 新闻后,这也是我的猜测。 将尝试重新运行单线程。

@jangorecki ,如果你做简单的数学运算,它是数千微秒,即毫秒
这个测试的灵感是我当前的项目,我加入了 7-8 次表,有 40 000 行
之前需要 18 秒,之后需要 40 秒
我的测试还包括字符键
我安装了 3.6+1.12 然后回滚到 3.4+1.10
我认为这不是正常情况,这就是为什么这个问题在这里

是的,图表真的很棒; 我们应该做更多的事情并自动化它。

但原始报告将 R 3.4 与 R 3.6 进行了比较,R 3.5 在中间。 我们知道 R 3.5 通过 ALTREP 将 REAL()、INTEGER() 从宏更改为(稍微重一些)函数来影响 data.table。

@vk111请您现在在 CRAN 上使用 v1.12.4 重新运行您的测试。 好点? 我们一直在将 R API 置于循环之外以应对 R 3.5+。 作为下一步,很高兴知道您如何找到 v1.12.4。 您能否还包括有关您的机器的更多信息? 例如操作系统、RAM 和 CPU 数量。

@matt ,会的

@mattdowle

我已经更新到 1.12.4 (+R 3.6.1) - 单元测试和我的函数基本上有相同的数字
我会在这里考虑只加入没有索引的字符 - 作为基准
单元测试(如第一篇文章中所述)
3.6.1:6.87 秒无索引
3.4.0:3.02 秒(无索引)

我的项目功能保持不变:20 对 40 秒
它仅包含 ~14 dt 连接
两个表 30 000 行 (dt1) 和 366 000 行 (dt2)
每列 15 列
键是 2 个字符和 1 个数字
并在连接期间分配了 7 个字段(从 dt1 到 dt2)

我的电脑:
赢10 64位
AMD A8 2.2 GHz
8 Gb 内存

cmd: wmic cpu get NumberOfCores, NumberOfLogicalProcessors/格式:列表
核心数=4
NumberOfLogicalProcessors=4

谢谢你的分析

@mattdowle ,你在考虑这个吗?

根据我刚刚重新运行的git bisect ,这个提交对于减速是“有罪的”,希望这可以帮助马特确定根源。 它与forder相关,这让我相对有信心我们已经正确地确定了它。

https://github.com/Rdatatable/data.table/commit/e59ba149f21bb35098ed0b527505d5ed6c5cb0db

更确切地说:

# run_join_time.sh
R CMD INSTALL . && Rscript time_join.R
# time_join.R
library(data.table)
library(microbenchmark)

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

summary(microbenchmark(times = 1000L, aa[bb, b := i.b, on=.(a)])$time/1e6)

使用这两个脚本,来自master/HEAD

git checkout 1.12.0
git bisect start
git bisect bad
git bisect good 1.11.8

然后在每次迭代中,运行sh run_join_time.sh并检查结果。 bad提交在我的机器上每次迭代大约需要 0.9 秒, good提交大约 0.7 秒。

嗨迈克尔,谢谢你,但性能已经急剧下降,尤其是
对于字符键-我在第一篇文章中提供了单元测试
无论如何,我认为我们需要等待马特确认它是否已修复
新版本与否

2019 年 12 月 26 日星期四 15:02,Michael Chirico [email protected]
写道:

根据我刚刚重新运行的 git bisect ,这个提交是“有罪”的
放慢速度,希望这可以帮助马特确定根源。 那就是
与 forder 相关的事情让我相对有信心我们已经确定了它
正确。

e59ba14
https://github.com/Rdatatable/data.table/commit/e59ba149f21bb35098ed0b527505d5ed6c5cb0db

更确切地说:

run_join_time.sh

R CMD 安装。 && Rscript time_join.R

time_join.R

库(数据表)
库(微基准)

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

摘要(微基准(次 = 1000L,aa[bb,b := ib,on=.(a)])$time/1e6)

使用这两个脚本,来自 master/HEAD:

混帐结帐 1.12.0
git bisect 开始
git bisect 坏
git bisect 好 1.11.8

然后在每次迭代时,运行 sh run_join_time.sh 并检查结果。 这
错误的提交在我的机器上每次迭代大约需要 0.9 秒,
大约 0.7 的良好提交。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928?email_source=notifications&email_token=ABRM7MJQITNUZDNYV7NTIX3Q2S2ORA5CNFSM4I4ZPREKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHVTR4Q#issuecomment-569063
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MOTZRFFM2QKVFUHT6LQ2S2ORANCNFSM4I4ZPREA
.

>

最好的祝福,
瓦西里·库兹涅佐夫

@vk111如果此问题尚未解决,那么您可以放心地假设问题尚未解决。 每当我们解决某些问题时,我们都会关闭相关问题。

@vk111您可以使用setkey而不是setindex进行一些小的调整来缓解。 这将您在我的计算机上的测试减少了一半以上:

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

aa[,  a1 := as.character(a)]
bb[,  a1 := as.character(a)]

tic()
for(i in c(1:1000)) {
  aa[bb, b := i.b, on=.(a, a1)] # test1 without key
}
toc()
## 4 sec elapsed

tic()
setkey(aa, a, a1)
setkey(bb, a, a1)

for(i in c(1:1000)) {
  aa[bb, b := i.b] # test2 without key
}
toc()
## 1.46 sec elapsed

image

library(microbenchmark)
library(data.table)

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

aa[,  a1 := as.character(a)]
bb[,  a1 := as.character(a)]

aa_nokey = copy(aa)
bb_nokey = copy(bb)

tic()
aa_int_key = setkey(copy(aa), a)
bb_int_key = setkey(copy(bb), a)
toc()

tic()
aa_int_char = setkey(copy(aa), a, a1)
bb_int_char = setkey(copy(bb), a, a1)
toc()
int_nokey = function(dt1, dt2){
  dt1[dt2, b := i.b, on=.(a)]
}

int_key = function(dt1, dt2){
  dt1[dt2, b := i.b]
}

int_char_nokey = function(dt1, dt2){
  dt1[dt2, b := i.b, on=.(a, a1)]
}

int_char_key = function(dt1, dt2){
  dt1[dt2, b := i.b]
}

int_char_key_on = function(dt1, dt2){
  dt1[dt2, b := i.b, on=.(a, a1)]
}

boxplot(
  microbenchmark(
    a_no_key = int_nokey(aa, bb),
    a_key = int_key(aa_int_key, bb_int_key),
    a_a1_no_key = int_char_nokey(aa, bb),
    a_a1_key = int_char_key(aa_int_char, bb_int_char),
    a_a1_key_on = int_char_key_on(aa_int_char, bb_int_char)
  )
)

嗨,科尔,是的,我知道 setkey - 我在简短描述上方提供了
我的项目中没有 1000 次加入的目的 - 有 10-20 次
加入,问题是我的字符串非常动态,我
每次加入前都需要设置setkey,比如20次setkey和20次
次加入,因此时间增益小于 2 倍
作为这种情况的一般解决方法,我使用 3.4.0 一段时间
在新版本中修复它可能没什么大不了的,特别是如果
发现有罪
顺便说一句,如果我们将苹果与苹果进行比较,我认为 3.4(1.10) 将是
如果我对 setkey 使用相同的单元测试,仍然比 3.6(1.12) 快
两个版本
节日快乐

在 2019 年 12 月 29 日星期日 14:48 Cole Miller, notifications @github.com 写道:

@vk111 https://github.com/vk111你也许可以减轻一些
使用 setkey 而不是 setindex 的小调整。 这将您的测试减少了
一半以上在我的电脑上:

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

aa[, a1 := as.character(a)]
bb[, a1 := as.character(a)]

抽动()
for(i in c(1:1000)) {
aa[bb, b := ib, on=.(a, a1)] # test1 没有密钥
}
目录()

4 秒过去

抽动()
设置键(aa,a,a1)
setkey(bb, a, a1)

for(i in c(1:1000)) {
aa[bb, b := ib] # test2 没有密钥
}
目录()

1.46 秒过去

[图片:图片]
https://user-images.githubusercontent.com/57992489/71557665-126d7880-2a17-11ea-8664-21a10deb47ca.png

库(微基准)
库(数据表)

aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))

aa[, a1 := as.character(a)]
bb[, a1 := as.character(a)]

aa_nokey = 复制(aa)
bb_nokey = 复制(bb)

抽动()
aa_int_key = setkey(复制(aa),a)
bb_int_key = setkey(复制(bb), a)
目录()

抽动()
aa_int_char = setkey(复制(aa),a,a1)
bb_int_char = setkey(复制(bb), a, a1)
目录()
int_nokey = 函数(dt1,dt2){
dt1[dt2, b := ib, on=.(a)]
}

int_key = 函数(dt1,dt2){
dt1[dt2, b := ib]
}

int_char_nokey = 函数(dt1,dt2){
dt1[dt2, b := ib, on=.(a, a1)]
}

int_char_key = 函数(dt1,dt2){
dt1[dt2, b := ib]
}

int_char_key_on = 函数(dt1,dt2){
dt1[dt2, b := ib, on=.(a, a1)]
}

箱形图(
微基准(
a_no_key = int_nokey(aa, bb),
a_key = int_key(aa_int_key, bb_int_key),
a_a1_no_key = int_char_nokey(aa, bb),
a_a1_key = int_char_key(aa_int_char, bb_int_char),
a_a1_key_on = int_char_key_on(aa_int_char, bb_int_char)
)
)


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928?email_source=notifications&email_token=ABRM7MPU64YBK7UNT3O3OYDQ3CTBRA5CNFSM4I4ZPREKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHY73VA#issuecomment-569
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MKSP6LCUJD2HOAG7D3Q3CTBRANCNFSM4I4ZPREA
.

有一个相关的待处理 PR #4370。 它可以使用[通常施加的更少开销进行连接。 它将示例的时间减少了 2 倍。数据越大,改进越小,但另一方面,迭代越多,改进越好。 请注意,在加入期间无法更新列。 您基本上必须在连接期间添加列,然后使用类似fcoalesce的东西,但这应该是高性能的。
一旦 #4386 也将被合并,它应该会得到一些额外的加速。

library(data.table)
aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))
setindex(aa, a)
setindex(bb, a)
p = proc.time()[[3L]]
for (i in c(1:1000)) {
  aa[bb, b := i.b, on=.(a)]
}
proc.time()[[3L]]-p
#[1] 1.1

合并列表 #4370

library(data.table)
aa = data.table(a = seq(1,100), b = rep(0, 100))
bb = data.table(a = seq(1,100), b = rep(1, 100))
setindex(aa, a)
setindex(bb, a)
p = proc.time()[[3L]]
for (i in c(1:1000)) {
  mergelist(list(aa, bb), on="a")
}
proc.time()[[3L]]-p
#[1] 0.696

嗨,简,好的,谢谢

数据越大,改进越小,但另一方面,
迭代次数越多,改进越好
真正的问题与大数据和一些迭代有关——我写的
多于

2020 年 5 月 4 日星期一 00:33 Jan Gorecki, notifications @github.com 写道:

有一个待处理的 PR #4370
https://github.com/Rdatatable/data.table/pull/4370这是相关的。 它
可以用较少的开销进行连接 [ 通常强加。 它减少了
你的例子的时间是 2 倍。数据越大,越小
改进,但另一方面,迭代次数越多越好
改进。 请注意,在加入期间无法更新列。 你
基本上必须在加入期间添加列,然后使用类似的东西
fcoalesce,但这应该是高性能的。
它应该会得到一些额外的加速 #4386
https://github.com/Rdatatable/data.table/pull/4386也将被合并。

库(data.table)aa = data.table(a = seq(1,100), b = rep(0, 100))bb = data.table(a = seq(1,100), b = rep(1, 100))
设置索引(aa,a)
setindex(bb, a)p = proc.time()[[3L]]for (i in c(1:1000)) {
aa[bb, b := ib, on=.(a)]
}
proc.time()[[3L]]-p#[1] 1.1

合并列表 #4370 https://github.com/Rdatatable/data.table/pull/4370

库(data.table)aa = data.table(a = seq(1,100), b = rep(0, 100))bb = data.table(a = seq(1,100), b = rep(1, 100))
设置索引(aa,a)
setindex(bb, a)p = proc.time()[[3L]]for (i in c(1:1000)) {
合并列表(列表(aa,bb),on =“a”)
}
proc.time()[[3L]]-p#[1] 0.696


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928#issuecomment-623193573
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MJR5THRHE3FIXXV3YTRPXWNNNANCNFSM4I4ZPREA
.

我检查了#4440(昨天合并)是否解决了这个问题,但它看起来没有。 虽然我到达这里的时间与@vk111报告的时间有很大不同,所以如果你能够测试最近的开发版本,看看它是否对你的工作流程有任何帮助,这会有所帮助。
当您在 Windows 上时,还有另一个待处理的 PR 最终可能会帮助 #4558。


这是我在每个环境中运行的通用脚本:

library(data.table)
setDTthreads(2) ## vk111 has 4 cores
aa = data.table(a = seq(1,100), b = rep(0, 100))[,  a1 := as.character(a)]
bb = data.table(a = seq(1,100), b = rep(1, 100))[,  a1 := as.character(a)]
setindex(aa, a); setindex(bb, a)
cat("test1\n")
system.time({
for(i in c(1:1000)) {
  aa[bb, b := i.b, on=.(a, a1)] # test1
}})
cat("test2\n")
system.time({
for(i in c(1:1000)) {
  aa[bb, b := i.b, on=.(a)] # test2
}})

这些是环境:

docker run -it --rm r-base:3.4.0
install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.10.4-3.tar.gz", repos=NULL)
test1
   user  system elapsed 
  0.949   0.007   0.957 
test2
   user  system elapsed 
  0.864   0.000   0.864 
docker run -it --rm r-base:3.6.1
install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.12.2.tar.gz", repos=NULL)
test1
   user  system elapsed 
  2.598   0.047   1.350 
test2
   user  system elapsed 
  2.092   0.016   1.062 



md5-e82041eef8382e88332d077bb608b87c



docker run -it --rm r-base:3.6.1
install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
test1
   user  system elapsed 
  1.174   0.045   1.219 
test2
   user  system elapsed 
  0.981   0.024   1.004 

你好,我在 Windows

2020 年 6 月 26 日星期五,13:19 Jan Gorecki, notifications @github.com 写道:

我检查了 #4440 https://github.com/Rdatatable/data.table/pull/4440
解决了这个问题,但它看起来没有。 虽然我得到的时间
这里与@vk111 报告的时间有很大不同
https://github.com/vk111 ,所以如果你能够测试最近的开发
版本,看看它是否对您的工作流程有任何影响
有帮助。
这是我在每个环境中运行的通用脚本:

库(数据表)
setDTthreads(2) ## vk111 有 4 个 coresaa = data.table(a = seq(1,100), b = rep(0, 100))[, a1 := as.character(a)]bb = data.table(a = seq(1,100), b = rep(1, 100))[, a1 := as.character(a)]
设置索引(aa,a); 设置索引(bb, a)
猫(“test1n”)
system.time({for(i in c(1:1000)) {
aa[bb, b := ib, on=.(a, a1)] # test1
}})
猫(“test2n”)
system.time({for(i in c(1:1000)) {
aa[bb, b := ib, on=.(a)] # test2
}})

这些是环境:

docker run -it --rm r- base:3.4.0
install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.10.4-3.tar.gz", repos=NULL)
测试1
用户系统已过
0.949 0.007 0.957
测试2
用户系统已过
0.864 0.000 0.864

docker run -it --rm r -base:3.6.1
install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.12.2.tar.gz", repos=NULL)
测试1
用户系统已过
2.598 0.047 1.350
测试2
用户系统已过
2.092 0.016 1.062

docker run -it --rm r -base:3.6.1
install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
测试1
用户系统已过
1.174 0.045 1.219
测试2
用户系统已过
0.981 0.024 1.004

正如您在 Windows 上一样,还有另一个待处理的 PR 最终可能会
帮助 #4558 https://github.com/Rdatatable/data.table/pull/4558


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928#issuecomment-650126662
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MJLU3XRN5YN37ZVLALRYR727ANCNFSM4I4ZPREA
.

@vk111是的,正是我要问的。 如果您可以在 Windows 机器上检查这 3 个配置,看看是否有任何进展。

但据我了解,适用于 Windows 的 pr 尚未合并

2020 年 6 月 26 日星期五 20:07 Jan Gorecki, notifications @github.com 写道:

@vk111 https://github.com/vk111是的,正是我要问的。
如果您可以在 Windows 机器上检查这 3 个配置并查看
如果您有任何进展。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928#issuecomment-650320224
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MMDOCNAWARCCDRSU23RYTPVFANCNFSM4I4ZPREA
.

4440 已经被合并,并且对每个操作系统都有帮助。 #4558 仍在等待合并。

@vk111 #4558 也已合并

嗨 Jan,我最近听取了你的建议 - #4419
https://github.com/Rdatatable/data.table/issues/4419与我的问题相关
所以我没有这段带有字符连接的确切代码
但无论如何,谢谢你的改进,我相信它会帮助很多
未来的人,如果你认为它有更好的表现,那么我
猜你应该合并它
谢谢

пт, 26 岁。 2020 年 в 20:29,Jan Gorecki通知@github.com:

4440 https://github.com/Rdatatable/data.table/pull/4440已经

合并并为每个操作系统提供帮助。 #4558
https://github.com/Rdatatable/data.table/pull/4558仍在等待中
合并。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928#issuecomment-650330154
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MIN2P7BXMKG74Z7EKTRYTSIJANCNFSM4I4ZPREA
.

--
最好的祝福,
瓦西里·库兹涅佐夫

@vk111提到的 PR 已经合并。 关键是要知道这个问题是否得到了解决,或者至少得到了改善。 那些合并的 PR 并没有试图解决这个问题,但它们是相关的,因此它们可能会有所帮助。

碰巧最初报告的最小示例未能很好地暴露该问题。 基于@vk111 https://github.com/Rdatatable/data.table/issues/3928#issuecomment -538681316 我构建了一个例子,它清楚地表明这里有一个问题。

install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.10.4-3.tar.gz", repos=NULL)
install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.12.2.tar.gz", repos=NULL)
install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
library(data.table)
setDTthreads(2L)
set.seed(108)
n2 = 1:366000
n1 = sample(n2, 30000)
d1=data.table(id1=paste0("id",sample(n1)), id2=paste0("id",sample(n1)), v1=n1)
d2=data.table(id1=paste0("id",sample(n2)), id2=paste0("id",sample(n2)), v2=n2)
system.time(d2[d1, v1 := i.v1, on=c("id1","id2")])



md5-e82041eef8382e88332d077bb608b87c



# 3.4.0: 1.10.4-3
   user  system elapsed 
  0.144   0.000   0.144 
# 3.4.0: 1.12.2
   user  system elapsed 
  0.611   0.003   0.451 
# 3.4.0: 1.12.9
   user  system elapsed 
  0.611   0.000   0.452 



md5-e82041eef8382e88332d077bb608b87c



# 4.0.0: 1.10.4-3
   user  system elapsed 
  0.203   0.008   0.211 
# 4.0.0: 1.12.9
   user  system elapsed 
  0.812   0.004   0.644 

详细表明大部分时间都花在Calculated ad hoc index in 0.611s elapsed (0.769s cpu)上,这是从bmerge调用forderv #$ 。

对,就是这样
但似乎这个测试没有任何改进?

2020 年 6 月 30 日星期二 00:45 Jan Gorecki, notifications @github.com 写道:

碰巧最初报告的最小示例曝光不佳
问题。 基于@vk111 https://github.com/vk111 #3928(评论)
https://github.com/Rdatatable/data.table/issues/3928#issuecomment-538681316
我构建了一个例子,它清楚地表明这里有一个问题。

库(数据表)
setDTthreads(2L)
set.seed(108)n2 = 1:366000n1 = sample(n2, 30000)d1=data.table(id1=paste0("id",sample(n1)), id2=paste0("id",sample(n1) ), v1=n1)d2=data.table(id1=paste0("id",sample(n2)), id2=paste0("id",sample(n2)), v2=n2)
system.time(d2[d1, v1 := i.v1, on=c("id1","id2")])

3.4.0:1.10.4-3

用户系统已过
0.144 0.000 0.144

3.4.0:1.12.2

用户系统已过
0.611 0.003 0.451

3.4.0:1.12.9

用户系统已过
0.611 0.000 0.452

4.0.0:1.12.9

用户系统已过
0.812 0.004 0.644


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/3928#issuecomment-651408089
或退订
https://github.com/notifications/unsubscribe-auth/ABRM7MPQHSOEGLXS625BU3TRZEKSBANCNFSM4I4ZPREA
.

没有改善,这仍然是一个问题。
经过一番挖掘,我发现瓶颈是这个电话
https://github.com/Rdatatable/data.table/blob/ba32f3cba38ec270587e395f6e6c26a80be36be6/src/forder.c#L282
走得更远……
这个循环负责 60%
https://github.com/Rdatatable/data.table/blob/ba32f3cba38ec270587e395f6e6c26a80be36be6/src/forder.c#L242 -L245
和那些 30%
https://github.com/Rdatatable/data.table/blob/ba32f3cba38ec270587e395f6e6c26a80be36be6/src/forder.c#L252 -L258

我尝试检查使用 R 内部结构( CHAR()一个宏而不是函数)在这里是否有帮助,但它没有帮助。


更简单的例子,仅在 R 4.0 上计时forderv

install.packages("https://cran.r-project.org/src/contrib/Archive/data.table/data.table_1.10.4-3.tar.gz", repos=NULL)
library(data.table)
setDTthreads(1L)
set.seed(108)
n2 = 1:366000
d2=data.table(id1=paste0("id",sample(n2)), id2=paste0("id",sample(n2)), v2=n2)
system.time(data.table:::forderv(d2, c("id1","id2")))
install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
library(data.table)
setDTthreads(1L)
set.seed(108)
n2 = 1:366000
d2=data.table(id1=paste0("id",sample(n2)), id2=paste0("id",sample(n2)), v2=n2)
system.time(data.table:::forderv(d2, c("id1","id2")))



md5-e82041eef8382e88332d077bb608b87c



# 4.0.2: 1.10.4-3
   user  system elapsed 
  0.198   0.000   0.198 
# 4.0.2: 1.12.9
   user  system elapsed 
  0.393   0.000   0.392 

我尝试检查使用 R 内部(CHAR() 一个宏而不是函数)是否会有所帮助,但它没有帮助。

那是因为 R 不再那样做了。 对于 USE_RINTERNALS、INTEGER、REAL 和我也假设 CHAR,仍然是内联函数。 换句话说,USE_RINTERNALS 的好处(即宏而不是函数)在最近的 R 版本中被删除以允许 ALTREP。 我不相信这真的是宏、函数还是内联函数本身会产生影响。 这就是 R 在该工作单元内所做的事情。 在过去,USE_RINTERNALS 把它变成了一个宏,它是一个直接的内存查找(一个数组取消引用,只考虑了向量的标题),这就是它更快的原因。 这是一个非常轻量级的工作单元。 如今,R 至少有一个分支来检查 ALTREP,即使有分支预测,额外的工作也会增加和/或阻止预取。 换句话说,R 现在在这些内联函数中多了一点代码。 我没有基准证据,只是基于观察它的理论。

根本原因可能是删除了cradix_r中的以下分支
https://github.com/Rdatatable/data.table/blob/76bb569fd7736b5f89471a35357e6a971ae1d424/src/forder.c#L780 -L783
现在对于长度为 2 的输入,没有短路。
现在如何放置该分支并不明显,因为功能在其操作方式方面发生了很大变化。

此页面是否有帮助?
0 / 5 - 0 等级