2024 年 Clojure 状态调查!中分享你的想法。

欢迎!请参阅关于页面,了解更多有关此功能的信息。

0
tools.cli
重新标记

tools.cli/parse-opts接受参数、选项规范和附加选项。它调用compile-option-specsrequired-arguments来构建specreq。然后,它对这些spec和req以及提供的参数(以及选项)执行验证。对于某个应用,前两个步骤在调用中不会改变。

我建议一个名为make-parse-opts-fn的新函数,它提前执行compile-option-specsrequired-arguments,并返回一个依赖于编译后的specreq的函数。它可以这样使用:(def compiled-parser (make-parse-opts-fn cli-options))

使用criterium进行微基准测试表明,速度提高了两倍以上(首先是tools.cli/parse-opts,然后是预编译的解析器)

; user=> (def cli-options
  [["-h" "--help" "This message"]
   [nil "--extra" "Output in extra format"
    :default false]
   ["-q" "--quiet" "Print no suggestions, only return exit code"
    :default false]])
#'user/cli-options

; user=> (bench (cli/parse-opts ["--quiet" "src"] cli-options :in-order true))
Evaluation count : 10962 in 6 samples of 1827 calls.
             Execution time mean : 65.021329 µs
    Execution time std-deviation : 5.276033 µs
   Execution time lower quantile : 58.658638 µs ( 2.5%)
   Execution time upper quantile : 69.724228 µs (97.5%)
                   Overhead used : 9.607933 ns
nil

; user=> (bench (compiled-parser ["--quiet" "src"] :in-order true))
Evaluation count : 24660 in 6 samples of 4110 calls.
             Execution time mean : 25.090846 µs
    Execution time std-deviation : 253.361821 ns
   Execution time lower quantile : 24.769079 µs ( 2.5%)
   Execution time upper quantile : 25.413286 µs (97.5%)
                   Overhead used : 9.607933 ns
nil

如果将附加选项也提升到make-parse-opts-fn中(以速度换取灵活性),那么差异更加显著,比现有的tools.cli/parse-opts函数快大约10倍

; user=> (bench (compiled-parser-2 ["--quiet" "src"]))
Evaluation count : 73116 in 6 samples of 12186 calls.
             Execution time mean : 8.349923 µs
    Execution time std-deviation : 51.419864 ns
   Execution time lower quantile : 8.282266 µs ( 2.5%)
   Execution time upper quantile : 8.407998 µs (97.5%)
                   Overhead used : 9.607933 ns

Found 2 outliers in 6 samples (33.3333 %)
	low-severe	 1 (16.6667 %)
	low-mild	 1 (16.6667 %)
 Variance from outliers : 13.8889 % Variance is moderately inflated by outliers
nil

如果有兴趣,我可以提供一个补丁。

你能解释一下在应用程序的单次运行中多次解析命令行参数的应用场景吗?

我不怀疑你的基准测试表明,首先预编译然后多次解析比多次运行整个过程更快——但我不明白为什么会有用。
根据README中提到的两个用例为“以顺序处理子命令”以及运行测试。最初我对预编译有助于单次AOT编译时的运行有所期待,但实际上并不如此。然而,这种分割在运行我的测试套件时略有帮助,其中我通过cli接口大量调用代码。
在“以顺序处理子命令”中,除非你正在编写某种交互式shell(可能语法不同,并不便于使用cli工具),否则这通常只会在启动时执行一次。

如果这对你有帮助,我会很乐意将私有函数公开,这样你就可以编写自己的预处理/编译包装器,但我不确定这是否是一个足以直接将其嵌入API的使用场景。
是的,它只会发生一次,我正在考虑连续多次处理子命令和调用parse-opts。

我认为使parse-opts中调用的私有函数公开会很不错,但我已经不再确信预编译是一个好主意。也许可以通过将编译和将结果数据复制粘贴回我的代码中,然后编写一个使用手动编译的opts的第二部分的函数来获得速度提升?

是的,将编译函数公开(在文档字符串中注明预期的隐私性)至少可以让我更容易地实验这个想法。
诺亚,既然你可以调用私有函数(使用 '#' 来引用它们的变量),你介意去做那些实验 - 如果显示出可观的改进,那么我会立即将相关部分公开。谢谢。
嗨,Sean!在尝试了大约9个月后,我认为唯一需要改变的是,我希望 `cli/summarize` 是公开的。我向 `cli/parse-opts` 传入 `:summary-fn identity` 以避免构建总结字符串,只有当用户请求帮助文档时才会调用 `(#'cli/summarize summary)`。如果它是私有的(如演示所示),我可以访问它,但这会产生解引用成本,我愿意避免。
开个玩笑,`cli/summarize` 已经是公开的。我认为文档字符串让我认为它是私有的,而我没有真正检查。我希望我能读懂。:泣
所以……听起来 TCLI-101 可以不用采取任何行动就可以关闭了吗?
是的,谢谢。

1 个回答

+1

选中
 
最佳答案
...