Introduction to

Julia toolchain

Types of computer languages

  • Compiled languages: C/C++, Fortran, ...

    • Directly compiled to machine code that is executed by CPU
    • Pros: fast, memory efficient
    • Cons: longer development time, hard to debug
  • Interpreted language: R, Matlab, Python, SAS IML, JavaScript, ...

    • Interpreted by interpreter
    • Pros: fast prototyping
    • Cons: excruciatingly slow for loops
  • Mixed (dynamic) languages: Matlab (JIT), R (compiler package), Julia, Cython, JAVA, ...

    • Pros and cons: between the compiled and interpreted languages
  • Script languages: Linux shell scripts, Perl, ...

    • Extremely useful for some data preprocessing and manipulation
  • Database languages: SQL, Hadoop.

    • Data analysis never happens if we do not know how to retrieve data from databases

Messages

  • To be versatile in the big data era, master at least one language in each category.

  • To improve efficiency of interpreted languages such as R or Matlab, conventional wisdom is to avoid loops as much as possible. Aka, vectorize code

    The only loop you are allowed to have is that for an iterative algorithm.

  • When looping is necessary, need to code in C, C++, or Fortran.
    Success stories: the popular glmnet package in R is coded in Fortran.

  • Modern languages such as Julia tries to solve the two language problem. That is to achieve efficiency without vectorizing code.

What's Julia?

Julia is a high-level, high-performance dynamic programming language for technical computing, with syntax that is familiar to users of other technical computing environments

  • Started in 2009. First public release in 2012.

    • Creators: Jeff Bezanson, Alan Edelman, Stefan Karpinski, Viral Shah
    • Current release v0.6.2
    • v1.0 is staged to release in 2018
  • Aim to solve the notorious two language problem:

    • Prototype code goes into high-level languages like R/Python, production code goes into low-level language like C/C++

      Walks like Python. Runs like C.

  • Write high-level, abstract code that closely resembles mathematical formulas

    • yet produces fast, low-level machine code that has traditionally only been generated by static languages.
  • Julia is more than just "Fast R" or "Fast Matlab"

    • Performance comes from features that work well together.
    • You can't just take the magic dust that makes Julia fast and sprinkle it on [language of choice]

R is great, but...

  • It's not meant for high performance computing

  • Deficiencies in the core language

    • Many fixed with packages (devtools, roxygen2, Matrix)
    • Others harder to fix (R uses an old version of BLAS)
    • Some impossible to fix (clunky syntax, poor design choices)
  • Only 6 active developers left (out of 20 R-Core members)

  • Doug Bates (member of R-Core, Matrix and lme4)

    • Getting Doug on board was a big win for statistics with Julia, as he brought a lot of knowledge about the history of R development and design choices
    • https://github.com/dmbates/MixedModels.jl

      As some of you may know, I have had a (rather late) mid-life crisis and run off with another language called Julia.

      -- Doug Bates (on the `knitr` Google Group)

Language features of R, Matlab and Julia

Features R Matlab Julia
Open source 👍 👎 👍
IDE RStudio 👍 👍 👍 👍 👍 👍 Atom+Juno 👎
Dynamic document RMarkdown 👍 👍 👍 👍 👍 Jupyter 👍 👍
Multi-threading parallel 👎 👍 👍 👍 see docs
JIT compiler 👎 👍 👍 👍 👍 👍
Call C/Fortran wrapper, Rcpp wrapper no glue code needed
Call shared library wrapper wrapper no glue code needed
Type system 👎 👍 👍 👍 👍 👍
Pass by reference 👎 👎 👍 👍 👍
Linear algebra 👎 MKL, Arpack OpenBLAS, eigpack, or MKL
Distributed computing 👎 👍 👍 👍 👍
Sparse linear algebra Matrix package 👎 👍 👍 👍 👍 👍 👍
Documentation 👍 👍 👍 👍 👍
Profiler 👍 👍 👍 👍 👍 👍

Benchmark

Test R 3.4.3 Matlab R2017a Julia 0.6.2
Matrix creation, trans., deform. (2500 x 2500) 0.65 0.13 0.21
Power of matrix (2400 x 2400, A.^1000) 0.18 0.10 0.18
Quick sort ($n = 7 \times 10^6$) 0.75 0.30 0.65
Cross product (2800 x 2800, $A^TA$) 15.02 0.18 0.23
LS solution ($n = p = 2000$) 7.00 0.07 0.06
FFT ($n = 2,400,000$) 0.32 0.03 0.04
Eigen-values ($600 \times 600$) 0.75 0.22 0.26
Determinant ($2500 \times 2500$) 3.77 0.19 0.14
Cholesky ($3000 \times 3000$) 5.54 0.08 0.17
Matrix inverse ($1600 \times 1600$) 4.13 0.11 0.14
Fibonacci (vector calculation) 0.23 0.16 0.27
Hilbert (matrix calculation) 0.27 0.07 0.06
GCD (recursion) 0.42 0.09 0.16
Toeplitz matrix (loops) 0.32 0.0012 0.0007
Escoufiers (mixed) 0.30 0.15 0.14

Machine specs: Intel i7 @ 2.9GHz (4 physical cores, 8 threads), 16G RAM, Mac OS 10.13.3.

Gibbs sampler example by Doug Bates

  • An example from Dr. Doug Bates's slides Julia for R Programmers.

  • The task is to create a Gibbs sampler for the density
    $$ f(x, y) = k x^2 exp(- x y^2 - y^2 + 2y - 4x), x > 0 $$ using the conditional distributions $$ \begin{eqnarray*} X | Y &\sim& \Gamma \left( 3, \frac{1}{y^2 + 4} \right) \\ Y | X &\sim& N \left(\frac{1}{1+x}, \frac{1}{2(1+x)} \right). \end{eqnarray*} $$

  • This is a Julia function for the simple Gibbs sampler:

In [1]:
using Distributions

function jgibbs(N, thin)
    mat = zeros(N, 2)
    x = y = 0.0
    for i in 1:N
        for j in 1:thin
            x = rand(Gamma(3.0, 1.0 / (y * y + 4.0)))
            y = rand(Normal(1.0 / (x + 1.0), 1.0 / sqrt(2.0(x + 1.0))))
        end
        mat[i, 1] = x
        mat[i, 2] = y
    end
    mat
end
Out[1]:
jgibbs (generic function with 1 method)

Generate a bivariate sample of size 10,000 with a thinning of 500. How long does it take?

In [2]:
jgibbs(100, 5); # warm-up
@elapsed jgibbs(10000, 500)
Out[2]:
0.381052949
  • R solution. The RCall.jl package allows us to execute R code without leaving the Julia environment. We first define an R function Rgibbs().
In [3]:
using RCall

R"""
library(Matrix)
Rgibbs <- function(N, thin) {
  mat <- matrix(0, nrow=N, ncol=2)
  x <- y <- 0
  for (i in 1:N) {
    for (j in 1:thin) {
      x <- rgamma(1, 3, y * y + 4) # 3rd arg is rate
      y <- rnorm(1, 1 / (x + 1), 1 / sqrt(2 * (x + 1)))
    }
    mat[i,] <- c(x, y)
  }
  mat
}
"""
Out[3]:
RCall.RObject{RCall.ClosSxp}
function (N, thin) 
{
    mat <- matrix(0, nrow = N, ncol = 2)
    x <- y <- 0
    for (i in 1:N) {
        for (j in 1:thin) {
            x <- rgamma(1, 3, y * y + 4)
            y <- rnorm(1, 1/(x + 1), 1/sqrt(2 * (x + 1)))
        }
        mat[i, ] <- c(x, y)
    }
    mat
}

and then generate the same number of samples

In [4]:
# benchmark
@elapsed R"""
system.time(Rgibbs(10000, 500))
"""
Out[4]:
18.415733088

We see 40-80 fold speed up of Julia over R on this example, without extra coding effort!

Learning resources

  1. Intro to Julia (1h40m), by Jane Herriman (Dec 19, 2017), and next (monthly) tutorial
    Intro to Julia, by Jane Herriman on April 6, 2018 at 10AM PDT.
  2. Cheat sheet: The Fast Track to Julia.
  3. Browse the Julia documentation.
  4. For Matlab users, read Noteworthy Differences From Matlab.
    For R users, read Noteworthy Differences From R.
    For Python users, read Noteworthy Differences From Python.
  5. The Learning page on Julia's website has pointers to many other learning resources.

Julia REPL (Read-Evaluation-Print-Loop)

