2024 年 Clojure 调查中分享您的想法!

欢迎!有关如何操作的信息,请参阅 关于 页面。

0 投票
REPL

嗨!

我阅读了关于在开发过程中减少 REPl 启动时间的文章,并尝试将此知识应用于使用 Leiningen 启动 REPl(文章中 Alex 使用 deps.edn 展示了此做法)。

我的步骤

我覆盖了 clojure.core/load 变量以跟踪加载模块所花费的时间

(alter-var-root #'clojure.core/load (fn [origin-fn]
                                  (if (get (meta origin-fn) ::track-load?)
                                    origin-fn
                                    (vary-meta (fn [& args]
                                                 (let [start# (System/nanoTime)
                                                       ret# (apply origin-fn args)
                                                       elapsed-ms# (/ (double (- (System/nanoTime) start#)) 1000000.0)]
                                                   (newline)
                                                   (pr {:args (vec args)
                                                        :ms   elapsed-ms#})
                                                   (newline)
                                                   ret#))
                                               merge
                                               {::track-load? true}))))

我注意到加载核心 Clojure 部分需要时间 ~0.10-0.20 毫秒,而加载第三方库模块(例如 cheshire)则需要 ~200-400 毫秒。

据我理解,减少 REPl 启动时间的主要思想是,如果 clj 模块未更改,则编译类的优先级高于 clj 模块。因此,我们可以编译第三方库,并从它们编译的类中受益,而不是解析它们的 clj 文件。

我使用 Leiningen uberjar 编译了所有 Clojure 文件(我的和库)并将包含编译后的类文件文件夹添加到 :checkout-deps-shares 中,我想这是一种将包含类文件的文件夹添加到类路径的方法。

:checkout-deps-shares [:source-paths :test-paths
                     ~(fn [p] (str (:root p) "/target/uberjar/classes/*"))]

然后我重新启动 REPl,覆盖了 clojure.core/load 以跟踪模块加载时间,并作为一个实验进行以下操作

(time (require 'runtime.account :reload-all))

其中 runtime.account 是我的根模块之一。

结果,我看不到第三方库模块加载时间有任何差别 :/。

也许你可以给我一些关于决定加载类文件还是 clj 文件机制的细节,以及某些用于监控该活动的途径?

谢谢!

1 个答案

+1 投票

lein uberjar 可能不会自动编译所有的内容。一个确保万无一失的方法是为 :aot 键设置值 :all
你能发布你的 lein project.clj 吗?

好!
明白了,我用 :aot :all 编译的 uberjar 运行我的实验。

没有预料到结果 :/


这里是我的 project.clj,对于格式错误表示歉意

(defproject myproject "1.0.0")
   :dependencies [(comment "deps")]
   :uberjar-name "my-project.jar"
   :main runtime.run
   :source-paths ["src-clj"]
   :resource-paths ["resources"]
   :test-paths ["test-clj"]
   :target-path "target/%s"
   :env {(comment "ENV VARS")}
   :eftest {:multithread? :namespaces}
   :capture-output? true
   :test-warn-time 1000}
   :profiles {:default [:base :system :user :provided :local-dev]}
   :user {:dependencies [(comment "dev deps")]}
   :checkout-deps-shares [:source-paths :test-paths
   :~(fn [p] (str (:root p) "/target/uberjar/classes/*"))]}
   :local-dev {}
   :uberjar {:aot :all}
   :jvm-opts ["-Dclojure.compiler.direct-linking=true" "-Dclojure.compiler.elide-meta=[:doc :file :line :added]"]}
   :aliases {"run-tests" ["trampoline" "run" "-m" "test.core"]}
   :run-server ["trampoline" "run" "-m" "runtime.run"]}
   :jvm-opts [(comment "JVM OPTS")]
   :repl-options {:timeout 180000}
   :init-ns runtime.repl})
我发现将 "target/uberjar/classes" 添加到 :src-path 中可以加快 repl 启动速度,从 25 秒减少到 15 秒

但之后
(time (require 'runtime.account :reload-all))

(clojure.tools.namespace.repl/refresh) 无法解析依赖

clojure.tools.namespace.repl 已配置为跟踪以下目录:"src-clj","test-clj"}
我为 Lein 项目添加了 :compile 配置文件,并配置了 :aot :all 选项
现在使用 'lein with-profile compile compile',我可以在以下文件夹中为每个命名空间生成类文件:
文件夹 "/Users/user-name/core/project-name/target/default/classes"

我通过 (clojure.java.classpath/classpath-directories) 检查,确定类路径包含此文件夹

lein repl 启动时间为 15 秒而不是 25 秒

但使用 (clojure.tools.namespace.repl/refresh) 时  失败的错误是由于无法解析依赖,无法找到文件

我认为这与类加载器有关

正在调查
...