Welcome! Please see the About page for a little more info on how this works.

+2 votes
in Compiler by
edited by

Blanketly enablind direct-linking can break libraries, and I can't control what vars in them are affected, but I'd still like to improve performance at least in parts of my own code.

Would the following work?

(binding [clojure.core/*compiler-options* 
  {:direct-linking true}]

Here's the docs: https://clojuredocs.org/clojure.core/*compiler-options*

1 Answer

0 votes

can break libraries

How so?

What matters is what those compiler bindings are set to when compiling happens. Compiling happens during load, so it should be possible to be clever about this, but it would be clever. I think there is another ask question somewhere about giving more control around where to direct link.

"How so?"

As i see in decompiled code + regarding to clojure docs 'direct-linking' compiler key enables direct function call where possible instead of indirect call via var.

Assuming i have my clojure code which uses some 3rd party clojure libs.
Using 'lein uberjar'i can see 3rd party clojure lib's compiled class files in the my result jar file.

if i am using 'lein uberjar'  with '-Dclojure.compiler.direct-linking=true' then is it possible that i can break potentially 3rd party lib which relies on some _runtime_ rebinding global vars magic which can be gone due to direct linking?
Dynamic vars don't get direct linked.
got it

what about repetitive def and alter-var-root?

AFAIK, with direct-linking enabled alter-var-root changes the var but its reference was already compiled so call sites will not be affected and continue to use compiled direct function call

— Can break libraries
— How so?

Yeah, I'm confused and put it wrong. I mean, we can have issues when interacting with libraries and direct linking is enabled for the whole codebase.

The first scenario is, perhaps, unlikely, but a library might expect that users implement a protocol differently and redefine an instance, or we'd like to do this for some reason.

    ;; Library
    (defprotocol MyProtocol
      (my-function [this]))
    (def my-instance (reify MyProtocol
                       (my-function [this] (println "hello"))))
    ;; Application
    ;; we can't redefine my-instance
    ;; and we can't make it ^:redef, it's in the library

The second case is when we're working on a library and forgot to mark a variable as ^:redef, the applicaion won't see the changes in it.

    ;; Library
    (def my-var 10)
    ;; we're trying to update this later, when everything is compiled
    ;; Application
    (defn use-var []
      (println my-var))

Both examples are probably contrived, and I don't have an exact question to ask in the form "How to do the following?", but direct linking is documented only briefly and not explained how to deal with in a fine-grained manner, that's why I'm asking.

Andray Shotkin checked and, apparently, this works, the code is indeed directly linked with no vars.

    (binding [*compiler-options* {:direct-linking false}]
      (clj-java-decompiler.core/decompile (fn []
                                            (seq (map :x [1 2 3])))))
    (binding [*compiler-options* {:direct-linking true}]
      (clj-java-decompiler.core/decompile (fn []
                                            (seq (map :x [1 2 3])))))
So, I'm pasting an example of how to enable direct linking for a namespace here, just in case. In case I completely forget about this conversation and look up direct linking again later haha.

    (ns my-namespace
     (:require [clojure.core :as core]))
    (binding [core/*compiler-options*
              {:direct-linking true}]