The Julia REPL, or Julia shell, has four main modes.

  1. Default mode is the Julian prompt julia>. Type backspace in other modes to enter default mode.

  2. Help mode help?>. Type ? to enter help mode. ?search_term does a fuzzy search for search_term.

  3. Shell mode shell>. Type ; to enter shell mode.

  4. Search mode (reverse-i-search). Press ctrl+R to enter search model.

  5. With RCall.jl package installed, we can enter the R mode by typing $ (shift+4) at Julia REPL.

Some survival commands in Julia REPL:

  1. quit() or Ctrl+D: exit Julia.

  2. Ctrl+C: interrupt execution.

  3. Ctrl+L: clear screen.

  4. whos(): list all variables in current workspace.

  5. workspace(): clear all variables and reset session.

  6. Append ; (semi-colon) to suppress displaying output from a command.

  7. include("filename.jl") to source a Julia code file.

Seek help

Which IDE?

  • I highly recommend the editor Atom with packages julia-client, language-julia, and latex-completions installed.

  • If you want RStudio- or Matlab- like IDE, install the uber-juno package in Atom. Follow instructions at https://github.com/JunoLab/uber-juno/blob/master/setup.md.

  • JuliaPro bundles Julia, Atom, Juno, and many commonly used packages.

  • For homework, I recommend Jupyter Notebook.

    Also worth trying is JupyterLab, which is supposed to replace Jupyter Notebook after it reaches v1.0.

Julia package system

  • Each Julia package is a Git repository. Each Julia package name ends with .jl. E.g., Distributions.jl package lives at https://github.com/JuliaStats/Distributions.jl.
    Google search with PackageName.jl usually leads to the package on github.com.

  • The package ecosystem is rapidly maturing; a complete list of registered packages (which are required to have a certain level of testing and documentation) is at http://pkg.julialang.org/.

  • For example, the package called Distributions.jl is added with

    Pkg.add("Distributions")   # no .jl
    

    and "removed" (although not completely deleted) with

    Pkg.rm("Distributions")
    
  • The package manager provides a dependency solver that determines which packages are actually required to be installed.

  • Non-registered packages are added by cloning the relevant Git repository. E.g.,

    Pkg.clone("git@github.com:OpenMendel/SnpArrays.jl.git")
    
  • A package needs only be added once, at which point it is downloaded into your local .julia/vx.x directory in your home directory.

In [5]:
;ls -l /Users/huazhou/.julia/v0.6 "|" head -20
total 16
drwxr-xr-x    11 huazhou  staff    352 Nov 24 21:46 ASTInterpreter2
drwxr-xr-x    11 huazhou  staff    352 Feb 28 18:40 AbstractFFTs
drwxr-xr-x    11 huazhou  staff    352 Dec 22 06:47 Atom
drwxr-xr-x    15 huazhou  staff    480 Apr  2 08:12 Automa
drwxr-xr-x    10 huazhou  staff    320 Aug  9  2017 AxisAlgorithms
drwxr-xr-x    11 huazhou  staff    352 Feb  7 07:03 AxisArrays
drwxr-xr-x    11 huazhou  staff    352 Aug 12  2017 BGZFStreams
drwxr-xr-x    13 huazhou  staff    416 Feb  7 07:03 BenchmarkTools
drwxr-xr-x    11 huazhou  staff    352 Jan 31 09:03 BinDeps
drwxr-xr-x    13 huazhou  staff    416 Mar 15 10:37 BinaryProvider
drwxr-xr-x    14 huazhou  staff    448 Feb 19 11:35 BioCore
drwxr-xr-x    14 huazhou  staff    448 Mar  4 17:31 BioSequences
drwxr-xr-x    12 huazhou  staff    384 Mar 22 07:14 BioSymbols
drwxr-xr-x    13 huazhou  staff    416 Feb 14 12:35 Blink
drwxr-xr-x    12 huazhou  staff    384 Apr  7 16:15 Blosc
drwxr-xr-x    13 huazhou  staff    416 Mar  4 17:31 BufferedStreams
drwxr-xr-x    11 huazhou  staff    352 Jan 10 12:56 CPLEX
drwxr-xr-x    14 huazhou  staff    448 Oct 24 10:48 CSDP
drwxr-xr-x    13 huazhou  staff    416 Apr 13 15:31 CSV
  • Directory of a specific package can be queried by Pkg.dir():
In [6]:
Pkg.dir("Distributions")
Out[6]:
"/Users/huazhou/.julia/v0.6/Distributions"
  • If you start having problems with packages that seem to be unsolvable, you can try just deleting your .julia directory and reinstalling all your packages.

  • For JuliaPro, the packages are downloaed to directory /Applications/JuliaPro-0.6.2.2.app/Contents/Resources/pkgs-0.6.2.2/v0.6/.

  • Periodically, one should run Pkg.update(), which checks for, downloads and installs updated versions of all the packages you currently have installed.

  • Pkg.status() lists the status of all installed packages.

  • Using functions in package.

    using Distributions
    

    This pulls all of the exported functions in the module into your local namespace, as you can check using the whos() command. An alternative is

    import Distributions
    

    Now, the functions from the Distributions package are available only using

    Distributions.<FUNNAME>
    

    All functions, not only exported functions, are always available like this.

Calling R from Julia

  • The RCall.jl package allows you to embed R code inside of Julia.

  • There are also PyCall, MATLAB, JavaCall, CxxWrap packages.

In [7]:
# Pkg.add("RCall")
using RCall

x = randn(1000)
R"""
hist($x, main="I'm plotting a Julia vector")
"""
Out[7]:
RCall.RObject{RCall.VecSxp}
$breaks
 [1] -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5  0.0  0.5  1.0  1.5  2.0  2.5  3.0

$counts
 [1]   2   1   6  22  55  93 158 184 166 165  85  45  15   3

$density
 [1] 0.004 0.002 0.012 0.044 0.110 0.186 0.316 0.368 0.332 0.330 0.170 0.090
[13] 0.030 0.006

$mids
 [1] -3.75 -3.25 -2.75 -2.25 -1.75 -1.25 -0.75 -0.25  0.25  0.75  1.25  1.75
[13]  2.25  2.75

$xname
[1] "`#JL`$x"

$equidist
[1] TRUE

