# Can Julia really be used as a scripting language? (Performance)

**URL:** <https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384>\
**Category:** Performance\
**Created:** [May 29, 2020, 1:06am UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384 "2020-05-29T01:06:51Z")\
**Posts on this page:** 10\
**Page:** 4

<div class="post-metadata">

**Author:** ![johnmyleswhite](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnmyleswhite/32/31_2.png) [@johnmyleswhite](https://discourse.julialang.org/u/johnmyleswhite)\
**Post date:** [June 3, 2020, 3:16pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/61 "2020-06-03T15:16:03Z")

</div>

AFAICT, this thread seems to involve some misunderstanding between parties: it seems like there’s a group of people who often use Julia on local machines or the machines of their close collaborators and another group primarily interested in shipping what is essentially the dynamic language equivalent of an executable binary. Probably important to explicitly call out with each suggestion what usage scenario you’re optimizing for as the requirements are very different.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [June 3, 2020, 3:18pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/62 "2020-06-03T15:18:32Z")

</div>

When I open a new Julia REPL, and say `using Queryverse` which is a package that has lots of dependencies which it loads in as well, it takes about 20 seconds. It does that even with --compile=min. This is _not compilation time_ it’s just loading the precompiled packages. It doesn’t give the “precompiling” message, as that was done the first time I loaded it… Probably that was minutes of compile time.

So, yes 20 seconds of load time is quite a bit, but if I’m downloading a batch of files and generating a 100 page PDF report with graphs and charts nightly in cron, it’s not a big deal. But if I’m going to have a user invoke a script, and they’re going to sit in front of this in order to get a single page pdf plot… then it’s pretty annoying. They just want to see that one graph… and it takes tens of seconds.

---

<div class="post-metadata">

**Author:** ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)\
**Post date:** [June 3, 2020, 3:29pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/63 "2020-06-03T15:29:09Z")

</div>

> [@dlakelan](#):
>
> This is _not compilation time_ it’s just loading the precompiled packages.

As far as I understand, just because the package is precompiled, that does not obviate the need for more compilation when a package is loaded. And potentially more when functions are called for the first time.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 3, 2020, 3:35pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/64 "2020-06-03T15:35:54Z")

</div>

