I expect the performance of a < b
to be the same as Base.lt(Base.Forward, a, b)
if all type information is known at compile time, but I’m observing a 2.5x difference with Float64
.
using BenchmarkTools
using Random
Random.seed!(0)
xs = rand(10^6)
function f1(a::Vector)
n = length(a)
c = 0
for i = 2:n
if a[i-1] < a[i]
# Same as:
#if Base.lt_float(a[i-1], a[i])
c += 1
end
end
return c
end
function f2(a::Vector{Float64})
n = length(a)
c = 0
for i = 2:n
if Base.lt(Base.Forward, a[i-1], a[i])
# Same as:
#if Base.fpislt(a[i-1], a[i])
c += 1
end
end
return c
end
julia> @benchmark f1($xs)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 908.224 μs (0.00% GC)
median time: 937.959 μs (0.00% GC)
mean time: 951.230 μs (0.00% GC)
maximum time: 1.760 ms (0.00% GC)
--------------
samples: 5229
evals/sample: 1
julia> @benchmark f2($xs)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 2.653 ms (0.00% GC)
median time: 2.806 ms (0.00% GC)
mean time: 2.831 ms (0.00% GC)
maximum time: 4.744 ms (0.00% GC)
--------------
samples: 1763
evals/sample: 1
julia>
The root of the problem seem to be a difference of calling the faster Base.lt_float
versus the slower Base.fpislt
.
https://github.com/JuliaLang/julia/blob/9a1dbc038587c6072ac99699e416cbc8908054ed/base/float.jl#L458-L465
julia> @code_native 1.0 < 2.0
.text
; ┌ @ float.jl:452 within `<'
vucomisd %xmm0, %xmm1
seta %al
retq
nopl (%rax,%rax)
; └
julia> @code_native isless(1.0, 2.0)
.text
; ┌ @ float.jl:459 within `isless'
vmovq %xmm0, %rax
vmovq %xmm1, %rcx
testq %rax, %rax
sets %dl
setns %sil
cmpq %rcx, %rax
seta %al
setl %cl
andb %dl, %al
andb %sil, %cl
orb %al, %cl
vucomisd %xmm1, %xmm0
setnp %dl
andb %cl, %dl
vucomisd %xmm1, %xmm1
setp %cl
vucomisd %xmm0, %xmm0
setnp %al
andb %cl, %al
orb %dl, %al
retq
; └
What is the purpose of these two variants?
Also, it would be nice to look at the source of non-generic functions directly.
julia> @code_native Base.lt_float(1.0, 2.0)
ERROR: ArgumentError: argument is not a generic function
julia> @code_native Base.fpislt(1.0, 2.0)
ERROR: ArgumentError: argument is not a generic function