R语言hashtab适用场景:除任意键外的独特价值探究
R中hashtab的独特优势与对比分析
摘要
R核心团队常因维护负担拒绝功能请求,但hashtab(R>4.2.0)是例外。?hashtab称其可高效关联键与值,目前已有多种实现(hash、r2r、hashmap等),以及environment及其易用扩展(rlang、RC、R6等)。除对象混淆与任意键外,未发现hashtab在其他场景下比其他实现更高效。
技术问询
hashtab除支持任意键外,是否具备独特功能,或在速度、内存、语法方面对某些场景存在切实优势?
已针对hashtab与environment的性能、功能及内部实现展开对比测试:
性能对比测试代码
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 # 自动补全 #> [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 # 报错,无法用任意对象作为键...原因何在? h1[[iris]] <- 5 # 可正常运行
内部实现对比
通过内部检查发现,environment内部包含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)] # 注意此处的(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") # 第二次请求 #> $env #> [1] 1 #> #> $hash #> [1] 1
对比结论
- 内存使用相近;
- 访问速度相近;
- hashtab创建耗时更长(是否因代码实现问题?);
- hashtab元素无法在RStudio中自动补全;
- hashtab键名更灵活;
- hashtab批量数据访问操作繁琐;
- hashtab文档极少(仍处于实验阶段);
- hashtab的size参数未被遵守(?);
- hashtab存在被PROTECTED的内容¹,但new.env()中无此情况;
- hashtab存在行为不一致问题。
¹ 不清楚这意味着什么。
内容的提问来源于stack exchange,提问作者Donald Seinen
相关产品推荐
相关产品推荐

