从 setkey 中获得性能增益
Getting a performance gain out of setkey
我已经阅读了文档 vignette("datatable-intro")
和一些关于该主题的在线资源,例如 this。
但我仍然在努力看到使用 data.table setkey
的性能提升(尽管我发现它比基础 [
和 dplyr::filter
快得多没有密钥集)。
知道为什么吗?我的例子 small/simple 是为了看到性能提升吗?还是我做错了什么?
我原以为使用密钥的 dtk 会更快。
library(data.table)
library(microbenchmark)
df = data.frame(l = letters, n = 1:26)
df = do.call(rbind, replicate(1e4, df, FALSE))
dfdt = dfdtk = data.table(df)
setkey(dfdtk, l)
mb = microbenchmark(times = 10, unit = "s",
base = df[df$l == "a",],
dt = dfdt[l == "a"],
dtl = dfdt[list("a")],
dtk = dfdtk[list("a")]
)
plot(mb)
我找到了 3 个需要修复或改进的地方。
=
运算符不会复制 data.table,随后 setkey
将在两个 data.table 上设置密钥,因此 copy
是必要的。
- 没有键的临时连接,还不能使用索引,但已计划。
dfdt[l == "a"]
将在第一次尝试时构建索引并重新使用它,因此值得添加没有索引的基准。
library(data.table)
library(microbenchmark)
op = options("datatable.auto.index" = TRUE) # default!
df = data.frame(l = letters, n = 1:26)
df = do.call(rbind, replicate(1e4, df, FALSE))
dfdtk = as.data.table(df)
dfdt = copy(dfdtk) # fix #1
setkeyv(dfdtk, "l")
stopifnot(
is.null(key(dfdt)),
key(dfdtk) == "l"
)
mb = microbenchmark(times = 10, unit = "s",
base = df[df$l == "a",],
dt = dfdt[l == "a"],
dtl = dfdt[list("a"), on = c("l" = "V1")], # fix #2
dtk = dfdtk[list("a")])
print(mb)
#Unit: seconds
# expr min lq mean median uq max neval
# base 0.016255351 0.017294076 0.0177331871 0.0178513590 0.018269365 0.019392296 10
# dt 0.000792324 0.000819030 0.0011565028 0.0009645955 0.001056976 0.002278742 10
# dtl 0.001625836 0.001657269 0.0018865184 0.0019408475 0.002009650 0.002196615 10
# dtk 0.000566798 0.000598538 0.0007664731 0.0007530190 0.000897327 0.001008621 10
options("datatable.auto.index" = FALSE) # fix #3
stopifnot(
key2(dfdt) == "l",
is.null(key2(set2keyv(dfdt, NULL)))
)
mb = microbenchmark(times = 10, unit = "s",
base = df[df$l == "a",],
dt = dfdt[l == "a"],
dtl = dfdt[list("a"), on = c("l" = "V1")],
dtk = dfdtk[list("a")])
print(mb)
#Unit: seconds
# expr min lq mean median uq max neval
# base 0.015935139 0.017397039 0.0253407267 0.0180737620 0.019560766 0.090317493 10
# dt 0.014194243 0.014292279 0.0153187365 0.0153102030 0.015997166 0.016689574 10
# dtl 0.001628532 0.001774283 0.0020169391 0.0018818880 0.001935386 0.003697506 10
# dtk 0.000556702 0.000653134 0.0006869461 0.0006898765 0.000764199 0.000780357 10
options(op)
抱歉 stopifnot
s,单元测试太多...
我已经阅读了文档 vignette("datatable-intro")
和一些关于该主题的在线资源,例如 this。
但我仍然在努力看到使用 data.table setkey
的性能提升(尽管我发现它比基础 [
和 dplyr::filter
快得多没有密钥集)。
知道为什么吗?我的例子 small/simple 是为了看到性能提升吗?还是我做错了什么?
我原以为使用密钥的 dtk 会更快。
library(data.table)
library(microbenchmark)
df = data.frame(l = letters, n = 1:26)
df = do.call(rbind, replicate(1e4, df, FALSE))
dfdt = dfdtk = data.table(df)
setkey(dfdtk, l)
mb = microbenchmark(times = 10, unit = "s",
base = df[df$l == "a",],
dt = dfdt[l == "a"],
dtl = dfdt[list("a")],
dtk = dfdtk[list("a")]
)
plot(mb)
我找到了 3 个需要修复或改进的地方。
=
运算符不会复制 data.table,随后setkey
将在两个 data.table 上设置密钥,因此copy
是必要的。- 没有键的临时连接,还不能使用索引,但已计划。
dfdt[l == "a"]
将在第一次尝试时构建索引并重新使用它,因此值得添加没有索引的基准。
library(data.table)
library(microbenchmark)
op = options("datatable.auto.index" = TRUE) # default!
df = data.frame(l = letters, n = 1:26)
df = do.call(rbind, replicate(1e4, df, FALSE))
dfdtk = as.data.table(df)
dfdt = copy(dfdtk) # fix #1
setkeyv(dfdtk, "l")
stopifnot(
is.null(key(dfdt)),
key(dfdtk) == "l"
)
mb = microbenchmark(times = 10, unit = "s",
base = df[df$l == "a",],
dt = dfdt[l == "a"],
dtl = dfdt[list("a"), on = c("l" = "V1")], # fix #2
dtk = dfdtk[list("a")])
print(mb)
#Unit: seconds
# expr min lq mean median uq max neval
# base 0.016255351 0.017294076 0.0177331871 0.0178513590 0.018269365 0.019392296 10
# dt 0.000792324 0.000819030 0.0011565028 0.0009645955 0.001056976 0.002278742 10
# dtl 0.001625836 0.001657269 0.0018865184 0.0019408475 0.002009650 0.002196615 10
# dtk 0.000566798 0.000598538 0.0007664731 0.0007530190 0.000897327 0.001008621 10
options("datatable.auto.index" = FALSE) # fix #3
stopifnot(
key2(dfdt) == "l",
is.null(key2(set2keyv(dfdt, NULL)))
)
mb = microbenchmark(times = 10, unit = "s",
base = df[df$l == "a",],
dt = dfdt[l == "a"],
dtl = dfdt[list("a"), on = c("l" = "V1")],
dtk = dfdtk[list("a")])
print(mb)
#Unit: seconds
# expr min lq mean median uq max neval
# base 0.015935139 0.017397039 0.0253407267 0.0180737620 0.019560766 0.090317493 10
# dt 0.014194243 0.014292279 0.0153187365 0.0153102030 0.015997166 0.016689574 10
# dtl 0.001628532 0.001774283 0.0020169391 0.0018818880 0.001935386 0.003697506 10
# dtk 0.000556702 0.000653134 0.0006869461 0.0006898765 0.000764199 0.000780357 10
options(op)
抱歉 stopifnot
s,单元测试太多...