Want this course?
Clojure compiler options
Course: JVM Fundamentals for Clojure
The Clojure compiler has a few options that can speed up the execution of generated code at the expense of dynamism.
Hello. We're going to be talking about some Closure compiler options. The compiler for Closure has a few options that are mainly for optimization purposes. I'll go through them, actually, watch this. Here we go. I'll go through what each one means, and they're not exactly JVM specific, but they are important to know because they are Java options.
They can be set as Java options, although, you know, the compiler runs at runtime too, so you can change the values of these things as you're compiling stuff, but usually these are set up so that you can compile your Closure code, say, with AOT Compiler, and have it be a little bit more optimized. Okay, so these are the three options here. Disable-locals-clearing, elide-meta, and direct-linking. And we'll go over how to set these up in boot and line again in a minute. So the first thing is locals clearing.
This is something that happens when you are calling, you have some code, and at a certain point, you actually won't need to hold on to the values of some of the local variables anymore because you'll never access them again. You can tell in the code that that was the last time I'm going to need that value. Well, this option will set it so that in the Java output, Java Byte code output, that the compiler generates, it'll actually set that field to null, and that will allow the garbage collector to clear it out just a little bit sooner than it would normally. And that's really important when you're doing, like, you might have a loop, and the loop is just going to return, so why do you need to keep something from before the loop? You know, it's just a way of having a little bit more optimized.
Okay, elide meta, and it's set to true by default because it's only there for, if you set it to false, you would get more debugging information, because you'd have more values available on the stack while you're debugging. But it can be kind of expensive, you can have some problems where you're actually holding on to large values that you don't need to, like infinite values, and so you want to just get rid of them.
Okay, elide meta. This one is a way to remove some information from vars. Vars, when they get compiled, have a bunch of nice stuff that really helps when you're programming, like the docstring is taken out of the function and put into the var, as meta data on the var. The file name, like the path to the file, so like, if you have a, you can see what file this function was defined in, or any var was defined in, you know, any top level definition. Same with the line number. Alright, so it's really handy when you're, like, coding something up and debugging it and working interactively with it. But you might not need that stuff at runtime in production. And, you know, a docstring can be pretty big, not only does it take up memory, but it takes time to build that string and get put where it needs to go, so if you have thousands of them, you might want to get rid of them and not have to load all that. And so what elide meta lets you do, you see, it's a vector of keywords, it will let you say, well, like this, don't store the doc.
These other things you can remove too if you want, but the doc is the big one. And it says, for example, "doc cannot return docstrings if they have been elided." But if you're running this in production, you're probably not running doc on a function. In development, you often do run doc, but in production, you don't, so you can set this option. For your production compile, it doesn't have this stuff in it. And then the startup times and stuff will be a little faster. Okay, direct linking. This one is a big one because vars actually have a, vars are an object that have a field in them that can change. Its tag's volatile, which means that the JIT compiler always checks the value of that field every time you access it. Other fields that aren't marked volatile, the JIT is allowed to skip checking them. It only checks them one time. So in a loop, let's say you have a loop.
Before the loop, you get the value of some field. The JIT compiler might not check it ever again if it's not volatile. But they're volatile, so what this means is there's an extra pointer dereference every time you call a function. And what this direct linking does is it compiles those functions into static methods, so they are going to be as fast as any static method call in Byte code. And what that means is, you can't change them. Like a var, you can always just recompile your file, and all the vars get redefined. Now of course, when you're developing, you want that.
You want to be able to redefine. But, if you're running a production, again, you don't want to redefine stuff in production. Usually, you just deploy it and it starts running, and you never, like REPL into it and start messing with the code because that might be dangerous.
So this is something, again, that you probably want on your deployed stuff. It's not a big deal if you don't want to do it, if you want to just leave it the way the defaults are, that's fine too. But I'm going to show you how to do it. Alright, so I have this project. This is a web server, a ring thing that I run, I think on Heroku. And what I want to do is in the uberjar profile, set the options, the compile options. So these are actually JVM options. And I want to make sure that I don't have JVM options anywhere else, because this will clobber it. So these JVM options are going to be the options that are run while compiling, not while it runs, but while compiling.
Does that make sense? So while I'm building this uberjar, these are the JVM options that are going to be in effect. So I'm going to go in and set these. Now these are like command line options, so that's how you should consider them. And that's what this So this is how you set up the JVM options, you know, each option is its own string.
Okay, now this one, it's got quotes in it, so we've got to be sure to add the backslash. And then this one. There. So this is going to remove all the meta data, all four of these types of meta data. We don't want to disable locals clearing. We want to leave that, so I'll remove that one. You can set it to false, but that's the default. Direct linking equals true, that's good.
Alright, let's try this. Now I don't know how to test whether it actually got compiled in a certain way, because it's actually not supposed to change the semantics except what we talked about where the vars are now not redefinable, and the meta data, which typically you're not using meta data, so it's okay. You're not switching on what file a function was defined in or anything at runtime.