jq 大量文件性能低下
jq slow performance for numerous files
我的理解是,由于 jq 主动解析 json 流的方式,它可以大大加快 json 读取速度。但我在实践中没有看到这样的事情。这是与 R 工作流的比较(使用用 R 创建的虚拟数据)。
require(dplyr)
# also requires purrr, stringr, tibble, jsonlite
dir.create('jq-test')
setwd('jq-test')
# dummy data ----------------
dat = stringr::sentences[1:100] |> tibble::enframe(name='id', value = 'sentence') |>
mutate(text = purrr::map_chr(id, \(x)c(letters,rep(' ',6))[sample(1:32, 1000, rep=TRUE)] |> paste(collapse='')))
for(i in 1:nrow(dat)) jsonlite::write_json(as.list(dat[i,]), paste0(i, '.json'), auto_unbox=TRUE)
fl = list.files(pattern = '.json')
# test read
system.time({
x = purrr::map_chr(fl, ~ jsonlite::fromJSON(.x)[['sentence']])
data.frame(x=x) |> write.table(file = 'sentences-r.txt', row.names=FALSE, col.names=FALSE)
})
.. 报告以下测试读取阶段的运行时间:
user system elapsed
0.042 0.030 0.557
正在 bash 中使用 jq 进行测试:
cat /dev/null > sentences-jq.txt
SECONDS=0
for file in $(ls *.json)./; do
sentence=$(jq ".sentence" $file)
echo $sentence >> sentences-jq.txt
done
echo runtime ${SECONDS}s
.. 报告运行时间为 5 秒。以慢着称的 R 却快了 10 倍。为了排除 >>
操作说明,我得到了与 echo $sentence > /dev/null
.
相同的运行时间
我是否遗漏了一些有关 jq 工作原理的信息?
Am I missing something about how jq works?
jq 对 JSON 的处理通常非常快 (*),但每次调用 jq 确实有 non-trivial start-up 成本,因此您的 bash
" " 循环是次优的,因为除非 glob 扩展失败,否则单个 jq 调用就足以产生相同的结果:
jq .sentence *.json
如果您对此有困难,那么既然您已经指出 ls *.json
有效,那么以下内容也应该有效:
cat *.json | jq .sentence
(*) 至少如果必须阅读整个 JSON 文档。为了比较,这里有一些读取 well-known JSON 文件的时间,jeopardy.json (**):
$ time jq length jeopardy.json
216930
user 0m1.266s
sys 0m0.271s
$ time gojq length jeopardy.json
216930
user 0m0.989s
sys 0m0.162s
$ time jaq length < jeopardy.json
216930
user 0m1.406s
sys 0m0.221s
$ time R -f <(echo 'require(jsonlite);x=read_json("jeopardy.json")' )
user 0m2.709s
sys 0m0.463s
$ time python3 <<< "import json; f = open('jeopardy.json'); data=json.load(f); print(len(data))"
216930
user 0m0.693s
sys 0m0.250s
time jj '#' < jeopardy.json
216930
user 0m0.242s
sys 0m0.102s
对于某些类型的查询,替代 jq 的标准解析器会更快。一个可能相关的替代方案是 jq 自己的“流式解析器”,尽管它通常对 JSON 太大而无法放入内存的文本感兴趣。
(**) 通常命名为 JEOPARDY_QUESTIONS1.json
例如在 https://github.com/ryanwholey/jeopardy_bot/blob/master/JEOPARDY_QUESTIONS1.json
运行 jq(或任何程序)重复调用会耗费很长时间,因此我们需要尽可能减少调用次数。
对于 argument list too long errors
,试试这个:
find . -maxdepth 1 -mindepth 1 -name "*.json" -exec jq .sentence {} +
find
尝试在对 jq
.
的单次调用中附加尽可能多的 json 个文件
我的理解是,由于 jq 主动解析 json 流的方式,它可以大大加快 json 读取速度。但我在实践中没有看到这样的事情。这是与 R 工作流的比较(使用用 R 创建的虚拟数据)。
require(dplyr)
# also requires purrr, stringr, tibble, jsonlite
dir.create('jq-test')
setwd('jq-test')
# dummy data ----------------
dat = stringr::sentences[1:100] |> tibble::enframe(name='id', value = 'sentence') |>
mutate(text = purrr::map_chr(id, \(x)c(letters,rep(' ',6))[sample(1:32, 1000, rep=TRUE)] |> paste(collapse='')))
for(i in 1:nrow(dat)) jsonlite::write_json(as.list(dat[i,]), paste0(i, '.json'), auto_unbox=TRUE)
fl = list.files(pattern = '.json')
# test read
system.time({
x = purrr::map_chr(fl, ~ jsonlite::fromJSON(.x)[['sentence']])
data.frame(x=x) |> write.table(file = 'sentences-r.txt', row.names=FALSE, col.names=FALSE)
})
.. 报告以下测试读取阶段的运行时间:
user system elapsed
0.042 0.030 0.557
正在 bash 中使用 jq 进行测试:
cat /dev/null > sentences-jq.txt
SECONDS=0
for file in $(ls *.json)./; do
sentence=$(jq ".sentence" $file)
echo $sentence >> sentences-jq.txt
done
echo runtime ${SECONDS}s
.. 报告运行时间为 5 秒。以慢着称的 R 却快了 10 倍。为了排除 >>
操作说明,我得到了与 echo $sentence > /dev/null
.
我是否遗漏了一些有关 jq 工作原理的信息?
Am I missing something about how jq works?
jq 对 JSON 的处理通常非常快 (*),但每次调用 jq 确实有 non-trivial start-up 成本,因此您的 bash
" " 循环是次优的,因为除非 glob 扩展失败,否则单个 jq 调用就足以产生相同的结果:
jq .sentence *.json
如果您对此有困难,那么既然您已经指出 ls *.json
有效,那么以下内容也应该有效:
cat *.json | jq .sentence
(*) 至少如果必须阅读整个 JSON 文档。为了比较,这里有一些读取 well-known JSON 文件的时间,jeopardy.json (**):
$ time jq length jeopardy.json
216930
user 0m1.266s
sys 0m0.271s
$ time gojq length jeopardy.json
216930
user 0m0.989s
sys 0m0.162s
$ time jaq length < jeopardy.json
216930
user 0m1.406s
sys 0m0.221s
$ time R -f <(echo 'require(jsonlite);x=read_json("jeopardy.json")' )
user 0m2.709s
sys 0m0.463s
$ time python3 <<< "import json; f = open('jeopardy.json'); data=json.load(f); print(len(data))"
216930
user 0m0.693s
sys 0m0.250s
time jj '#' < jeopardy.json
216930
user 0m0.242s
sys 0m0.102s
对于某些类型的查询,替代 jq 的标准解析器会更快。一个可能相关的替代方案是 jq 自己的“流式解析器”,尽管它通常对 JSON 太大而无法放入内存的文本感兴趣。
(**) 通常命名为 JEOPARDY_QUESTIONS1.json 例如在 https://github.com/ryanwholey/jeopardy_bot/blob/master/JEOPARDY_QUESTIONS1.json
运行 jq(或任何程序)重复调用会耗费很长时间,因此我们需要尽可能减少调用次数。
对于 argument list too long errors
,试试这个:
find . -maxdepth 1 -mindepth 1 -name "*.json" -exec jq .sentence {} +
find
尝试在对 jq
.