From 6e84c2a6c3622e2f25c455fef3007b1bac141c8c Mon Sep 17 00:00:00 2001 From: Jamie English Date: Sat, 4 Apr 2026 16:04:26 +0100 Subject: [PATCH 1/2] perf(gen_build): memoize classpath-files for is-aoted? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmark (examples/gen_srcs_bench, hyperfine, 10 runs, prepare: delete src/**/BUILD.bazel):\n- before median: 10.113s (mean 11.905s ± 6.529s)\n- after median: 9.017s (mean 9.399s ± 1.827s)\n- change (median): -10.84% --- src/rules_clojure/gen_build.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rules_clojure/gen_build.clj b/src/rules_clojure/gen_build.clj index 3ff8a46..9ca3016 100644 --- a/src/rules_clojure/gen_build.clj +++ b/src/rules_clojure/gen_build.clj @@ -241,6 +241,9 @@ (-> path fs/path->file (.isDirectory)) (dir-files path) (re-find #".jar$" (str path)) (jar-files path))) +(def classpath-files-memo + (memoize classpath-files)) + (defn jar-classes "given the path to a jar, return a list of classes contained" [path] @@ -259,7 +262,7 @@ [path ns] (let [root-resource (#'clojure.core/root-resource ns) class-file (str (.substring ^String root-resource 1) "__init.class")] - (some #(= class-file %) (classpath-files path)))) + (some #(= class-file %) (classpath-files-memo path)))) (def special-namespaces '#{clojure.core clojure.core.specs.alpha}) From 22a7b97e0555b8ebdf39cde07311a664191d11b2 Mon Sep 17 00:00:00 2001 From: Jamie English Date: Tue, 7 Apr 2026 09:41:01 +0100 Subject: [PATCH 2/2] perf(gen_build): pre-compute classpath file sets for is-aoted? The previous memoize approach cached the file listing per path but still did O(n) linear scans via `some` on every call. Restructure to build a set once per classpath entry and pass it through should-compile-namespace?/is-aoted?, replacing the linear scan with O(1) `contains?` lookups. Benchmark (examples/gen_srcs_bench, hyperfine, 5 runs): - baseline median: 13.155s - memoize median: 10.899s (-17%) - restructured median: 6.636s (-50%) (written by Claude) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/rules_clojure/gen_build.clj | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/rules_clojure/gen_build.clj b/src/rules_clojure/gen_build.clj index 9ca3016..0ec6923 100644 --- a/src/rules_clojure/gen_build.clj +++ b/src/rules_clojure/gen_build.clj @@ -241,9 +241,6 @@ (-> path fs/path->file (.isDirectory)) (dir-files path) (re-find #".jar$" (str path)) (jar-files path))) -(def classpath-files-memo - (memoize classpath-files)) - (defn jar-classes "given the path to a jar, return a list of classes contained" [path] @@ -259,17 +256,17 @@ symbol))))) (defn is-aoted? - [path ns] + [files-set ns] (let [root-resource (#'clojure.core/root-resource ns) class-file (str (.substring ^String root-resource 1) "__init.class")] - (some #(= class-file %) (classpath-files-memo path)))) + (contains? files-set class-file))) (def special-namespaces '#{clojure.core clojure.core.specs.alpha}) -(defn should-compile-namespace? [deps-bazel path ns] +(defn should-compile-namespace? [deps-bazel files-set ns] (and (not (contains? special-namespaces ns)) (not (contains? (get-in deps-bazel [:no-aot]) ns)) - (not (is-aoted? path ns)))) + (not (is-aoted? files-set ns)))) (defn ns-matches-path? [ns path] @@ -285,16 +282,17 @@ :classpath (map (fn [[path {:keys [lib-name]}]] (when lib-name - {:clj (->> (find/find-namespaces [(fs/path->file path)] find/clj) - (map (fn [n] - [n (if (should-compile-namespace? deps-bazel path n) - (internal-dep-ns-aot-label lib-name n) - (library->label lib-name))])) - (into {})) + (let [files-set (set (classpath-files path))] + {:clj (->> (find/find-namespaces [(fs/path->file path)] find/clj) + (map (fn [n] + [n (if (should-compile-namespace? deps-bazel files-set n) + (internal-dep-ns-aot-label lib-name n) + (library->label lib-name))])) + (into {})) :cljs (->> (find/find-namespaces [(fs/path->file path)] find/cljs) (map (fn [n] [n (library->label lib-name)])) - (into {}))}))) + (into {}))})))) (filter identity) (apply merge-with merge))) @@ -811,7 +809,8 @@ extra-args (-> deps-bazel (get-in [:deps external-label])) _ (assert (re-find #".jar$" (str jarpath)) "only know how to handle jars for now") - jarfile (JarFile. (fs/path->file jarpath))] + jarfile (JarFile. (fs/path->file jarpath)) + files-set (set (jar-files jarpath))] (vec (concat [(emit-bazel (list 'java_import (kwargs (merge-with into @@ -831,7 +830,7 @@ ns-decl)))) (filter (fn [ns-decl] - (should-compile-namespace? deps-bazel jarpath (parse/name-from-ns-decl ns-decl)))) + (should-compile-namespace? deps-bazel files-set (parse/name-from-ns-decl ns-decl)))) (group-by parse/name-from-ns-decl) (map (fn [[ns ns-decls]] (let [extra-deps (-> deps-bazel (get-in [:deps (str deps-repo-tag "//:" (internal-dep-ns-aot-label lib ns))]))]