attr(,"class")
[1] "histogram"
In [8]:
R"""
library(ggplot2)
qplot($x)
"""
Out[8]:
RCall.RObject{RCall.VecSxp}
WARNING: RCall.jl: `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
In [9]:
x = R"""
rnorm(10)
"""
Out[9]:
RCall.RObject{RCall.RealSxp}
 [1]  0.51015405 -0.84060075 -0.99595630  0.33365937 -0.23253892 -1.37439873
 [7] -0.59601796  1.76222020  0.06672042  1.70257300
In [10]:
y = collect(x)
Out[10]:
10-element Array{Float64,1}:
  0.510154 
 -0.840601 
 -0.995956 
  0.333659 
 -0.232539 
 -1.3744   
 -0.596018 
  1.76222  
  0.0667204
  1.70257  
  • Access Julia variables in R REPL mode:

    julia> x = rand(5) # Julia variable
    R> y <- $x
    
  • Pass Julia expression in R REPL mode:

    R> y <- $(rand(5))
    
  • Put Julia variable into R environment:

    julia> @rput x
    R> x
    
  • Get R variable into Julia environment:

    R> r <- 2
    Julia> @rget r
    
  • If you want to call Julia within R, check out the XRJulia package by John Chambers.

Some basic Julia code

In [11]:
y = 1
typeof(y) # same as int in R
Out[11]:
Int64
In [12]:
y = 1.0
typeof(y) # same as double in R
Out[12]:
Float64
In [13]:
# Greek letters:  `\pi<tab>`
π
Out[13]:
π = 3.1415926535897...
In [14]:
# Greek letters:  `\theta<tab>`
θ = y + π
Out[14]:
4.141592653589793
In [15]:
# emoji! `\:kissing_cat:<tab>`
😽 = 5.0
Out[15]:
5.0
In [16]:
α̂ = π
Out[16]:
π = 3.1415926535897...
In [17]:
# vector of Float64 0s
x = zeros(5)
Out[17]:
5-element Array{Float64,1}:
 0.0
 0.0
 0.0
 0.0
 0.0
In [18]:
# vector Int64 0s
x = zeros(Int, 5)
Out[18]:
5-element Array{Int64,1}:
 0
 0
 0
 0
 0
In [19]:
# matrix of Float64 0s
x = zeros(5, 3)
Out[19]:
5×3 Array{Float64,2}:
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0
In [20]:
# matrix of Float64 1s
x = ones(5, 3)
Out[20]:
5×3 Array{Float64,2}:
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
In [21]:
# define array without initialization
x = Array{Float64}(5, 3)
Out[21]:
5×3 Array{Float64,2}:
 2.34728e-314  2.34728e-314  0.0         
 2.34728e-314  2.34728e-314  0.0         
 2.34728e-314  0.0           2.34728e-314
 2.34728e-314  0.0           2.34728e-314
 2.34728e-314  2.34728e-314  0.0         
In [22]:
# fill a matrix by 0s
fill!(x, 0)
Out[22]:
5×3 Array{Float64,2}:
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0
In [23]:
# initialize an array to be 2.5
fill(π, (5, 3))
Out[23]:
5×3 Array{Irrational{:π},2}:
 π = 3.1415926535897...  π = 3.1415926535897...  π = 3.1415926535897...
 π = 3.1415926535897...  π = 3.1415926535897...  π = 3.1415926535897...
 π = 3.1415926535897...  π = 3.1415926535897...  π = 3.1415926535897...
 π = 3.1415926535897...  π = 3.1415926535897...  π = 3.1415926535897...
 π = 3.1415926535897...  π = 3.1415926535897...  π = 3.1415926535897...
In [24]:
a = 3//5
Out[24]:
3//5
In [25]:
typeof(a)
Out[25]:
Rational{Int64}
In [26]:
b = 3//7
Out[26]:
3//7
In [27]:
a + b
Out[27]:
36//35
In [28]:
# uniform [0, 1) random numbers
x = rand(5, 3)
Out[28]:
5×3 Array{Float64,2}:
 0.626985  0.16688   0.796883
 0.83137   0.82617   0.109381
 0.718678  0.820927  0.858267
 0.344819  0.527395  0.848442
 0.52959   0.379976  0.953298
In [29]:
# uniform random numbers (in single precision)
x = rand(Float16, 5, 3)
Out[29]:
5×3 Array{Float16,2}:
 0.56543  0.99023  0.92969
 0.83594  0.58398  0.72461
 0.50488  0.09668  0.82129
 0.15234  0.34375  0.79297
 0.77441  0.96094  0.17676
In [30]:
# random numbers from {1,...,5}
x = rand(1:5, 5, 3)
Out[30]:
5×3 Array{Int64,2}:
 1  1  3
 1  5  4
 2  2  2
 4  4  5
 2  4  3
In [31]:
# standard normal random numbers
x = randn(5, 3)
Out[31]:
5×3 Array{Float64,2}:
 -1.1904    -0.435366  -0.0241442
  0.185787   3.06241    2.37215  
  0.928226   0.582092   1.31479  
  0.570806   0.072032   0.138682 
  0.700352   1.17485   -1.78152  
In [32]:
# range
1:10
Out[32]:
1:10
In [33]:
typeof(1:10)
Out[33]:
UnitRange{Int64}
In [34]:
1:2:10
Out[34]:
1:2:9
In [35]:
typeof(1:2:10)
Out[35]:
StepRange{Int64,Int64}
In [36]:
# integers 1-10
x = collect(1:10)
Out[36]:
10-element Array{Int64,1}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
In [37]:
# Float64 numbers 1-10
x = collect(1.0:10)
Out[37]:
10-element Array{Float64,1}:
  1.0
  2.0
  3.0
  4.0
  5.0
  6.0
  7.0
  8.0
  9.0
 10.0
In [38]:
# convert to a specific type
convert(Vector{Float64}, 1:10)
Out[38]:
10-element Array{Float64,1}:
  1.0
  2.0
  3.0
  4.0
  5.0
  6.0
  7.0
  8.0
  9.0
 10.0

Timing and benchmark

@time, @elapsed, @allocated macros:

In [39]:
srand(123) # seed
x = randn(10000)
@time sum(x) # first run includes compilation time
@time sum(x) # no compilation time after first run
  0.035783 seconds (9.90 k allocations: 546.145 KiB)
  0.000011 seconds (5 allocations: 176 bytes)
Out[39]:
117.13472277852794
In [40]:
@elapsed sum(x)
Out[40]:
9.925e-6
In [41]:
@allocated sum(x)
Out[41]:
16

Use BenchmarkTools.jl for more robust benchmarking. Analog of microbenchmark package in R.

In [42]:
using BenchmarkTools

@benchmark sum(x)
Out[42]:
BenchmarkTools.Trial: 
  memory estimate:  16 bytes
  allocs estimate:  1
  --------------
  minimum time:     2.688 μs (0.00% GC)
  median time:      2.773 μs (0.00% GC)
  mean time:        2.851 μs (0.00% GC)
  maximum time:     13.655 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9
In [43]:
R"""
library(microbenchmark)
microbenchmark(sum($x))
"""
Out[43]:
RCall.RObject{RCall.VecSxp}
Unit: microseconds
         expr    min     lq     mean  median      uq    max neval
 sum(`#JL`$x) 11.599 11.649 11.85592 11.6935 11.7555 24.952   100

Matrices and vectors

Dimensions

In [44]:
x = randn(5, 3)
Out[44]:
5×3 Array{Float64,2}:
  0.897602  -2.00348   -0.205452 
  0.206015   0.166398  -0.0682929
 -0.553413   0.687318   0.0969771
  0.422711   0.835254  -0.329361 
  2.41808    0.650404   0.0921857
In [45]:
size(x)
Out[45]:
(5, 3)
In [46]:
size(x, 1) # nrow() in R
Out[46]:
5
In [47]:
size(x, 2)
Out[47]:
3
In [48]:
# total number of elements
length(x)
Out[48]:
15

Indexing

In [49]:
# 5 × 5 matrix of random Normal(0, 1)
x = randn(5, 5)
Out[49]:
5×5 Array{Float64,2}:
 -0.263946   0.306647    1.3524    -1.54503    0.733192
 -1.58659    1.09814    -0.995732  -1.72885    0.332798
  1.32131    0.708017   -1.18628    0.853263   0.149281
 -1.45118    0.0445099   0.799792  -0.0403455  1.11101 
 -1.41587   -0.59818    -0.202697   1.48291    1.07844 
In [50]:
# first column
x[:, 1]
Out[50]:
5-element Array{Float64,1}:
 -0.263946
 -1.58659 
  1.32131 
 -1.45118 
 -1.41587 
In [51]:
# first row
x[1, :]
Out[51]:
5-element Array{Float64,1}:
 -0.263946
  0.306647
  1.3524  
 -1.54503 
  0.733192
In [52]:
# sub-array
x[1:2, 2:3]
Out[52]:
2×2 Array{Float64,2}:
 0.306647   1.3524  
 1.09814   -0.995732
In [53]:
# getting a subset of a matrix creates a copy, but you can also create "views"
z = view(x, 1:2, 2:3)
Out[53]:
2×2 SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}:
 0.306647   1.3524  
 1.09814   -0.995732
In [54]:
z[2, 2] = 0.0
Out[54]:
0.0
In [55]:
x
Out[55]:
5×5 Array{Float64,2}:
 -0.263946   0.306647    1.3524    -1.54503    0.733192
 -1.58659    1.09814     0.0       -1.72885    0.332798
  1.32131    0.708017   -1.18628    0.853263   0.149281
 -1.45118    0.0445099   0.799792  -0.0403455  1.11101 
 -1.41587   -0.59818    -0.202697   1.48291    1.07844 
In [56]:
# y points to same data as x
y = x
Out[56]:
5×5 Array{Float64,2}:
 -0.263946   0.306647    1.3524    -1.54503    0.733192
 -1.58659    1.09814     0.0       -1.72885    0.332798
  1.32131    0.708017   -1.18628    0.853263   0.149281
 -1.45118    0.0445099   0.799792  -0.0403455  1.11101 
 -1.41587   -0.59818    -0.202697   1.48291    1.07844 
In [57]:
# x and y point to same data
pointer(x), pointer(y)
Out[57]:
(Ptr{Float64} @0x000000011d409a90, Ptr{Float64} @0x000000011d409a90)
In [58]:
# changing y also changes x
y[:, 1] = 0
x
Out[58]:
5×5 Array{Float64,2}:
 0.0   0.306647    1.3524    -1.54503    0.733192
 0.0   1.09814     0.0       -1.72885    0.332798
 0.0   0.708017   -1.18628    0.853263   0.149281
 0.0   0.0445099   0.799792  -0.0403455  1.11101 
 0.0  -0.59818    -0.202697   1.48291    1.07844 
In [59]:
# create a new copy of data
z = copy(x)
Out[59]:
5×5 Array{Float64,2}:
 0.0   0.306647    1.3524    -1.54503    0.733192
 0.0   1.09814     0.0       -1.72885    0.332798
 0.0   0.708017   -1.18628    0.853263   0.149281
 0.0   0.0445099   0.799792  -0.0403455  1.11101 
 0.0  -0.59818    -0.202697   1.48291    1.07844 
