Type stability

I don’t understand the following is not typestable,
changing the _p assignment to a mapreduce call doesn’t help, subsetting h or a for loop helps:

function conf_int_sum{T}(p::T, h::Array{T}, eps::T)
    b1 = zero(T)
    b2 = one(T)
    b = (b1 + b2) / 2
    _p = sum(x -> (x >= b) ? x : zero(x), h)
    while abs(_p - p) > eps
        if _p > p
            b1 = b
        else 
            b2 = b
        end
        b = (b1 + b2) / 2
        _p = sum(x -> (x >= b) ? x : zero(x), h)
    end
    return b
end

function conf_int_mapreduce{T}(p::T, h::Array{T}, eps::T)
    b1 = zero(T)
    b2 = one(T)
    b = (b1 + b2) / 2
    _p = mapreduce(x -> (x >= b) ? x : zero(x), +, h)
    while abs(_p - p) > eps
        if _p > p
            b1 = b
        else 
            b2 = b
        end
        b = (b1 + b2) / 2
        _p = mapreduce(x -> (x >= b) ? x : zero(x), +, h)
    end
    return b
end

function conf_int_subset{T}(p::T, h::Array{T}, eps::T)
    b1 = zero(T)
    b2 = one(T)
    b = (b1 + b2) / 2
    _p = sum(h[h .>= b])
    while abs(_p - p) > eps
        if _p > p
            b1 = b
        else 
            b2 = b
        end
        b = (b1 + b2) / 2
        _p = sum(h[h .>= b])
    end
    return b
end

function conf_int_for{T}(p::T, h::Array{T}, eps::T)
    b1 = zero(T)
    b2 = one(T)
    b = (b1 + b2) / 2
    _p = zero(T)
    for i in h
        if i >= b
            _p += i
        end
    end
    while abs(_p - p) > eps
        if _p > p
            b1 = b
        else 
            b2 = b
        end
        b = (b1 + b2) / 2
        _p = zero(T)
        for i in h
            if i >= b
                _p += i
            end
        end
    end
    return b
end

# typestable:
@code_warntype conf_int_for(0.5f0, randn(Float32, 30, 30, 30), 1f-3)
@code_warntype conf_int_subset(0.5f0, randn(Float32, 30, 30, 30), 1f-3)
# not typestable:
@code_warntype conf_int_mapreduce(0.5f0, randn(Float32, 30, 30, 30), 1f-3)
@code_warntype conf_int_sum(0.5f0, randn(Float32, 30, 30, 30), 1f-3)

Probably performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub (one of the bugs that I dislike the most, for reason like this).

so this is because I use b inside of the closure?

Yes you can work around it by adding

let b = b
    sum(...)
end

on the two places you call sum in conf_int_sum.

In MicroLogging.jl I tried to automate the let workaround using a macro. It turned out to be a bit more work than expected, so I thought people might find it useful in a package. Here it is, in rough form:

https://github.com/c42f/FastClosures.jl

I’m not sure about the time frame when this might be fixed properly in the compiler. If people find it useful, I can go to the effort of registering the package.

6 Likes