Nothingness in Julia

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.