Skip to content

Large performance regression for simple bisection function #42007

Open
@giordano

Description

@giordano
function bisectroot(f::Function, left, right, ϵ=1e-6)
    @assert f(left) < 0 && f(right) > 0
    ϵ₁ = Inf
    while ϵ₁ > ϵ
        mid = (left + right)/2
        left, right = f(mid) > 0 ? (left, mid) : (mid, right)
        ϵ₁ = right - left
    end
    (left + right)/2
end

on v1.6:

julia> @benchmark bisectroot(x -> x^2-7, 0, 7)
BenchmarkTools.Trial: 10000 samples with 935 evaluations.
 Range (min  max):  105.705 ns  293.216 ns  ┊ GC (min  max): 0.00%  0.00%
 Time  (median):     111.143 ns               ┊ GC (median):    0.00%
 Time  (mean ± σ):   114.704 ns ±  12.841 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▄▂█▆▅▇▅▄   ▄▃▁▂       ▁▁                                      ▂
  ████████████████████████▇▇▇▆▇▇▇█▇▇▅▅▅▅▄▅▆▇▆▅▇▅▅▄▆▅▆▇▅▅▅▅▄▄▅▆▆ █
  106 ns        Histogram: log(frequency) by time        176 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

on master (a11de31):

julia> @benchmark bisectroot(x -> x^2-7, 0, 7)
BenchmarkTools.Trial: 10000 samples with 7 evaluations.
 Range (min  max):  4.255 μs  308.235 μs  ┊ GC (min  max): 0.00%  97.95%
 Time  (median):     4.441 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   5.046 μs ±   5.829 μs  ┊ GC (mean ± σ):  2.50% ±  2.19%

  ▃█▆▃▂▃▁▁▁▁▄▃▂ ▁▁▁▁                                          ▁
  ███████████████████▇▆▆▇▆▇▆▆▇██▇▆▆▅▅▆▅▆▆▆▅▅▆▅▅▆▅▅▅▆▅▅▄▅▅▅▅▅▄ █
  4.26 μs      Histogram: log(frequency) by time      10.5 μs <

 Memory estimate: 3.30 KiB, allocs estimate: 140.

Note that on master the function allocates, when it shouldn't.

The regression can be completely cleared (master would be actually ~25% faster) by replacing left, right = f(mid) > 0 ? (left, mid) : (mid, right) with an if like this:

function bisectroot(f::Function, left, right, ϵ=1e-6)
    @assert f(left) < 0 && f(right) > 0
    ϵ₁ = Inf
    while ϵ₁ > ϵ
        mid = (left + right)/2
        if f(mid) > 0
            left, right = left, mid
        else
            left, right = mid, right
        end
        ϵ₁ = right - left
    end
    (left + right)/2
end

v1.6:

julia> @benchmark bisectroot(x -> x^2-7, 0, 7)
BenchmarkTools.Trial: 10000 samples with 930 evaluations.
 Range (min  max):  105.654 ns  240.912 ns  ┊ GC (min  max): 0.00%  0.00%
 Time  (median):     111.938 ns               ┊ GC (median):    0.00%
 Time  (mean ± σ):   115.669 ns ±  14.069 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▄ █▃█▅▃ ▃ ▄▃▂▁ ▁▁▁                                            ▂
  █▇█████▇███████████████▇▆▇▇▇▇▇▆▇▆▆▆▅▆▆▆▆█▆▆▆▆▆▆▆▇▆▆▅▅▅▆▆▄▅▃▅▄ █
  106 ns        Histogram: log(frequency) by time        183 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

master:

julia> @benchmark bisectroot(x -> x^2-7, 0, 7)
BenchmarkTools.Trial: 10000 samples with 969 evaluations.
 Range (min  max):  75.507 ns  200.540 ns  ┊ GC (min  max): 0.00%  0.00%
 Time  (median):     78.062 ns               ┊ GC (median):    0.00%
 Time  (mean ± σ):   82.220 ns ±  10.800 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▂ █ ▇▁   ▂▂ ▁▂▁                      ▁                       ▁
  █▄████▅▇▆███████▇▇▇▇▇▇▇▇██▆▅▆▆▅▅▆▆▇▆▅█▅▅▅▄▅▄▆▆▆▄▃▅▄▅▄▃▅▄▅▄▄▄ █
  75.5 ns       Histogram: log(frequency) by time       135 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

At the moment I can't run git bisect myself to try and find the culprit (would take too long...), but if someone has clues about potential candidates, I could do a smaller search.

Metadata

Metadata

Assignees

No one assigned

    Labels

    performanceMust go fasterregressionRegression in behavior compared to a previous version

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions