Julia is an open-source, multi-platform, high-level, high-performance programming language for technical computing.
Julia has an LLVM Low-Level Virtual Machine (LLVM) is a compiler infrastructure to build intermediate and/or binary machine code. -based JIT Just-In-Time compilation occurs at run-time rather than prior to execution, which means it offers both the speed of compiled code and the flexibility of interpretation. The compiler parses the code and infers types, after which the LLVM code is generated, which in turn is compiled into native code. compiler that allows it to match the performance of languages such as C and FORTRAN without the hassle of low-level code. Because the code is compiled on the fly you can run (bits of) code in a shell or REPL Read-Eval-Print-Loop , which is part of the recommended workflow .
Julia is dynamically typed, provides multiple dispatch Because function argument types are determined at run-time, the compiler can choose the implementation that is optimized for the provided arguments and the processor architecture. , and is designed for parallelism and distributed computation.
Julia has a built-in package manager.
Julia has many built-in mathematical functions, including special functions (e.g. Gamma), and supports complex numbers right out of the box.
Julia allows you to generate code automagically thanks to Lisp-inspired macros.
Julia was created in 2012.
| Assignment | answer = 42x, y, z = 1, [1.0, 2.0, 3.0], "A string" |
| Swap | x, y = y, x # swap x and y |
| Constant declaration | const DATE_OF_BIRTH = 2012 |
| End-of-line comment | i = 1 # This is a comment |
| Delimited comment | #= This is a multiline comment =# |
| Chaining | x = y = z = 1 # right-to-left0 < x < 3 # true5 < x != y < 5 # false |
| Function definition | function add_one(i) return i + 1end |
| Insert LaTeX symbols | \delta + [Tab] |
A variable, in Julia, is a name associated (or bound) to a value. Notice that for most complex types, it will not copy the underlying data! For example
x = [1,2,3]
y = x
y[1] = 10
x[1] == 10 # this is true, since x and y are associated to the same array
| Basic arithmetic | +, -,*,/ |
| Exponentiation | 2^3 == 8 |
| Division | 3/12 == 0.25 |
| Inverse division | 7\3 == 3/7 |
| Remainder | x % y or rem(x,y) |
| Negation | !true == false |
| Equality | a == b |
| Inequality | a != b or a ≠ b |
| Less and larger than | < and > |
| Less than or equal to | <= or ≤ |
| Greater than or equal to | >= or ≥ |
| Element-wise operation | [1, 2, 3] .+ [1, 2, 3] == [2, 4, 6][1, 2, 3] .* [1, 2, 3] == [1, 4, 9] |
| Not a number | isnan(NaN) not(!) NaN == NaN |
| Ternary operator | a == b ? "Equal" : "Not equal" |
| Short-circuited AND and OR | a && b and a || b |
| Object equivalence | a === b |
| Recall last result | ans |
| Interrupt execution | [Ctrl] + [C] |
| Clear screen | [Ctrl] + [L] |
| Run program | include("filename.jl") |
Get help for func is defined |
?func |
See all places where func is defined |
apropos("func") |
| Command line mode | ; on empty line |
| Package Manager mode | ] on empty line |
| Help mode | ? on empty line |
| Exit special mode / Return to REPL | [Backspace] on empty line |
| Exit REPL | exit() or [Ctrl] + [D] |
To help Julia load faster, many core functionalities exist in standard libraries that
come bundled with Julia. To make their functions available, use using PackageName. Here
are some Standard Libraries and popular functions.
Random |
rand, randn, randsubseq |
Statistics |
mean, std, cor, median, quantile |
LinearAlgebra |
I, eigvals, eigvecs, det, cholesky |
SparseArrays |
sparse, SparseVector, SparseMatrixCSC |
Threads |
@threads |
Distributed |
@distributed, pmap, addprocs |
Dates |
DateTime, Date |
Packages must be registered before they are visible to the
package manager. In Julia, there are two ways to work with the package manager:
either with using Pkg and using Pkg functions, or by typing ] in the REPL to
enter the special interactive package management mode. (To return to regular REPL, just
hit BACKSPACE on an empty line in package management mode).
Add PackageName |
add PackageName |
Remove PackageName |
rm PackageName |
Update PackageName |
update PackageName |
| Use development version | dev PackageName or dev GitRepoUrl |
| Stop using development version, revert to public release | free PackageName |
Pkg in Julia session| List installed packages (human-readable) | Pkg.status() |
| Update all packages | Pkg.update() |
Install PackageName |
Pkg.add("PackageName") |
Rebuild PackageName |
Pkg.build("PackageName") |
Use PackageName (after install) |
using PackageName |
Remove PackageName |
Pkg.rm("PackageName") |
| Character | chr = 'C' |
| String | str = "A string" |
| Character code | Int('J') == 74 |
| Character from code | Char(74) == 'J' |
| Any UTF character | chr = '\uXXXX' # 4-digit HEXchr = '\UXXXXXXXX' # 8-digit HEX |
| Loop through characters | for c in str println(c)end |
| Concatenation | str = "Learn" * " " * "Julia" |
| Concatenation (with other types) | string('X', " = ", 42) == "X = 42" |
| String interpolation | a = b = 2println("a * b = $(a*b)") |
| First matching character or regular expression | findfirst(isequal('i'), "Julia") == 4 |
| Replace substring or regular expression | replace("Julia", "a" => "us") == "Julius" |
| Last index (of collection) | lastindex("Hello") == 5 |
| Number of characters | length("Hello") == 5 |
| Regular expression | pattern = r"l[aeiou]" |
| Subexpressions | str = "+1 234 567 890"pat = r"\+([0-9]) ([0-9]+)"m = match(pat, str)m.captures == ["1", "234"] |
| All occurrences | [m.match for m = eachmatch(pat, str)] |
| All occurrences (as iterator) | eachmatch(pat, str) |
Beware of multi-byte Unicode encodings in UTF-8:
10 == lastindex("Ångström") != length("Ångström") == 8
Strings are immutable.
| Integer types | IntN and UIntN, with N ∈ {8, 16, 32, 64, 128}, BigInt x = 1 -> typeof(x) |
| Floating-point types | FloatN with N ∈ {16, 32, 64}BigFloat |
| Type detection | n = 100; x = 3.14 typeof(n) == Int64, typeof(x) == Float64 |
| Minimum and maximum values by type | typemin(Int8)typemax(Int64) |
| Complex types | Complex{T}, e.g. Complex{Float64} |
| Imaginary unit | im, e.g. z = 1.0 - 2.0im |
| Real and imaginary parts | real(z) == 1.0 and imag( z ) == -2.0 |
| Machine precision | eps(1.0) # Float64 precision around x = 1.0 |
| Rounding | round(x) # returns floating-pointround(Int64, x) # returns 64-bit integer |
| Floor (round down) | floor(x) resp. floor(Int64, x) |
| Ceiling (round up) | ceil(x) resp. ceil(Int64, x) |
| Type conversions | convert(T, val) # for T numbertype e.g. convert(UInt8, 42)Float64(val) # calls convert |
| Parse string (to number) | parse(Float64, "3.14") |
| Global constants | pi # 3.1415...π # 3.1415...im # real(im * im) == -1 |
| More constants | using Base.MathConstants |
Julia does not automatically check for numerical overflow. Use package SaferIntegers for ints with overflow checking.
Many random number functions require using Random.
| Set seed | seed!(seed) |
| Random numbers | rand() # uniform [0,1)randn() # normal (-Inf, Inf) |
| Random arrays | rand(2, 3) # 2×3 arrayrandn(100) # 100 dim vector |
| Random from Other Distribution | using Distributionsmy_dist = Bernoulli(0.2) # For examplerand(my_dist) |
| Random subsample elements from A with inclusion probability p | randsubseq(A, p) |
| Random permutation elements of A | shuffle(A) |
| Declaration | arr = Float64[] arr = [] # element type is Any |
| Pre-allocation | sizehint!(arr, 10^4) |
| Access and assignment | arr = [1,2]x = arr[1] + arr[end] arr[1] = 10 |
| Array comprehension | arr = [i^2 for i in 1:10] |
| Array comprehension (2D) | [i+j for i in 1:3, j in 1:3] |
| Assigment | a = [1,2,3]b = a # b and a bound to same arraya[1] = -99a == b # true |
| Copy elements (not address) | b = copy(a) # shallow copyb = deepcopy(a) |
| Length | length(arr) # int |
| Size | size(arr) # tuple |
| Select subarray from m to n | arr[m:n] |
| n-element array with 0.0s | zeros(n) |
| n-element array with 1.0s | ones(n) |
| n-element array with #undefs | Vector{Type}(undef,n) |
| n equally spaced numbers from start to stop | range(start,stop=stop,length=n) |
| Array with n random Int8 elements | rand(Int8, n) |
| Fill array with val | fill!(arr, val) |
| Pop last element | pop!(arr) |
| Pop first element | popfirst!(a) |
| Push val as last element | push!(arr, val) |
| Push val as first element | pushfirst!(arr, val) |
| Remove element at index idx | deleteat!(arr, idx) |
| Sort | sort!(arr) |
| Append a with b | append!(a,b) |
| Check whether val is element | val in arr |
| Scalar product | dot(a, b) == sum(a .* b) |
| Change dimensions (if possible) | reshape(1:6, 3, 2)' == [1 2 3; 4 5 6] |
| To string (with delimiter del between elements) | join(arr, del) |
| Iterate | arr = rand(100)for x in arr # do somethingend |
For most linear algebra tools, use using LinearAlgebra.
| Identity matrix | I # just use variable I. Will automatically conform to dimensions required. |
| Define matrix | M = [1 0; 0 1] |
| Matrix dimensions | size(M) |
Select i th row |
M[i, :] |
Select i th column |
M[:, i] |
| Concatenate horizontally | M = [a b] or M = hcat(a, b) |
| Concatenate vertically | M = [a ; b] or M = vcat(a, b) |
| Matrix transposition | transpose(M) |
| Conjugate matrix transposition | M' or adjoint(M) |
| Matrix trace | tr(M) |
| Matrix determinant | det(M) |
| Matrix rank | rank(M) |
| Matrix eigenvalues | eigvals(M) |
| Matrix eigenvectors | eigvecs(M) |
| Matrix inverse | inv(M) |
Solve M*x == v |
M\v is better Numerically more stable and typically also faster. than inv(M)*v |
| Moore-Penrose pseudo-inverse | pinv(M) |
| Automatic factorization | Afact = factorize(A) use Afact\v to solve |
| LU factorization | Alu = lu(A) use Afact\v to solve |
Julia has built-in support for matrix decompositions.
Julia tries to infer whether matrices are of a special type (symmetric,
hermitian, etc.), but sometimes fails. To aid Julia in dispatching the
optimal algorithms, special matrices can be declared to have a structure
with functions like Symmetric , Hermitian , UpperTriangular, LowerTriangular,
Diagonal , and more.
Conditional if, else, elseif, end |
if 10 == 11 println("Error in the matrix.")else println("All good.")end |
|
Simple for loop |
for i in 1:10 println(i)end |
|
| Unnested for loop | for i in 1:10, j = 1:5 println(i*j)end |
|
| Enumeration | for (idx, val) in enumerate(arr) println("the $idx-th element is $val")end |
|
while loop |
while bool_expr # do stuffend |
|
| Exit loop | break |
|
| Skip current iteration | continue |
All arguments to functions are passed by reference.
Convention: Functions with ! at the end of the name change at least one argument, typically the first, e.g.
sort!(arr).
There are three kinds of arguments:
=.function func(req1, req2, opt1 = 1; key1="x", key2=0)
# do stuff
end
The semicolon is not required in the call to a function that accepts keyword arguments.
The return statement is optional but highly recommended. (Without return, the function returns the value of the last line.)
Multiple variables can be returned as a tuple in a single return statement.
Functions can have explicit input and return types
# take any Number subtype and return it as a String
function stringifynumber(num::Number)::String
return "$num"
end
Multiple dispatch: Functions can have multiple definitions for different input types. At each call, the most spezialized definition will be used, e.g.
intbyfloat(x,y) = NaN
intbyfloat(x::Int64,y::Float64) = x/y
intbyfloat(x::Float64,y::Int64) = y/x
intbyfloat(1,2.0) == 1/2.0 # calls second definition
intbyfloat(3.0,2) == 2/3.0 # calls third definition
isnan( intbyfloat(3.0, 3.0) ) # calls first definition
Functions can be vectorized by using the Dot Syntax
# here we broadcast the addition and the call of f(x)
A = rand(3, 4)
B = A .- randn(3,4) # broadcast '-'
f(x) = 2x-1
C = f.(B) # broadcast 'f'
Alternative ways to define functions are one line functions and anonymous functions.
One line functions use = instead of return
f(x,y=1;z=2) = x+y*z
Anonymous functions do not have a function name and use -> instead of return
(x,y=1;z=2) -> x+y*z
They can be used for example in collection functions or list comprehension, e.g.
sort([1,2,3], by = (x) -> 1/sin(x))
map((x,y) -> x/y^2, 1:10, 2:11)
Anonymous functions can best be used in collection functions or list comprehensions:
x -> x^2.
Functions can accept a variable number of arguments:
function func(a...)
println(a)
end
func(1, 2, [3:5]) # tuple: (1, 2, UnitRange{Int64}[3:5])
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
Parametric functions
Julia generates specialized versions Multiple dispatch a type of polymorphism that dynamically determines which version of a function to call. In this context, dynamic means that it is resolved at run-time, whereas method overloading is resolved at compile time. Julia manages multiple dispatch completely in the background. Of course, you can provide custom function overloadings with type annotations. of functions based on data types. When a function is called with the same argument types again, Julia can look up the native machine code and skip the compilation process.
The existence of potential ambiguities is acceptable, but actually calling an ambiguous method is an immediate error.
Stack overflow is possible when recursive functions nest many levels deep. ```
Tuples are like inmutable arrays with static types.
| Tuple construction | x = (1, 2.0, "3") # Tuple{Int64, Float64, String} |
| Access | x[1] == 1, x[end] == "3" |
| Unpack variables | a, b, c = x |
NamedTuples are like Tuples but the elements have (static) names and allow access as data as fields.
| Construction | nt = (; a = 10, b = 2.0, c = "3") |
| Access | nt.a == 10, nt.c == "3" |
| Unpack (by order) | (a,b,c) = nt |
| Unpack (by variable names) | (;c,b,a) = nt (order does not matter) |
| Dictionary | d = Dict(key1 => val1, key2 => val2, ...)d = Dict(:key1 => val1, :key2 => val2, ...) |
| All keys (iterator) | keys(d) |
| All values (iterator) | values(d) |
| Loop through key-value pairs | for (k,v) in d println("key: $k, value: $v")end |
Check for key :k |
haskey(d, :k) |
| Copy keys (or values) to array | arr = collect(keys(d))arr = [k for (k,v) in d] |
Dictionaries are mutable; when symbols are used as keys, the keys are immutable.
| Declaration | s = Set([1, 2, 3, "Some text"]) |
Union s1 ∪ s2 |
union(s1, s2) |
Intersection s1 ∩ s2 |
intersect(s1, s2) |
Difference s1 \\ s2 |
setdiff(s1, s2) |
Difference s1 △ s2 |
symdiff(s1, s2) |
Subset s1 ⊆ s2 |
issubset(s1, s2) |
Checking whether an element is contained in a set is done in O(1).
| Apply f to all elements of collection coll | map(f, coll) ormap(coll) do elem # do stuff with elem # must contain returnend |
| Filter coll for true values of f | filter(f, coll) |
| List comprehension | arr = [f(elem) for elem in coll] |
Julia has no classes and thus no class-specific methods.
Types are like classes without methods.
Abstract types can be subtyped but not instantiated.
Concrete types cannot be subtyped.
By default, struct s are immutable.
Immutable types enhance performance and are thread safe, as they can be shared among threads without the need for synchronization.
Objects that may be one of a set of types are called Union types.
| Type annotation | var::TypeName |
| Type declaration | struct Programmer name::String birth_year::UInt16 fave_language::AbstractStringend |
| Mutable type declaration | replace struct with mutable struct |
| Type alias | const Nerd = Programmer |
| Type constructors | methods(TypeName) |
| Type instantiation | me = Programmer("Ian", 1984, "Julia")me = Nerd("Ian", 1984, "Julia") |
| Subtype declaration | abstract type Bird endstruct Duck <: Bird pond::Stringend |
| Parametric type | struct Point{T <: Real} x::T y::Tendp =Point{Float64}(1,2) |
| Union types | Union{Int, String} |
| Traverse type hierarchy | supertype(TypeName) and subtypes(TypeName) |
| Default supertype | Any |
| All fields | fieldnames(TypeName) |
| All field types | TypeName.types |
When a type is defined with an inner constructor, the default outer
constructors are not available and have to be defined manually if need
be. An inner constructor is best used to check whether the parameters
conform to certain (invariance) conditions. Obviously, these invariants
can be violated by accessing and modifying the fields directly, unless
the type is defined as immutable. The new keyword may be used to
create an object of the same type.
Type parameters are invariant, which means that Point{Float64} <: Point{Real} is
false, even though Float64 <: Real.
Tuple types, on the other hand, are covariant: Tuple{Float64} <: Tuple{Real}.
The type-inferred form of Julia’s internal representation can be found
with code_typed(). This is useful to identify where Any rather
than type-specific native code is generated.
| Type | typeof(name) |
| Type check | isa(name, TypeName) |
| List subtypes | subtypes(TypeName) |
| List supertype | supertype(TypeName) |
| Function methods | methods(func) |
| JIT bytecode | code_llvm(expr) |
| Assembly code | code_native(expr) |
| Programmers Null | nothing |
| Missing Data | missing |
| Not a Number in Float | NaN |
| Filter missings | collect(skipmissing([1, 2, missing])) == [1,2] |
| Replace missings | collect((df[:col], 1)) |
| Check if missing | ismissing(x) not x == missing |
| Throw SomeExcep | throw(SomeExcep()) |
| Rethrow current exception | rethrow() |
| Define NewExcep | struct NewExcep <: Exception v::StringendBase.showerror(io::IO, e::NewExcep) = print(io, "A problem with $(e.v)!")throw(NewExcep("x")) |
| Throw error with msg text | error(msg) |
| Handler | try # do something potentially iffycatch ex if isa(ex, SomeExcep) # handle SomeExcep elseif isa(ex, AnotherExcep) # handle AnotherExcep else # handle all others endfinally # do this in any caseend |
Modules are separate global variable workspaces that group together similar functionality.
| Definition | module PackageName# add module definitions# use export to make definitions accessibleend |
Include filename.jl |
include("filename.jl") |
| Load | using ModuleName # all exported namesusing ModuleName: x, y # only x, yimport ModuleName # only ModuleNameimport ModuleName: x, y # only x, yimport ModuleName.x, ModuleName.y # only x, y |
| Exports | # Get an array of names exported by Modulenames(ModuleName)# include non-exports, deprecateds# and compiler-generated namesnames(ModuleName, all::Bool)#also show names explicitly imported from other modulesnames(ModuleName, all::Bool, imported::Bool) |
With using Foo you need to say function Foo.bar(... to extend module Foo’s
function bar with a new method, but with import Foo.bar, you only need to say
function bar(... and it automatically extends module Foo’s function bar .
Julia is homoiconic: programs are represented as data structures of the
language itself. In fact, everything is an expression Expr.
Symbols are interned strings Only one copy of each distinct (immutable) string value is stored. prefixed with a colon. Symbols are more efficient and they are typically used as identifiers, keys (in dictionaries), or columns in data frames. Symbols cannot be concatenated.
Quoting :( ... ) or quote ... end creates an expression, just
like Meta.parse(str) This form is probably most familiar to
people with knowledge of dynamic SQL. The Meta.parse function is similar
to Oracle”s and PostgreSQL”s EXECUTE IMMEDIATE statement or SQL
Server’s sp_executesql() procedure. , and Expr(:call, ...).
x = 1
line = "1 + $x" # some code
expr = Meta.parse(line) # make an Expr object
typeof(expr) == Expr # true
dump(expr) # generate abstract syntax tree
eval(expr) == 2 # evaluate Expr object: true
Macros allow generated code (i.e. expressions) to be included in a program.
| Definition | macro macroname(expr) # do stuffend |
| Usage | @macroname(ex1, ex2, ...) or @macroname ex1 ex2 ... |
| Built-in macros | @test # equal (exact)@test x ≈ y # isapprox(x, y)@assert # assert (unit test)@which # types used@time # time and memory statistics@elapsed # time elapsed@allocated # memory allocated@profile # profile@spawn # run at some worker@spawnat # run at specified worker@async # asynchronous task@distributed # parallel for loop@everywhere # make available to workers |
Rules for creating hygienic macros:
local .eval inside macro.$(esc(expr))Multithreading is provided by the Threads standard package.
| Launch Julia with threads | julia --threads 4 |
| Number of threads | Threads.nthreads() |
Multithreaded for loop |
Threads.@threads for i = 1:10a[i] = i^2 end |
| Data-race freedom | use lock(x) (and unlock(x)),e.g. lock(lk) do use(a) end |
Parallel computing tools are available in the Distributed standard library.
| Launch REPL with N workers | julia -p N |
| Number of available workers | nprocs() |
| Add N workers | addprocs(N) |
| Get id of executing worker | myid() |
| Remove worker | rmprocs(pid) |
| Make expr available to all workers | @everywhere expr |
| Apply f to all elements in collection coll | pmap(f, coll) |
| Run f with arguments args on pid (more efficient) | remotecall_fetch(f, pid, args...) |
| Run f with arguments args on any worker | r = @spawn f(args) ... fetch(r) |
| Run f with arguments args on all workers | r = [@spawnat w f(args) for w in workers()] ... fetch(r) |
| Parallel for loop with reducerA reducer combines the results from different (independent) workers. function red | sum = @distributed (red) for i in 1:10^6 # do parallelstuffend |
Workers are also known as concurrent/parallel processes.
Modules with parallel processing capabilities are best split into a functions file that contains all the functions and variables needed by all workers, and a driver file that handles the processing of data. The driver file obviously has to import the functions file.
| Read stream | stream = stdinfor line in eachline(stream) # do stuffend |
| Read file | open(filename) do file for line in eachline(file) # do stuff endend |
| Read CSV file | using CSVdata = CSV.read(filename) |
| Write CSV file | using CSVCSV.write(filename, data) |
| Save HDF5 | using HDF5h5write(filename, "key", object) |
| Load HDF5 | using HDF5h5read(filename, "key") |
For dplyr-like tools, see
DataFramesMeta.jl.
| Read Stata, SPSS, etc. | StatFiles Package |
DescribeSimilar to summary(df) in R. data frame |
describe(df) |
Make vector of column col |
v = df[:col] |
Sort by col |
sort!(df, [:col]) |
CategoricalSimilar to df$col = as.factor(df$col) in R. col |
categorical!(df, [:col]) |
List col levels |
levels(df[:col]) |
All observations with col==val |
df[df[:col] .== val, :] |
| Reshape from wide to long format | stack(df, [1:n; ])stack(df, [:col1, :col2, ...])melt(df, [:col1, :col2]) |
| Reshape from long to wide format | unstack(df, :id, :val) |
Make Nullable |
allowmissing!(df) or allowmissing!(df, :col) |
| Loop over Rows | for r in eachrow(df) # do stuff. # r is Struct with fields of col names.end |
| Loop over Columns | for c in eachcol(df) # do stuff. # c is tuple with name, then vectorend |
| Apply func to groups | by(df, :group_col, func) |
| Query | using Queryquery = @from r in df begin @where r.col1 > 40 @select {new_name=r.col1, r.col2} @collect DataFrame # Default: iteratorend |
Many core packages are managed by communities with names of the form Julia[Topic].
| Statistics | JuliaStats |
| Scientific Machine Learning | SciML (DifferentialEquations) |
| Automatic differentiation | JuliaDiff |
| Numerical optimization | JuliaOpt |
| Plotting | JuliaPlots, (and Makie) |
| Network (Graph) Analysis | JuliaGraphs |
| Web | JuliaWeb |
| Geo-Spatial | JuliaGeo |
| Machine Learning | JuliaML |
| Super-used Packages | DataFrames, Distributions, DifferentialEquations, JuMP (optimization), Flux (Machine learning), LightGraphs, Plots, Makie |
| Utility packages | Revise, UnPack, StaticArrays, PkgTemplates, BenchmarkTools |
The main convention in Julia is to avoid underscores unless they are required for legibility.
Variable names are in lower (or snake) case: somevariable.
Constants are in upper case: SOMECONSTANT.
Functions are in lower (or snake) case: somefunction.
Macros are in lower (or snake) case: @somemacro.
Type names are in initial-capital camel case: SomeType.
Julia files have the jl extension.
For more information on Julia code style visit the manual: style guide .
See Julia Manual - Performance tips.
@time or @btime and pay attention to memory allocation.Juno.@profiler and @code_warntype)Any in collections.sizehint! for large arrays.@. to fuse vectorized operations.@view for slices....) operator for keyword arguments.! to avoid copying data structures.try-catch in (computation-intensive) loops.eval at run-time.