> [@dlakelan](#):
>
> This is _not compilation time_ it’s just loading the precompiled packages. It doesn’t give the “precompiling” message, as that was done the first time I loaded it… Probably that was minutes of compile time.

Actually… it probably is. More specifically, it’s probably _recompilation_ time. Loading new packages can _invalidate_ the optimizations that were used to first compile Julia itself (and the precompilation in other packages). This is a large part of the time-to-first-plot and lots of effort has gone into reducing invalidations in 1.5 and beyond.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [June 3, 2020, 3:48pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/65 "2020-06-03T15:48:54Z")

</div>

> [@mbauman](#):
>
> Actually… it probably is.

Ok, thanks for clarification. I guess what I’m saying is it’s not the same time that is spent when you first precompile the package. But then this explanation suggests that for people looking to write scripts using certain common packages, one should ship a full binary with those packages preinstalled using PackageCompiler so that the assumptions made during compilation time take into account all those packages that are preinstalled?

For an example, suppose you’re going to use some “scripts” to let people grab up to date data displays with continuously updated data. They can run

./data\_display

and it’s actually a julia script

```julia
#!/usr/bin/julia

....

```

and it downloads a bunch of data from some database and graphs 10 pages of visualizations. Let’s say the data is updated continuously (say it’s stock trading data or hospital resource usage data or logistics data on warehouse stocks and supply chains or something).

You want anyone to be able to run this thing and grab the latest visualizations and it should run quickly. Suppose you want to use certain packages like Queryverse and DSP and GLM and whatnot to develop your data displays. For this usage, it seems reasonable to ship a julia system image that includes those packages using PackageCompiler… at that point. I would HOPE that the startup time would be less than 1s before the julia code started executing the queries against the database and soforth… so that if the graph generation time was 3 seconds, there would be no 30 second recompile times involved every time you invoke the script. Is that correct?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 3, 2020, 5:25pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/66 "2020-06-03T17:25:11Z")

</div>

> [@dlakelan](#):
>
> For this usage, it seems reasonable to ship a julia system image that includes those packages using PackageCompiler… at that point. I would HOPE that the startup time would be less than 1s before the julia code started executing the queries against the database and soforth… so that if the graph generation time was 3 seconds, there would be no 30 second recompile times involved every time you invoke the script. Is that correct?

Yes, that’s precisely what PackageCompiler will do. Even better, it can give you a relocatable bundle that includes all artifacts and dependencies and Julia itself with a single point of entry.

Were I putting together something to ship, I’d do it as a package. Note that packages have [facilities](https://timholy.github.io/SnoopCompile.jl/stable/) to do even more precompilation than the default. If I _really_ needed to cut down on startup time, I’d use PackageCompiler. Both of those are great as they will automatically install/bundle dependencies.

If you really want to stick to a script paradigm, you could look into using `@nospecialize` or `Base.Experimental.@optlevel 0` (on 1.5) to do more finely tuned versions of `--compile=min` and `-O0` that get included as a part of your script.

I should emphasize that _lots_ of work has gone into improving this latency on 1.5 and it’s continuing to improve.

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [June 3, 2020, 11:49pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/67 "2020-06-03T23:49:26Z")

</div>

> [@dlakelan](#):
>
> `using Queryverse` which is a package that has lots of dependencies which it loads in as well, it takes about 20 seconds. It does that even with --compile=min.

I get much faster for only VegaLite:

```julia
$ time ~/julia-1.6-DEV-latest-7c980c6af5/bin/julia -O0 --compile=min --startup-file=no -e "using VegaLite"
 
real	0m1,928s

```

Queryverse is for Vega-Lite (and more, it’s a meta package), and if you don’t need all the options, consider doing `using` separately for each of the dependencies of this meta-package you actually use (it seem such meta-packages are for convenience, more appropriate for interactive use, to include the “kitchen sink”).

I’m not up-to-speed on these \*Builder, and JLL packages, but I think they promise binary dependencies, as fast as in Python. I’m not sure if it’s used, or what to make of the 2.4 version number (higher than 2.1.3 for VegaLite.jl):

> **[GitHub - queryverse/VegaLiteBuilder](https://github.com/queryverse/VegaLiteBuilder)**
>
> Contribute to queryverse/VegaLiteBuilder development by creating an account on GitHub.

I took a look at the equivalent [Altair Python API](https://altair-viz.github.io/) for Vega-Lite.

The overhead for Julia including startup needs not be more than:

```julia
$ time ~/julia-1.6.0-DEV-8f512f3f6d/bin/julia --compile=min -O0 --startup-file=no -e "using PyCall"

real	0m1,047s

and then I did:

@time py"""
       import altair as alt

       # load a simple dataset as a pandas DataFrame
       from vega_datasets import data
       cars = data.cars()

       alt.Chart(cars).mark_point().encode(
           x='Horsepower',
           y='Miles_per_Gallon',
           color='Origin',
       )
       """
  0.603054 seconds (225 allocations: 10.531 KiB)

[strangely, a bit faster than the same in Python 3, minus its startup overhead.]

$ time python3 vega.py

real	0m0,735s

before I did:

pyimport_conda("altair", "altair")
pyimport_conda("vega_datasets", "vega_datasets")

```

I like better the Julia syntax (see same example):

> **[GitHub - queryverse/VegaLite.jl: Julia bindings to Vega-Lite](https://github.com/queryverse/VegaLite.jl)**
>
> Julia bindings to Vega-Lite. Contribute to queryverse/VegaLite.jl development by creating an account on GitHub.

I’m not saying we should give up and use Python for all your scripts, or use together (while in some cases an option). I hope Julia with binary packages (eventually) loading them can be as fast.

Since it’s not as fast currently, I’ve been thinking, could we compile Julia programs to e.g. Python, Perl or Go or Java? By now unmaintained Julia2C showed it was possible, and having a garbage collector in the target language would make it less difficult, reusing the one it has, and make source-to-source possible, while still being able to run as a script (I find it desirable that code needs not be a binary).

---

<div class="post-metadata">

**Author:** ![bjarthur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bjarthur/32/9638_2.png) [@bjarthur](https://discourse.julialang.org/u/bjarthur)\
**Post date:** [July 27, 2020, 11:23pm UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/68 "2020-07-27T23:23:19Z")

</div>

TIERED COMPILATION!!! what a great idea! any plans for julia to get this? thanks for the [link](http://jaliyaudagedara.blogspot.com/2019/10/net-core-tiered-compilation.html) @palli.

i’ve been thinking for a while that for interactive sessions, it would be nice if two threads were launched everytime a method needed compilation. the first would execute it without compilation for a responsive user interface, and the second would compile it in the background for use the second time the method was called. does that make sense?

much better than the module-level optional compilation coming out in 1.5 i think.

---

<div class="post-metadata">

**Author:** ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)\
**Post date:** [July 28, 2020, 3:01am UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/69 "2020-07-28T03:01:49Z")

</div>

If you are running Julia on a unix system , either Linux or Mac

Please consider DaemonMode when running the Julia program as a scripting language.

> [@\[ANN\] DaemonMode.jl: a package to run faster scripts in Julia](https://discourse.julialang.org/t/ann-daemonmode-jl-a-package-to-run-faster-scripts-in-julia/42384/14):
>
> @Mason I am happy to announce that I have update the Package, and now each file run in its own module, avoiding the conflict of names. After looking for many option, I change the include(fname) to m = Module() content = join(readlines(dname), "\n") include\_string(m, content) and it is currently working! Also, I want to thanks to the users, specially @gsoleilhac and @Palli, for the suggestions and changes. Now: New changes The port can be specified in server and client. Each file is run in…

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [July 28, 2020, 6:50am UTC](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384/70 "2020-07-28T06:50:48Z")

</div>

> [@bjarthur](#):
>
> the first would execute it without compilation for a responsive user interface, and the second would compile it in the background

One problem would be if the method in question takes a very long time to run. You may have to wait hours for the interpreted version to complete, even though the compiled version would only have taken minutes. (And you can’t simply kill the interpreted method and run the compiled version instead, because the method might have side-effects, such as writing to a database.)

A simple heuristic could be to run methods that contain no backward branches interpreted (until background-compilation finishes) but any function that has a loop still gets compiled before the first run.

Each time a compiled function calls a maybe-not-yet-compiled function there would have to be a branch to check if the compiled version is available, and that would slow things down a bit, but probably not as bad as doing a full dynamic dispatch…

[Previous page](https://discourse.julialang.org/t/can-julia-really-be-used-as-a-scripting-language-performance/40384.md?page=3)
