请在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 吗?

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

没有预期结果 :/


这是我的 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"
我为具有 :aot :all 选项配置的 Lein 项目添加了 :compile 配置文件
现在通过 '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)  无法解析依赖,找不到文件

我认为这有一些与类加载器相关的魔法

正在调查中
...