cameron pfiffer | aboutbloglinks

If you ever work in Julia, something you'll notice is that lots of people and the language server will recommend that you use isnothing(x) or x === nothing instead of the value comparison x == nothing. This is also true of the ismissing functionality for missing values.

Good discussion here, and a great Stack Overflow answer here.

How meaningful is this, though? I decided to do some benchmarking to see how much of a difference this makes.

The TLDR is that x == nothing isn't specialized to checking nothing, whereas isnothing and x === nothing are. x === nothing is a core language feature (I believe) and isnothing is compile time dispatched and is thus relatively quick. x == nothing is a value comparison and I think requires some extra stuff to happen on top.

There's a speed component to this too – isnothing is the fastest on my machine, x === nothing is the second fastest, and x == nothing is the slowest.

isnothing:

julia> @benchmark isnothing(x)
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  1.539 ns … 24.880 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     1.579 ns              ┊ GC (median):    0.00%
 Time  (mean ± σ):   1.589 ns ±  0.269 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

   ▁▃▁          ██
  ▃███▄▃▂▂▂▂▂▂▂███▇▆▃▂▂▂▂▁▁▂▃▅▅▃▃▂▂▂▂▂▁▁▁▂▂▂▂▂▂▂▂▁▁▁▁▁▂▂▂▂▂▂ ▃
  1.54 ns        Histogram: frequency by time         1.7 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

x === nothing:

julia> @benchmark x === nothing
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  1.755 ns … 5.381 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     1.772 ns             ┊ GC (median):    0.00%
 Time  (mean ± σ):   1.791 ns ± 0.102 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

             █▃▄▃▁
  ▂▂▂▂▂▂▃▃▄▆▇██████▆▅▄▃▂▂▁▂▂▂▂▂▂▂▁▁▁▁▁▁▂▂▂▂▂▂▃▃▇▅▆▇▇▇▆▆▄▄▃▃ ▃
  1.76 ns        Histogram: frequency by time       1.82 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

The bad one, x == nothing:

julia> @benchmark x == nothing
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  2.419 ns … 10.722 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     2.681 ns              ┊ GC (median):    0.00%
 Time  (mean ± σ):   2.693 ns ±  0.235 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

    █       ▂          ▁▁      ▅▄    ▁
  ▂▄█▂▁▅▄▃▁▆██▂▁▅▇▅▁▂▄▅██▂▅▆▂▁▅██▂▁▂██▃▂▂▂▂▂▂▂▂▁▁▂▄▃▁▁▁▂▃▂▁▁ ▃
  2.42 ns        Histogram: frequency by time        3.11 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

This is usually why the language server will recommend that you use isnothing or x === nothing instead.

Missing values

The same is generally true of missing values. Missing values differ from nothing in that they are used to represent missing data – nothing is returned by default when a return value is not otherwise specified. missing is more for cases where you don't know a value, e.g. if you don't have data for an observation in a statistical model.

Interestingly, on Julia 1.10.2, the fastest is not one of the strict comparisons x === missing or ismissing(x), but a raw comparison using ==. Not really sure what's up with that, but whatever.

julia> @benchmark x == missing
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  0.883 ns … 7.668 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     0.890 ns             ┊ GC (median):    0.00%
 Time  (mean ± σ):   0.896 ns ± 0.107 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

         ▁ ▇ ██ ▅ ▂
  ▂▂▁▄▁▆▁█▁█▁██▁█▁█▁▇▁▆▁▅▄▁▄▁▃▁▃▁▃▁▃▃▁▃▁▃▁▂▁▂▁▂▂▁▂▁▂▁▂▁▂▁▂▂ ▃
  0.883 ns       Histogram: frequency by time      0.914 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark ismissing(x)
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  1.757 ns … 5.822 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     1.776 ns             ┊ GC (median):    0.00%
 Time  (mean ± σ):   1.783 ns ± 0.088 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

           ▁▁▂█    ▂█▁▁▁▁
  ▂▂▂▂▂▃▆▆▇████▇▆▅███████▅▂▂▂▂▂▂▂▂▂▂▁▂▂▂▂▂▂▂▂▃▃▃▃▃▃▂▃▃▃▃▃▃▃ ▃
  1.76 ns        Histogram: frequency by time       1.82 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark x === missing
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  1.540 ns … 4.822 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     1.554 ns             ┊ GC (median):    0.00%
 Time  (mean ± σ):   1.561 ns ± 0.081 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

           ▂▃▄▅▇▇█▇▅▄ ▃
  ▂▂▂▃▄▅▅▇███████████▁█▇▅▃▃▂▂▂▂▂▁▁▁▁▁▁▁▁▁▂▂▂▂▂▃▂▃▃▃▃▃▃▃▃▃▃▃ ▄
  1.54 ns        Histogram: frequency by time       1.59 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
Website built with Franklin.jl and the Julia programming language.