In [60]:
pointer(x), pointer(z)
Out[60]:
(Ptr{Float64} @0x000000011d409a90, Ptr{Float64} @0x000000011b049fd0)

Concatenate matrices

In [61]:
x, y, z = randn(5, 3), randn(5, 2), randn(3, 5)
M = [x y]
Out[61]:
5×5 Array{Float64,2}:
 -1.06006   1.85635    -0.758945   -1.85887   -1.82908  
  0.726803  0.266693   -1.76046     1.64189    0.0684493
 -0.196268  0.0508897  -1.02538     0.6103     0.0964332
  0.311368  0.550423    0.0771399  -0.320978  -0.29254  
 -0.042317  0.0801359   0.0557964   0.924308  -0.484027 
In [62]:
M = [x y; z]
Out[62]:
8×5 Array{Float64,2}:
 -1.06006    1.85635    -0.758945   -1.85887   -1.82908  
  0.726803   0.266693   -1.76046     1.64189    0.0684493
 -0.196268   0.0508897  -1.02538     0.6103     0.0964332
  0.311368   0.550423    0.0771399  -0.320978  -0.29254  
 -0.042317   0.0801359   0.0557964   0.924308  -0.484027 
 -0.946129  -1.88499    -0.715218   -0.538779   1.02781  
 -0.247229   0.746322   -2.80947    -0.659972   0.374257 
 -1.86926    0.858622    0.573011   -0.886174  -0.188567 

Dot operation

Dot operation in Julia is elementwise operation.

In [63]:
x = randn(5, 3)
Out[63]:
5×3 Array{Float64,2}:
 -0.37393    0.553298   -1.01762 
  1.08906    0.990025    0.389225
  0.193156  -2.01294     1.87758 
  0.715318   0.143813    1.20991 
 -0.91177   -0.0733957   0.722246
In [64]:
y = ones(5, 3)
Out[64]:
5×3 Array{Float64,2}:
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
In [65]:
x .* y # same x * y in R
Out[65]:
5×3 Array{Float64,2}:
 -0.37393    0.553298   -1.01762 
  1.08906    0.990025    0.389225
  0.193156  -2.01294     1.87758 
  0.715318   0.143813    1.20991 
 -0.91177   -0.0733957   0.722246
In [66]:
x .^ (-2)
Out[66]:
5×3 Array{Float64,2}:
  7.15186     3.26649   0.965667
  0.843133    1.02025   6.60081 
 26.8031      0.246795  0.283664
  1.95435    48.3509    0.683112
  1.2029    185.635     1.91704 
In [67]:
sin.(x)
Out[67]:
5×3 Array{Float64,2}:
 -0.365277   0.525496   -0.850861
  0.886192   0.83604     0.379472
  0.191957  -0.903835    0.953311
  0.655858   0.143318    0.935585
 -0.790589  -0.0733298   0.661071

Basic linear algebra

In [68]:
x = randn(5)
Out[68]:
5-element Array{Float64,1}:
  0.98846  
 -0.804474 
  0.0199801
 -1.32858  
  1.67995  
In [69]:
# vector L2 norm
vecnorm(x)
Out[69]:
2.492388424401137
In [70]:
y = randn(5) # another vector
# dot product
dot(x, y) # x' * y
Out[70]:
0.22692502538805792
In [71]:
x'y
Out[71]:
0.22692502538805792
In [72]:
x, y = randn(5, 3), randn(3, 2)
# matrix multiplication, same as %*% in R
x * y
Out[72]:
5×2 Array{Float64,2}:
  1.26739   -0.422013
  1.07959   -1.25078 
 -0.870933  -2.30028 
 -0.788011  -0.927073
 -1.80023    1.44096 
In [73]:
x = randn(3, 3)
Out[73]:
3×3 Array{Float64,2}:
 -1.64764    0.689513   0.206809
  0.488763  -0.668832   0.991291
 -0.482333  -0.726196  -0.145318
In [74]:
# conjugate transpose
x'
Out[74]:
3×3 Array{Float64,2}:
 -1.64764    0.488763  -0.482333
  0.689513  -0.668832  -0.726196
  0.206809   0.991291  -0.145318
In [75]:
b = rand(3)
x'b # same as x' * b
Out[75]:
3-element Array{Float64,1}:
 -0.701955
 -0.153764
  0.297224
In [76]:
trace(x)
Out[76]:
-2.4617868414631436
In [77]:
det(x)
Out[77]:
-1.7670510948981355
In [78]:
rank(x)
Out[78]:
3

Sparse matrices

In [79]:
# 10-by-10 sparse matrix with sparsity 0.1
X = sprandn(10, 10, .1)
Out[79]:
10×10 SparseMatrixCSC{Float64,Int64} with 10 stored entries:
  [5 ,  1]  =  -0.185574
  [8 ,  2]  =  2.72401
  [9 ,  2]  =  -0.396973
  [4 ,  4]  =  1.67252
  [1 ,  5]  =  0.178644
  [8 ,  6]  =  -0.575279
  [2 ,  7]  =  0.870975
  [6 ,  7]  =  -0.53665
  [10,  8]  =  -0.796616
  [2 , 10]  =  -1.10258
In [80]:
# convert to dense matrix; be cautious when dealing with big data
Xfull = full(X)
Out[80]:
10×10 Array{Float64,2}:
  0.0        0.0       0.0  0.0      …   0.0        0.0       0.0   0.0    
  0.0        0.0       0.0  0.0          0.870975   0.0       0.0  -1.10258
  0.0        0.0       0.0  0.0          0.0        0.0       0.0   0.0    
  0.0        0.0       0.0  1.67252      0.0        0.0       0.0   0.0    
 -0.185574   0.0       0.0  0.0          0.0        0.0       0.0   0.0    
  0.0        0.0       0.0  0.0      …  -0.53665    0.0       0.0   0.0    
  0.0        0.0       0.0  0.0          0.0        0.0       0.0   0.0    
  0.0        2.72401   0.0  0.0          0.0        0.0       0.0   0.0    
  0.0       -0.396973  0.0  0.0          0.0        0.0       0.0   0.0    
  0.0        0.0       0.0  0.0          0.0       -0.796616  0.0   0.0    
In [81]:
# convert a dense matrix to sparse matrix
sparse(Xfull)
Out[81]:
10×10 SparseMatrixCSC{Float64,Int64} with 10 stored entries:
  [5 ,  1]  =  -0.185574
  [8 ,  2]  =  2.72401
  [9 ,  2]  =  -0.396973
  [4 ,  4]  =  1.67252
  [1 ,  5]  =  0.178644
  [8 ,  6]  =  -0.575279
  [2 ,  7]  =  0.870975
  [6 ,  7]  =  -0.53665
  [10,  8]  =  -0.796616
  [2 , 10]  =  -1.10258
In [82]:
# syntax for sparse linear algebra is same as dense linear algebra
β = ones(10)
X * β
Out[82]:
10-element Array{Float64,1}:
  0.178644
 -0.231604
  0.0     
  1.67252 
 -0.185574
 -0.53665 
  0.0     
  2.14873 
 -0.396973
 -0.796616
In [83]:
# many functions apply to sparse matrices as well
sum(X, 2)
Out[83]:
10×1 Array{Float64,2}:
  0.178644
 -0.231604
  0.0     
  1.67252 
 -0.185574
 -0.53665 
  0.0     
  2.14873 
 -0.396973
 -0.796616

Control flow and loops

  • if-elseif-else-end

    if condition1
      # do something
    elseif condition2
      # do something
    else
      # do something
    end
    
  • for loop

    for i in 1:10
      println(i)
    end
    
  • Nested for loop:

    for i in 1:10
      for j in 1:5
          println(i * j)
      end
    end
    

    Same as

    for i in 1:10, j in 1:5
      println(i * j)
    end
    
  • Exit loop:

    for i in 1:10
      # do something
      if condition1
          exit # skip remaining loop
      end
    end
    
  • Exit iteration:

    for i in 1:10
      # do something
      if condition1
          continue # skip to next iteration
      end
      # do something
    end
    

Functions

  • In Julia, all arguments to functions are passed by reference, in contrast to R and Matlab.

  • Function names ending with ! indicates that function mutates at least one argument, typically the first.

    sort!(x) # vs sort(x)
    
  • Function definition

    function func(req1, req2; key1=dflt1, key2=dflt2)
      # do stuff
      return out1, out2, out3
    end
    

    Required arguments are separated with a comma and use the positional notation.
    Optional arguments need a default value in the signature.
    Semicolon is not required in function call.
    return statement is optional.
    Multiple outputs can be returned as a tuple, e.g., return out1, out2, out3.

  • Anonymous functions, e.g., x -> x^2, is commonly used in collection function or list comprehensions.

    map(x -> x^2, y) # square each element in x
    
  • Functions can be nested:

    function outerfunction()
      # do some outer stuff
      function innerfunction()
          # do inner stuff
          # can access prior outer definitions
      end
      # do more outer stuff
    end
    
  • Functions can be vectorized using the Dot syntax:

