Julia Performance Tips

Table of Contents

This is a note for https://github.com/AdvancedScientificComputingInJuliaWashU/Performance.jl

Global variable

  • Avoid if possible
  • If not, use a const Ref to get the type fixed

Type: T <: Union{T, …} <: Any

  • If you do not specify a type, it is type Any
  • A single concrete type is the best
  • If not possible, a union of all possible types is still way better than Any.
  • This is especially true if it is in a container such as Array.

Type declaration with abstract type

It is good for function arguments: reusable for multiple types.

  • For example, function f(a:Real) means when you call it with f(1), a f(a::Int64) will be generated and compiled.

It is bad for fields in a struct

  • The compiler uses the types of objects, not their values, to determine how to build code.
  • For the following example, in S{Int64}, and s.a could be Vector{Int64} or SparseVector{Int64}. As a result, a concrete type with an abstract field, e.g. S{Int64}, feels like an abstract type itself. Imagine if you put it into a Vector, Vector{S{Int64}} is like Vector{AbstractVector{Int64}}. The memory layout of each AbstractVector{Int64} can be very different (for example, some are static, some are dynamic.). Therefore, it is hard to optimize.

    • If it is a mutable struct, you can even change s.a's type at runtime after creation. Therefore, the compiler has no way to optimize it.
    struct S{T}
        a::AbstractVector{T}
    end
    
  • The good version is as follows. In a S{Vector{Int}}, s.a must be Vector{Int}.

    struct S{T<:AbstractVector}
        a::T
    end
    
  • The key is, make sure once the type of S is specified as concrete, all its fields are concrete as well.
  • If you specify a field as an abstract type, the functions applies on it will likely return type: Any (Because anyone can subtype this abstract type and define a method for the function to return whatever type). Functions includes getindex. This meaning array[i] can return type Any, even if array::AbstractVector{Int64}. Although in the runtime, you can see the type being correct. However, @codewarntype or @inferred cannot infer the type while compiling.

Multiple Dispatch

Dict vs. Array

  • Hash table lookup can be a order of magnitude slower than Array lookup (even with some condiction check)
  • If you are working with char, using Array as a dictionary.

where

parametric type:

  • Vector{<:Real} or Vector{T} where T <: Real means Union{Vector{T} for all T belong to Real}.
  • Unionall: Vector means Vector{T} where T <: Any

method

  • function f(x::T) where {T} just tells the parser, T is template/parametric. function f(x::T) where {T<:Real} or function f(x::T where T<:Real) can be used to put some constraint on the type.

struct

  • On the other hand, there is no where for struct to put constraints. You directly put constraints in the {}. For example, struct S{T<:Real}.