Skip to content
Tims Gardner edited this page Mar 18, 2020 · 8 revisions

Improving Startup Times

Arcadia's startup time can be improved by clicking Arcadia → AOT Compile menu item. The effect that it has is to compile all available Clojure source code into DLL files that can be loaded much faster. You should do this at least once per project to compile all of Arcadia's source. You can use it again as you own code base grows if you notice that startup times are sluggish.

Using MAGIC

The MAGIC project is a Clojure compiler library for the CLR developed and maintained by the Arcadia team. In some situations, it can generate faster bytecode than standard ClojureCLR, but it is not feature-complete yet. Specifically, it tends to do a better job of handling value types and reducing needless allocation.

Installation

You will need to clone into your Assets folder the MAGIC library as well as the MAGE and tools.analyzer libraries on which it depends.

At the moment #105 will prevent Magic compiling in Unity. As a work-around, use the unity branch on timsgardner's fork of Magic instead, as shown below.

cd your/project/Assets
git clone https://github.com/clojure/tools.analyzer.git
git clone https://github.com/nasser/mage.git
git clone --branch unity git@github.com:timsgardner/magic.git

Usage

MAGIC's public facing API is in the magic.api namespace.

(ns foo
  (:require [magic.api :as m]))

At the moment, MAGIC exposes two macros: defn and faster.

defn

magic.api/defn is meant to be a drop in replacement for clojure.core/defn. It compiles the function in MAGIC, then creates a Clojure var in the namespace referring to that new function. That var is then invokeable from standard Clojure functions as well as MAGIC compiled functions. Note that invoking a MAGIC-complied function from Clojure code will not be able to preserve type information, and any value type arguments will be boxed. Calling MAGIC-compiled function from other MAGIC-compiled functions avoids this.

(m/defn pow2 [^float n]
  (* n n))

(m/defn sum-pow [^float n]
  (+ (pow2 n) (pow2 n))

faster

magic.api/faster compiles arbitrary expressions in MAGIC. It captures all closed over bindings, so it is useful in situations where you need to use features that MAGIC does not support yet (see below).

(defn bar [^int n]
  (m/faster (* n n))

(defn qux [n]
  (let [^long m (count (filter (fn [x] (> x 20))) n)] ;; MAGIC cannot do this
    (m/faster (* m m))))                              ;; but it can close over m

Limitations

The progress file in MAGIC's repo tracks how far along the compiler is, but basically MAGIC is currently unable to do the following:

  • Dynamic call sites
  • fn expressions inside of other functions
  • letfn
  • case
  • reify
  • deftype