In [84]:
function myfunc(x)
    return sin(x^2)
end

x = randn(5, 3)
myfunc.(x)
Out[84]:
5×3 Array{Float64,2}:
 0.179536     0.0661559  0.0298338
 0.794468     0.139424   0.561255 
 0.209101     0.294354   0.769808 
 0.000414421  0.255897   0.351913 
 0.021371     0.38493    0.663682 
  • Collection function (think this as the series of apply functions in R).

    Apply a function to each element of a collection:

    map(f, coll) # or
    map(coll) do elem
      # do stuff with elem
      # must contain return
    end
    
In [85]:
map(x -> sin(x^2), x)
Out[85]:
5×3 Array{Float64,2}:
 0.179536     0.0661559  0.0298338
 0.794468     0.139424   0.561255 
 0.209101     0.294354   0.769808 
 0.000414421  0.255897   0.351913 
 0.021371     0.38493    0.663682 
In [86]:
map(x) do elem
    elem = elem^2
    return sin(elem)
end
Out[86]:
5×3 Array{Float64,2}:
 0.179536     0.0661559  0.0298338
 0.794468     0.139424   0.561255 
 0.209101     0.294354   0.769808 
 0.000414421  0.255897   0.351913 
 0.021371     0.38493    0.663682 
In [87]:
# Mapreduce
mapreduce(x -> sin(x^2), +, x)
Out[87]:
4.722142023166778
In [88]:
# same as
sum(x -> sin(x^2), x)
Out[88]:
4.722142023166778
  • List comprehension
In [89]:
[sin(2i + j) for i in 1:5, j in 1:3] # similar to Python
Out[89]:
5×3 Array{Float64,2}:
  0.14112   -0.756802  -0.958924
 -0.958924  -0.279415   0.656987
  0.656987   0.989358   0.412118
  0.412118  -0.544021  -0.99999 
 -0.99999   -0.536573   0.420167

Type system

  • When thinking about types, think about sets.

  • Everything is a subtype of the abstract type Any.

  • An abstract type defines a set of types

    • Consider types in Julia that are a Number:

  • You can explore type hierarchy with typeof(), supertype(), and subtypes().
In [90]:
typeof(1.0), typeof(1)
Out[90]:
(Float64, Int64)
In [91]:
supertype(Float64)
Out[91]:
AbstractFloat
In [92]:
subtypes(AbstractFloat)
Out[92]:
4-element Array{Union{DataType, UnionAll},1}:
 BigFloat
 Float16 
 Float32 
 Float64 
In [93]:
# Is Float64 a subtype of AbstractFloat?
Float64 <: AbstractFloat
Out[93]:
true
In [94]:
# On 64bit machine, Int == Int64
Int == Int64
Out[94]:
true
In [95]:
convert(Float64, 1)
Out[95]:
1.0
In [96]:
randn(Float32, 5)
Out[96]:
5-element Array{Float32,1}:
 -0.510323
 -0.361734
  0.719499
  0.392724
 -0.15274 
In [97]:
convert(Vector{Float64}, randn(Float32, 5))
Out[97]:
5-element Array{Float64,1}:
 -1.00066 
 -0.852047
  0.052183
  0.935551
 -0.547745
In [98]:
convert(Int, 1.0)
Out[98]:
1
In [99]:
convert(Int, 1.5) # should use round(1.5)
InexactError()

Stacktrace:
 [1] convert(::Type{Int64}, ::Float64) at ./float.jl:679
 [2] include_string(::String, ::String) at ./loading.jl:522
In [100]:
round(Int, 1.5)
Out[100]:
2

Multiple dispatch

  • Multiple dispatch lies in the core of Julia design. It allows built-in and user-defined functions to be overloaded for different combinations of argument types.

  • Let's consider a simple "doubling" function:

In [101]:
g(x) = x + x
Out[101]:
g (generic function with 1 method)
In [102]:
g(1.5)
Out[102]:
3.0

This definition is too broad, since some things can't be added

In [103]:
g("hello world")
MethodError: no method matching +(::String, ::String)
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:424

Stacktrace:
 [1] g(::String) at ./In[101]:1
 [2] include_string(::String, ::String) at ./loading.jl:522
  • This definition is correct but too restrictive, since any Number can be added.
In [104]:
g(x::Float64) = x + x
Out[104]:
g (generic function with 2 methods)
  • This will automatically work on the entire type tree above!
In [105]:
g(x::Number) = x + x
Out[105]:
g (generic function with 3 methods)

This is a lot nicer than

function g(x)
    if isa(x, Number)
        return x + x
    else
        throw(ArgumentError("x should be a number"))
    end
end
  • methods(func) function display all methods defined for func.
In [106]:
methods(g)
Out[106]:
3 methods for generic function g:
  • g(x::Float64) at In[104]:1
  • g(x::Number) at In[105]:1
  • g(x) at In[101]:1
  • @which func(x) marco tells which method is being used for argument signature x.
In [107]:
x = 1
typeof(x)
Out[107]:
Int64
In [108]:
g(x)
Out[108]:
2
In [109]:
@which g(x)
Out[109]:
g(x::Number) at In[105]:1
In [110]:
x = randn(5)
@which g(x)
Out[110]:
g(x) at In[101]:1
In [111]:
g(x)
Out[111]:
5-element Array{Float64,1}:
  3.90284 
  2.33685 
 -0.460847
 -0.411265
  0.775717

Just-in-time compilation (JIT)

Following figures and some examples are taken from Arch D. Robinson's slides Introduction to Writing High Performance Julia.

Julia toolchain Julia toolchain
  • Julia's efficiency results from its capabilities to infer the types of all variables within a function and then call LLVM to generate optimized machine code at run-time.
In [112]:
workspace() # clear previous definition of g
g(x::Number) = x + x
Out[112]:
g (generic function with 1 method)

This function will work on any type which has a method for +.

In [113]:
@show g(2)
@show g(2.0);
g(2) = 4
g(2.0) = 4.0
In [114]:
@code_lowered g(2)
Out[114]:
CodeInfo(:(begin 
        nothing
        return x + x
    end))

Type inference:

In [115]:
@code_warntype g(2)
Variables:
  #self# <optimized out>
  x::Int64

Body:
  begin 
      return (Base.add_int)(x::Int64, x::Int64)::Int64
  end::Int64
In [116]:
@code_warntype g(2.0)
Variables:
  #self# <optimized out>
  x::Float64

Body:
  begin 
      return (Base.add_float)(x::Float64, x::Float64)::Float64
  end::Float64

Peek at the compiled LLVM bitcode with @code_llvm

In [117]:
@code_llvm g(2)
define i64 @julia_g_64864(i64) #0 !dbg !5 {
top:
  %1 = shl i64 %0, 1
  ret i64 %1
}
In [118]:
@code_llvm g(2.0)
define double @julia_g_64870(double) #0 !dbg !5 {
top:
  %1 = fadd double %0, %0
  ret double %1
}

We didn't provide a type annotation. But different LLVM code gets generated depending on the argument type!

  • In R or Python, g(2) and g(2.0) would use the same code for both.

  • In Julia, g(2) and g(2.0) dispatches to optimized code for Int64 and Float64, respectively.

  • For integer input x, LLVM compiler is smart enough to know x + x is simple shifting x by 1 bit, which is faster than addition.

Lowest level is the assembly code, which is machine dependent.

In [119]:
@code_native g(2)
	.section	__TEXT,__text,regular,pure_instructions
Filename: In[112]
	pushq	%rbp
	movq	%rsp, %rbp
Source line: 2
	leaq	(%rdi,%rdi), %rax
	popq	%rbp
	retq
	nopw	(%rax,%rax)
In [120]:
@code_native g(2.0)
	.section	__TEXT,__text,regular,pure_instructions
Filename: In[112]
	pushq	%rbp
	movq	%rsp, %rbp
Source line: 2
	addsd	%xmm0, %xmm0
	popq	%rbp
	retq
	nopw	(%rax,%rax)

Profiling Julia code

Julia has several built-in tools for profiling. The @time marco outputs run time and heap allocation.

In [121]:
function tally(x)
    s = 0
    for v in x
        s += v
    end
    s
end

a = rand(10000)
@time tally(a) # first run: include compile time
  0.011205 seconds (31.33 k allocations: 538.291 KiB)
Out[121]:
4981.321489638115
In [122]:
@time tally(a)
  0.000253 seconds (30.00 k allocations: 468.906 KiB)
