提问人:Donald Seinen 提问时间:8/6/2022 最后编辑:Donald Seinen 更新时间:8/7/2022 访问量:215
何时使用哈希标签?
When to use a hashtab?
问:
tl;由于维护负担,R-core 拒绝了许多功能请求,但不会 (R>4.2.0)。 声称有效地将键与值相关联。还有许多其他实现(hash、r2r、hashmap 等),环境和用户友好的扩展(rlang、RC、R6 等)也是如此。除了对象混淆和任意键之外,我还没有找到一个明显的用例比其他用例更有效。hashtab
?hashtab
hashtab
问题:
对于某些用例,hashtab
是否具有任意键以外的独特功能,或者在速度、内存或语法方面是否有明显的优势?
我试图查看环境的性能、功能和内部结构,以及 .hashtab
set.seed(1)
make_hash <- function(n, keys, values) {
h <- hashtab("identical", n)
for(i in seq_along(keys)) sethash(h, keys[i], values[[i]])
h
}
make_env <- function(n, keys, values) setNames(values, keys) |> list2env(size = n)
get_mem <- function(x) as.numeric(lobstr::obj_size(x)) * 0.001
compare <- function(n, keylen) {
keys <- stringi::stri_rand_strings(n, keylen)
values <- sample(list(mapply, iris, 1:1e6, "just a string", 1L), n, replace = TRUE)
ind <- sample(keys, 1)
h <- make_hash(n, keys, values)
e <- make_env(n, keys, values)
data.frame(
n = n,
method = c("environment", "hashtab"),
make_speed = {
bench::mark(
make_env(n, keys, values),
make_hash(n, keys, values),
check = F
)$median |> as.character()
},
memory = c(get_mem(e), get_mem(h)),
access = bench::mark(
e[[ind]],
gethash(h, ind, NULL), # the natural h[[ind]] is 2x slower
iterations = 1e4
)$median |> as.character()
)
}
一些性能基准,
purrr::map_dfr(c(1e2, 1e3, 1e4, 1e5, 1e6), compare, 10)
n method make_speed memory access
1 100 environment 13.8µs 45.11 200ns
2 100 hashtab 139.9µs 41.61 1µs
3 1000 environment 110.5µs 227.56 200ns
4 1000 hashtab 1.25ms 214.34 1µs
5 10000 environment 1.46ms 2041.96 200ns
6 10000 hashtab 13.64ms 2044.10 1µs
7 100000 environment 53.3ms 20185.96 200ns
8 100000 hashtab 394.9ms 19719.11 1µs
9 1000000 environment 2.2s 201625.96 300ns
10 1000000 hashtab 4.1s 192799.18 1µs
它们的参考语义,
e1 <- new.env()
e1$hi <- 1
e2 <- e1
e2$hi <- 2
e1$hi # autocompletion
#> [1] 2
h1 <- hashtab()
sethash(h1, "hi", 1)
h2 <- h1
sethash(h2, "hi", 2)
gethash(h1, "hi")
#> [1] 2
批量访问,
e1$bye <- 3
sethash(h1, "bye", 3)
eapply(e1, function(x) x)
#> $hi
#> [1] 2
#> $bye
#> [1] 3
(function(h) {
val <- list()
maphash(h, function(k, v) val[[k]] <<- v)
val
})(h1)
#> $bye
#> [1] 3
#> $hi
#> [1] 2
密钥名称,
e1[[iris]] <- 5 # error. arbitrary object as key... but why?
h1[[iris]] <- 5 # works fine
它们的内部(解释),发现环境包含一个 ,hashtab
e <- new.env(size = 2)
e$x <- 5
.Internal(inspect(e))
#> @0x00000226b4083c48 04 ENVSXP g0c0 [REF(5)] <0x00000226b4083c48>
#> ENCLOS:
#> @0x00000226ac100778 04 ENVSXP g1c0 [MARK,REF(65535),GL,gp=0x8000] #><R_GlobalEnv>
#> HASHTAB:
#> @0x00000226b5f6b588 19 VECSXP g0c2 [REF(1)] (len=2, tl=1)
#> @0x00000226b40b0a70 02 LISTSXP g0c0 [REF(1)]
#> TAG: @0x00000226aed32ae0 01 SYMSXP g1c0 [MARK,REF(65535)] "x"
#> @0x00000226b5f3d3a0 14 REALSXP g0c1 [REF(6)] (len=1, tl=0) 5
#> @0x00000226ac0add90 00 NILSXP g1c0 [MARK,REF(65535)]
# note the (len=8)
h <- hashtab(size = 2)
sethash(h, "x", 5)
.Internal(inspect(h))
#> @0x00000226b5f5e8e0 19 VECSXP g0c1 [OBJ,REF(9),ATT] (len=1, tl=0)
#> @0x00000226b4361ab0 22 EXTPTRSXP g0c0 [REF(3)] <0x00000226b4361ab0>
#> PROTECTED:
#> @0x00000226b5f4e898 19 VECSXP g0c4 [REF(1)] (len=8, tl=0)
#> @0x00000226ac0add90 00 NILSXP g1c0 [MARK,REF(65535)]
#> @0x00000226ac0add90 00 NILSXP g1c0 [MARK,REF(65535)]
#> @0x00000226ac0add90 00 NILSXP g1c0 [MARK,REF(65535)]
#> @0x00000226ac0add90 00 NILSXP g1c0 [MARK,REF(65535)]
#> @0x00000226ac0add90 00 NILSXP g1c0 [MARK,REF(65535)]
#> ...
#> TAG:
#> @0x00000226b235b2e8 13 INTSXP g0c2 [REF(1)] (len=3, tl=0) 1,0,3
#> ATTRIB:
#> @0x00000226b4361a78 02 LISTSXP g0c0 [REF(1)]
#> TAG: @0x00000226ac0ada80 01 SYMSXP g1c0 [MARK,REF(55126),LCK,gp=0x4000] #> "class" (has value)
#> @0x00000226b5f5e8a8 16 STRSXP g0c1 [REF(65535)] (len=1, tl=0)
#> @0x00000226b015bb00 09 CHARSXP g1c1 [MARK,REF(320),gp=0x61] [ASCII] #> [cached] "hashtab"
最后,他们在玩具包装中的行为。对哈希选项卡的第一次访问失败,后续访问成功。
# devtools::install_github("D-Se/so.hash")
so.hash:::data$hashtab
#> <hashtable (nil): count = 3, type = "identical">
so.hash::grab("x")
#> $env
#> [1] 1
#>
#> $hash
#> NULL
so.hash:::data$hashtab
#> <hashtable 0x00000168751ebcd0: count = 3, type = "identical">
so.hash::grab("x") # 2nd time asking
#> $env
#> [1] 1
#>
#> $hash
#> [1] 1
与环境相比,hashtab
- 内存使用类似,
- 访问时间相似,
- 创建需要更长的时间(因为我的代码很差?
- 元素不能自动完成(在 RStudio 中),
- 键名更灵活,
- 访问批量数据相当繁琐,
- 文档很少(它仍然是实验性的),
- 不考虑大小参数 (..?),
- 某些东西是受保护的1,但不在 ,
new.env()
- 行为不一致。
1 我不知道这意味着什么。
答: 暂无答案
评论