Out[122]:
4981.321489638115

For more robust benchmarking, the BenchmarkTools.jl package is highly recommended.

In [123]:
using BenchmarkTools

@benchmark tally(a)
WARNING: Method definition endof(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition ==(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:880 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:880.
WARNING: Method definition get(Union{Function, Type{T} where T}, Main.Base.EnvHash, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1036 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1036.
WARNING: Method definition ceil(Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:734 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:734.
WARNING: Method definition ceil(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:789 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:789.
WARNING: Method definition in(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:889 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:889.
WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), AbstractArray{T, 1} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:530 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:530.
WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), AbstractArray{T, 1} where T, AbstractArray{T, 1} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:531 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:531.
WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), AbstractArray{T, 2} where T, Int64) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:534 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:534.
WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), Union{AbstractArray{T, 1}, AbstractArray{T, 2}} where T, Union{AbstractArray{T, 1}, AbstractArray{T, 2}} where T, Int64) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:537 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:537.
WARNING: Method definition length(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition findlast(AbstractString, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1563 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1563.
WARNING: Method definition done(Main.Base.Cmd, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419.
WARNING: Method definition _redirect_stdin(IO) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18.
WARNING: Method definition findfirst(Main.Base.Regex, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1532 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1532.
WARNING: Method definition findfirst(AbstractString, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1547 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1547.
WARNING: Method definition #replace(Array{Any, 1}, typeof(Main.Base.replace), AbstractString, Main.Base.Pair{A, B} where B where A) in module Compat overwritten in module Compat.
WARNING: Method definition _redirect_stdout(IO) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18.
WARNING: Method definition spdiagm(Main.Base.Pair{A, B} where B where A...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:942 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:942.
WARNING: Method definition _redirect_stderr(IO) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18.
WARNING: Method definition getindex(Main.Base.Cmd, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419.
WARNING: Method definition convert(Type{Void}, Void) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1145 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1145.
WARNING: Method definition convert(Type{Void}, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1144 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1144.
WARNING: Method definition convert(Type{Union{T, Void}}, Any) in module Compat at string:8 overwritten in module Compat at string:8.
WARNING: Method definition include_string(Module, String, String) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:71 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:71.
WARNING: Method definition include_string(Module, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73.
WARNING: Method definition include_string(Module, AbstractString, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73.
WARNING: Method definition start(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition expand(Module, ANY) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:69 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:69.
WARNING: Method definition floor(Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, T<:Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:705 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:705.
WARNING: Method definition floor(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:788 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:788.
WARNING: Method definition ntuple(F, Main.Base.Val{N}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:445 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:445.
WARNING: Method definition reshape(AbstractArray{T, N} where N where T, Main.Base.Val{N}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:443 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:443.
WARNING: Method definition values(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:614 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:614.
WARNING: Method definition findnext(Main.Base.Regex, AbstractString, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1531 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1531.
WARNING: Method definition findnext(AbstractString, AbstractString, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1546 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1546.
WARNING: Method definition contains(AbstractString, Main.Base.Regex) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1197 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1197.
WARNING: Method definition repr(Union{AbstractString, Main.Base.MIME{mime} where mime}, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1576 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1576.
WARNING: Method definition eachindex(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition macroexpand(Module, ANY) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:70 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:70.
WARNING: Method definition eltype(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition keys(AbstractArray{T, 1} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:611 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:611.
WARNING: Method definition keys(AbstractArray{T, N} where N where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:610 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:610.
WARNING: Method definition keys(Main.Base.IndexStyle, AbstractArray{T, N} where N where T, AbstractArray{T, N} where N where T...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:612 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:612.
WARNING: Method definition read(Main.Base.Cmd, Type{String}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:493 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:493.
WARNING: Method definition read(AbstractString, Type{String}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:492 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:492.
WARNING: Method definition read(IO, Type{String}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:491 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:491.
WARNING: Method definition first(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition next(Main.Base.Cmd, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419.
WARNING: Method definition replace(AbstractString, Main.Base.Pair{A, B} where B where A) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1184 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1184.
WARNING: Method definition rtoldefault(Any, Any, Real) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:625 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:625.
WARNING: Method definition round(Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Rounding.RoundingMode{:NearestTiesUp}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:778 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:778.
WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:787 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:787.
WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period, Main.Base.Rounding.RoundingMode{:Down}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:783 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:783.
WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period, Main.Base.Rounding.RoundingMode{:Up}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:784 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:784.
WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period, Main.Base.Rounding.RoundingMode{T} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:786 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:786.
WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791.
WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}, Main.Base.Rounding.RoundingMode{T} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791.
WARNING: Method definition last(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.
WARNING: Method definition isequal(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:884 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:884.
WARNING: Method definition *(Union{Char, AbstractString}, Union{Char, AbstractString}...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:929 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:929.
WARNING: Method definition findprev(AbstractString, AbstractString, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1562 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1562.
WARNING: Method definition diagm(Main.Base.Pair{A, B} where B where A...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:952 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:952.
WARNING: Method definition logdet(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:451 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:451.
WARNING: Method definition chol(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Any...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:458 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:458.
WARNING: Method definition chol!(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:457 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:457.
WARNING: Method definition (::Type{DomainError})(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:503 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:503.
WARNING: Method definition (::Type{DomainError})(Any, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:504 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:504.
WARNING: Method definition (::Type{OverflowError})(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:509 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:509.
WARNING: Method definition (::Type{InexactError})(Symbol, Any, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:498 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:498.
WARNING: Method definition (::Type{Main.Base.IOContext{IO_t} where IO_t<:IO})(Main.Base.IOContext{IO_t} where IO_t<:IO, Main.Base.Pair{A, B} where B where A, Main.Base.Pair{A, B} where B where A) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1003 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1003.
WARNING: Method definition (::Type{Main.Base.IOContext{IO_t} where IO_t<:IO})(IO, Main.Base.Pair{A, B} where B where A, Main.Base.Pair{A, B} where B where A, Main.Base.Pair{A, B} where B where A...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1001 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1001.
WARNING: Method definition (::Type{Main.Base.Val{T} where T})(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:440 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:440.
WARNING: Method definition (::Type{Array{T, 2}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:973 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:973.
WARNING: Method definition (::Type{Array{T, 2}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:974 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:974.
WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti} where Ti<:Integer})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:977 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:977.
WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:976 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:976.
WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti} where Ti<:Integer})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:978 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:978.
WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:980 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:980.
WARNING: Method definition (::Type{Array{T, N} where N})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:992 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:992.
WARNING: Method definition (::Type{Array{T, N} where N})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:993 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:993.
WARNING: Method definition (::Type{Array{T, 2} where T})(Main.Base.LinAlg.UniformScaling{T}, Any...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:996 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:996.
Out[123]:
BenchmarkTools.Trial: 
  memory estimate:  468.75 KiB
  allocs estimate:  30000
  --------------
  minimum time:     174.517 μs (0.00% GC)
  median time:      181.316 μs (0.00% GC)
  mean time:        210.472 μs (8.43% GC)
  maximum time:     1.517 ms (78.50% GC)
  --------------
  samples:          10000
  evals/sample:     1

We see the memory allocation (468.75 KiB, average 10.73% GC) is suspiciously high.

The Profile module gives line by line profile results.

In [124]:
Profile.clear()
@profile tally(a)
Profile.print(format=:flat)
 Count File                        Line Function                               
     2 ./abstractarray.jl           573 copy!(::Array{Any,1}, ::Core.Infere... 
     2 ./array.jl                   396 _collect(::Type{Any}, ::Core.Infere... 
     2 ./array.jl                   393 collect(::Type{Any}, ::Core.Inferen... 
     2 ./generator.jl                45 next(::Core.Inference.Generator{Arr... 
     1 ./inference.jl              4224 #198                                   
     2 ./inference.jl              1897 abstract_call(::Any, ::Array{Any,1}... 
     2 ./inference.jl              1420 abstract_call_gf_by_type(::Any, ::A... 
     4 ./inference.jl              1950 abstract_eval(::Any, ::Array{Any,1}... 
     2 ./inference.jl              1901 abstract_eval_call(::Expr, ::Array{... 
     2 ./inference.jl              1927 abstract_eval_call(::Expr, ::Array{... 
     1 ./inference.jl              4224 inline_worthy(::Expr, ::Int64)         
     1 ./inference.jl              2885 isinlineable(::Method, ::CodeInfo)     
     3 ./inference.jl              3290 occurs_more(::Any, ::Core.Inference... 
     1 ./inference.jl              3297 occurs_more(::Any, ::Core.Inference... 
     1 ./inference.jl              2963 optimize(::Core.Inference.Inference... 
     2 ./inference.jl              2787 typeinf(::Core.Inference.InferenceS... 
     1 ./inference.jl              2816 typeinf(::Core.Inference.InferenceS... 
     1 ./inference.jl              2583 typeinf_code(::Core.MethodInstance,... 
     2 ./inference.jl              2535 typeinf_edge(::Method, ::Any, ::Sim... 
     1 ./inference.jl              2622 typeinf_ext(::Core.MethodInstance, ... 
     1 ./inference.jl              2504 typeinf_frame(::Core.MethodInstance... 
     2 ./inference.jl              2722 typeinf_work(::Core.Inference.Infer... 
     1 ./loading.jl                 522 include_string(::String, ::String)     
     1 ./task.jl                    335 (::LastMain.IJulia.##14#17)()          
     1 ....6/Compat/src/Compat.jl    71 include_string(::Module, ::String, ... 
     1 ....6/Compat/src/Compat.jl   385 (::LastMain.Compat.#inner#17{Array{... 
     1 ...IJulia/src/eventloop.jl     8 eventloop(::LastMain.ZMQ.Socket)       
     1 .../src/execute_request.jl   158 execute_request(::LastMain.ZMQ.Sock... 

One can use ProfileView package for better visualization of profile data:

using ProfileView

ProfileView.view()
In [125]:
@code_warntype tally(a)
Variables:
  #self# <optimized out>
  x::Array{Float64,1}
  v::Float64
  #temp#@_4::Int64
  s::Union{Float64, Int64}
  #temp#@_6::Core.MethodInstance
  #temp#@_7::Float64

Body:
  begin 
      s::Union{Float64, Int64} = 0 # line 3:
      #temp#@_4::Int64 = 1
      4: 
      unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)((Base.arraylen)(x::Array{Float64,1})::Int64, 1)::Int64)::Bool)::Bool goto 29
      SSAValue(2) = (Base.arrayref)(x::Array{Float64,1}, #temp#@_4::Int64)::Float64
      SSAValue(3) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64
      v::Float64 = SSAValue(2)
      #temp#@_4::Int64 = SSAValue(3) # line 4:
      unless (s::Union{Float64, Int64} isa Int64)::Bool goto 14
      #temp#@_6::Core.MethodInstance = MethodInstance for +(::Int64, ::Float64)
      goto 23
      14: 
      unless (s::Union{Float64, Int64} isa Float64)::Bool goto 18
      #temp#@_6::Core.MethodInstance = MethodInstance for +(::Float64, ::Float64)
      goto 23
      18: 
      goto 20
      20: 
      #temp#@_7::Float64 = (s::Union{Float64, Int64} + v::Float64)::Float64
      goto 25
      23: 
      #temp#@_7::Float64 = $(Expr(:invoke, :(#temp#@_6), :(Main.+), :(s), :(v)))
      25: 
      s::Union{Float64, Int64} = #temp#@_7::Float64
      27: 
      goto 4
      29:  # line 6:
      return s::Union{Float64, Int64}
  end::Union{Float64, Int64}

Memory profiling

Detailed memory profiling requires a detour. First let's write a script bar.jl, which contains the workload function tally and a wrapper for profiling.

In [126]:
;cat bar.jl
function tally(x)
    s = 0
    for v in x
        s += v
    end
    s
end

# call workload from wrapper to avoid misattribution bug
function wrapper()
    y = rand(10000)
    # force compilation
    println(tally(y))
    # clear allocation counters
    Profile.clear_malloc_data()
    # run compiled workload
    println(tally(y))
end

wrapper()

Next, in terminal, we run the script with --track-allocation=user option.

In [127]:
;julia --track-allocation=user bar.jl
5022.074157476412
5022.074157476412

The profiler outputs a file bar.jl.mem.

In [128]:
;cat bar.jl.mem
        - function tally(x)
        0     s = 0
        0     for v in x
   479984         s += v
        -     end
        0     s
        - end
        - 
        - # call workload from wrapper to avoid misattribution bug
        - function wrapper()
        0     y = rand(10000)
        -     # force compilation
        0     println(tally(y))
        -     # clear allocation counters
        0     Profile.clear_malloc_data()
        -     # run compiled workload
      192     println(tally(y))
        - end
        - 
        - wrapper()
        - 

We see line 4 is allocating suspicious amount of heap memory.

Type stability

The key to writing performant Julia code is to be type stable, such that Julia is able to infer types of all variables and output of a function from the types of input arguments.

Is the tally function type stable? How to diagnose and fix it?

In [129]:
@code_warntype tally(rand(100))
Variables:
  #self# <optimized out>
  x::Array{Float64,1}
  v::Float64
  #temp#@_4::Int64
  s::Union{Float64, Int64}
  #temp#@_6::Core.MethodInstance
  #temp#@_7::Float64

Body:
  begin 
      s::Union{Float64, Int64} = 0 # line 3:
      #temp#@_4::Int64 = 1
      4: 
      unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)((Base.arraylen)(x::Array{Float64,1})::Int64, 1)::Int64)::Bool)::Bool goto 29
      SSAValue(2) = (Base.arrayref)(x::Array{Float64,1}, #temp#@_4::Int64)::Float64
      SSAValue(3) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64
      v::Float64 = SSAValue(2)
      #temp#@_4::Int64 = SSAValue(3) # line 4:
      unless (s::Union{Float64, Int64} isa Int64)::Bool goto 14
      #temp#@_6::Core.MethodInstance = MethodInstance for +(::Int64, ::Float64)
      goto 23
      14: 
      unless (s::Union{Float64, Int64} isa Float64)::Bool goto 18
      #temp#@_6::Core.MethodInstance = MethodInstance for +(::Float64, ::Float64)
      goto 23
      18: 
      goto 20
      20: 
      #temp#@_7::Float64 = (s::Union{Float64, Int64} + v::Float64)::Float64
      goto 25
      23: 
      #temp#@_7::Float64 = $(Expr(:invoke, :(#temp#@_6), :(Main.+), :(s), :(v)))
      25: 
      s::Union{Float64, Int64} = #temp#@_7::Float64
      27: 
      goto 4
      29:  # line 6:
      return s::Union{Float64, Int64}
  end::Union{Float64, Int64}

In this case, Julia fails to infer the type of the reduction variable s, which has to be boxed in heap memory at run time.

This is the generated LLVM bitcode, which is unsually long and contains lots of box:

In [130]:
@code_llvm tally(rand(100))
define { i8**, i8 } @julia_tally_64973([8 x i8]* noalias nocapture, i8** dereferenceable(40)) #0 !dbg !5 {
top:
  %2 = call i8**** @jl_get_ptls_states() #5
  %3 = alloca [8 x i8**], align 8
  %.sub = getelementptr inbounds [8 x i8**], [8 x i8**]* %3, i64 0, i64 0
  %4 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 5
  %5 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 2
  %6 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 4
  %7 = bitcast i8*** %4 to i8*
  call void @llvm.memset.p0i8.i32(i8* %7, i8 0, i32 24, i32 8, i1 false)
  %8 = bitcast [8 x i8**]* %3 to i64*
  %9 = bitcast i8*** %5 to i8*
  call void @llvm.memset.p0i8.i64(i8* %9, i8 0, i64 16, i32 8, i1 false)
  store i64 12, i64* %8, align 8
  %10 = bitcast i8**** %2 to i64*
  %11 = load i64, i64* %10, align 8
  %12 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 1
  %13 = bitcast i8*** %12 to i64*
  store i64 %11, i64* %13, align 8
  store i8*** %.sub, i8**** %2, align 8
  store i8** null, i8*** %6, align 8
  %14 = getelementptr inbounds i8*, i8** %1, i64 1
  %15 = bitcast i8** %14 to i64*
  %16 = load i64, i64* %15, align 8
  %17 = icmp eq i64 %16, 0
  br i1 %17, label %L29, label %if.lr.ph

if.lr.ph:                                         ; preds = %top
  %18 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 3
  %19 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 7
  %20 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 6
  %21 = getelementptr i8*, i8** %1, i64 3
  %22 = bitcast i8** %21 to i64*
  %23 = bitcast i8** %1 to double**
  %24 = bitcast i8**** %2 to i8*
  br label %if

if:                                               ; preds = %if.lr.ph, %L25
  %.033 = phi i8 [ 1, %if.lr.ph ], [ 2, %L25 ]
  %"#temp#.032" = phi i64 [ 1, %if.lr.ph ], [ %40, %L25 ]
  %s.sroa.0.031 = phi i64 [ 0, %if.lr.ph ], [ %"#temp#2.sroa.0.0", %L25 ]
  %25 = add i64 %"#temp#.032", -1
  %26 = load i64, i64* %22, align 8
  %27 = icmp ult i64 %25, %26
  br i1 %27, label %idxend, label %oob

L29.loopexit:                                     ; preds = %L25
  br label %L29

L29:                                              ; preds = %L29.loopexit, %top
  %s.sroa.0.0.lcssa = phi i64 [ 0, %top ], [ %"#temp#2.sroa.0.0", %L29.loopexit ]
  %.0.lcssa = phi i8 [ 1, %top ], [ 2, %L29.loopexit ]
  %trunc16 = trunc i8 %.0.lcssa to i2
  switch i2 %trunc16, label %union_move_skip11 [
    i2 1, label %union_move13
    i2 -2, label %union_move14
  ]

oob:                                              ; preds = %if
  %28 = alloca i64, align 8
  store i64 %"#temp#.032", i64* %28, align 8
  call void @jl_bounds_error_ints(i8** nonnull %1, i64* nonnull %28, i64 1)
  unreachable

idxend:                                           ; preds = %if
  %29 = load double*, double** %23, align 8
  %30 = getelementptr double, double* %29, i64 %25
  %31 = bitcast double* %30 to i64*
  %32 = load i64, i64* %31, align 8
  %33 = icmp eq i8 %.033, 1
  %. = select i1 %33, i8** inttoptr (i64 4780560272 to i8**), i8** inttoptr (i64 4459704976 to i8**)
  store i8** %., i8*** %5, align 8
  store i8** inttoptr (i64 4417465624 to i8**), i8*** %4, align 8
  %trunc = trunc i8 %.033 to i2
  switch i2 %trunc, label %box_union_isboxed [
    i2 1, label %box_union
    i2 -2, label %box_union4
  ]

box_union_isboxed:                                ; preds = %idxend
  call void @llvm.trap()
  unreachable

box_union:                                        ; preds = %idxend
  %34 = call i8** @jl_box_int64(i64 signext %s.sroa.0.031)
  br label %L25

box_union4:                                       ; preds = %idxend
  %35 = call i8** @jl_gc_pool_alloc(i8* %24, i32 1384, i32 16)
  %36 = getelementptr i8*, i8** %35, i64 -1
  %37 = bitcast i8** %36 to i8***
  store i8** inttoptr (i64 4417870416 to i8**), i8*** %37, align 8
  %38 = bitcast i8** %35 to i64*
  store i64 %s.sroa.0.031, i64* %38, align 8
  br label %L25

L25:                                              ; preds = %box_union, %box_union4
  %39 = phi i8** [ %34, %box_union ], [ %35, %box_union4 ]
  %40 = add i64 %"#temp#.032", 1
  store i8** %39, i8*** %20, align 8
  %41 = call i8** @jl_gc_pool_alloc(i8* %24, i32 1384, i32 16)
  %42 = getelementptr i8*, i8** %41, i64 -1
  %43 = bitcast i8** %42 to i8***
  store i8** inttoptr (i64 4417870416 to i8**), i8*** %43, align 8
  %44 = bitcast i8** %41 to i64*
  store i64 %32, i64* %44, align 8
  store i8** %41, i8*** %19, align 8
  %45 = call i8** @jl_invoke(i8** %., i8*** %4, i32 3)
  store i8** %45, i8*** %18, align 8
  %"#temp#2.sroa.0.0.in" = bitcast i8** %45 to i64*
  %"#temp#2.sroa.0.0" = load i64, i64* %"#temp#2.sroa.0.0.in", align 8
  %46 = load i64, i64* %15, align 8
  %47 = icmp eq i64 %"#temp#.032", %46
  br i1 %47, label %L29.loopexit, label %if

union_move_skip11:                                ; preds = %L29
  call void @llvm.trap()
  unreachable

post_union_move12:                                ; preds = %union_move14, %union_move13
  %48 = bitcast [8 x i8]* %0 to i8**
  %49 = insertvalue { i8**, i8 } undef, i8** %48, 0
  %50 = insertvalue { i8**, i8 } %49, i8 %.0.lcssa, 1
  %51 = load i64, i64* %13, align 8
  store i64 %51, i64* %10, align 8
  ret { i8**, i8 } %50

union_move13:                                     ; preds = %L29
  %52 = bitcast [8 x i8]* %0 to i64*
  store i64 %s.sroa.0.0.lcssa, i64* %52, align 1
  br label %post_union_move12

union_move14:                                     ; preds = %L29
  %53 = bitcast [8 x i8]* %0 to i64*
  store i64 %s.sroa.0.0.lcssa, i64* %53, align 1
  br label %post_union_move12
}

What's the fix?

In [131]:
function tally2(x)
    s = zero(eltype(x))
    for v in x
        s += v
    end
    s
end
Out[131]:
tally2 (generic function with 1 method)
In [132]:
@benchmark tally2(a)
Out[132]:
BenchmarkTools.Trial: 
  memory estimate:  16 bytes
  allocs estimate:  1
  --------------
  minimum time:     10.588 μs (0.00% GC)
  median time:      10.639 μs (0.00% GC)
  mean time:        11.652 μs (0.00% GC)
  maximum time:     38.698 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

Much shorter LLVM bitcode:

In [133]:
@code_llvm tally2(a)
define double @julia_tally2_65300(i8** dereferenceable(40)) #0 !dbg !5 {
top:
  %1 = getelementptr inbounds i8*, i8** %0, i64 1
  %2 = bitcast i8** %1 to i64*
  %3 = load i64, i64* %2, align 8
  %4 = icmp eq i64 %3, 0
  br i1 %4, label %L14, label %if.lr.ph

if.lr.ph:                                         ; preds = %top
  %5 = getelementptr i8*, i8** %0, i64 3
  %6 = bitcast i8** %5 to i64*
  %7 = load i64, i64* %6, align 8
  %8 = bitcast i8** %0 to double**
  %9 = load double*, double** %8, align 8
  br label %if

if:                                               ; preds = %if.lr.ph, %idxend
  %s.06 = phi double [ 0.000000e+00, %if.lr.ph ], [ %16, %idxend ]
  %"#temp#.05" = phi i64 [ 1, %if.lr.ph ], [ %15, %idxend ]
  %10 = add i64 %"#temp#.05", -1
  %11 = icmp ult i64 %10, %7
  br i1 %11, label %idxend, label %oob

L14.loopexit:                                     ; preds = %idxend
  br label %L14

L14:                                              ; preds = %L14.loopexit, %top
  %s.0.lcssa = phi double [ 0.000000e+00, %top ], [ %16, %L14.loopexit ]
  ret double %s.0.lcssa

oob:                                              ; preds = %if
  %12 = alloca i64, align 8
  store i64 %"#temp#.05", i64* %12, align 8
  call void @jl_bounds_error_ints(i8** nonnull %0, i64* nonnull %12, i64 1)
  unreachable

idxend:                                           ; preds = %if
  %13 = getelementptr double, double* %9, i64 %10
  %14 = load double, double* %13, align 8
  %15 = add i64 %"#temp#.05", 1
  %16 = fadd double %s.06, %14
  %17 = icmp eq i64 %"#temp#.05", %3
  br i1 %17, label %L14.loopexit, label %if
}

Plotting in Julia

The three most popular options (as far as I know) in Julia are

  • Plots.jl
    • Defines an unified interface for plotting
    • maps arguments to different plotting "backends"
      • PyPlot, GR, PlotlyJS, and many more

We demonstrate Plots.jl below:

In [1]:
# Pkg.add("Plots")
using Plots

x = cumsum(randn(50, 2), 1);
In [2]:
# Pkg.add("PyPlot")
pyplot()  # set the backend to PyPlot
plot(x, title="Random walk", xlab="time")
Out[2]:
In [3]:
# Pkg.add("PlotlyJS")
plotlyjs()  # change backend to Plotly
plot(x, title="Random walk", xlab="time")

Plotly javascript loaded.

To load again call

init_notebook(true)

Out[3]:
In [4]:
gr()   # change backend to GR
plot(x, title="Random walk", xlab="time")
Out[4]:
10 20 30 40 50 -6 -4 -2 0 2 4 Random walk time y1 y2
In [5]:
gr()
@gif for i in 1:20
    plot(x -> sin(x) / (.2i), 0, i, xlim=(0, 20), ylim=(-.75, .75))
    scatter!(x -> cos(x) * .01 * i, 0, i, m=1)
end
INFO: Saved animation to /Users/huazhou/Documents/github.com/Hua-Zhou.github.io/teaching/biostatm280-2018spring/slides/02-juliaintro/tmp.gif
Out[5]:

In [6]:
versioninfo()
Julia Version 0.6.2
Commit d386e40c17 (2017-12-13 18:08 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin14.5.0)
  CPU: Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.9.1 (ORCJIT, skylake)