Dear Julia users,

It seems to me that Julia's distinction between a 'type' and an 'immutable' 
conflates two independent properties; the consequence of this conflation is 
a needless loss of performance.  In more detail, the differences between a 
'type' struct and 'immutable' struct in Julia are:

1. Assignment of 'type' struct copies a pointer; assignment of an 
'immutable' struct copies the data.

2. An array of type structs is an array of pointers, while an array of 
immutables is an array of data.

3. Type structs are refcounted, whereas immutables are not.  (This is not 
documented; it is my conjecture.)

4. Fields in type structs can be modified, but fields in immutables cannot.

Clearly #1-#3 are related concepts.  As far as I can see, #4 is completely 
independent from #1-#3, and there is no obvious reason why it is forbidden 
to modify fields in immutables.  There is no analogous restriction in C/C++.

This conflation causes a performance hit.  Consider:

type floatbool
  a::Float64
  b:Bool
end

If t is of type Array{floatbool,1} and I want to update the flag b in t[10] 
to 'true', I say 't[10].b=true' (call this 'fast'update).  But if instead 
of 'type floatbool' I had said 'immutable floatbool', then to set flag b in 
t[10] I need the more complex code t[10] = floatbool(t[10].a,true) (call 
this 'slow' update).

To document the performance hit, I wrote five functions below. The first 
three use 'type' and either no update, fast update, or slow update; the 
last two use 'immutable' and either no update or slow update.   You can see 
a HUGE hit on performance between slow and fast update for `type'; for 
immutable there would presumably also be a difference, although apparently 
smaller. (Obviously, I can't test fast update for immutable; this is the 
point of my message!)

So why does Julia impose this apparently needless restriction on immutable?

-- Steve Vavasis


julia> @time testimmut.type_upd_none()
@time testimmut.type_upd_none()
elapsed time: 0.141462422 seconds (48445152 bytes allocated)

julia> @time testimmut.type_upd_fast()
@time testimmut.type_upd_fast()
elapsed time: 0.618769232 seconds (48247072 bytes allocated)

julia> @time testimmut.type_upd_slow()
@time testimmut.type_upd_slow()
elapsed time: 4.511306586 seconds (4048268640 bytes allocated)

julia> @time testimmut.immut_upd_none()
@time testimmut.immut_upd_none()
elapsed time: 0.04480173 seconds (32229468 bytes allocated)

julia> @time testimmut.immut_upd_slow()
@time testimmut.immut_upd_slow()
elapsed time: 0.351634871 seconds (32000096 bytes allocated)

module testimmut

type xytype
    x::Int
    y::Float64
    z::Float64
    summed::Bool
end

immutable xyimmut
    x::Int
    y::Float64
    z::Float64
    summed::Bool
end

myfun(x) = x * (x + 1) * (x + 2)

function type_upd_none()
    n = 1000000
    a = Array(xytype, n)
    for i = 1 : n
        a[i] = xytype(div(i,2), 0.0, 0.0, false)
    end
    numtri = 100
    for tri = 1 : numtri
        sum = 0
        for i = 1 : n
            @inbounds x = a[i].x
            sum += myfun(x)
        end
    end
end


function type_upd_fast()
    n = 1000000
    a = Array(xytype, n)
    for i = 1 : n
        a[i] = xytype(div(i,2),  0.0, 0.0, false)
    end
    numtri = 100
    for tri = 1 : numtri
        sum = 0
        for i = 1 : n
            @inbounds x = a[i].x
            sum += myfun(x)
            @inbounds a[i].summed = true
        end
    end
end

function type_upd_slow()
    n = 1000000
    a = Array(xytype, n)
    for i = 1 : n
        a[i] = xytype(div(i,2),  0.0, 0.0, false)
    end
    numtri = 100
    for tri = 1 : numtri
        sum = 0
        for i = 1 : n
            @inbounds x = a[i].x
            sum += myfun(x)
            @inbounds a[i] = xytype(a[i].x, a[i].y, a[i].z, true)
        end
    end
end


function immut_upd_none()
    n = 1000000
    a = Array(xyimmut, n)
    for i = 1 : n
        a[i] = xyimmut(div(i,2),  0.0, 0.0, false)
    end
    numtri = 100
    for tri = 1 : numtri
        sum = 0
        for i = 1 : n
            @inbounds x = a[i].x
            sum += myfun(x)
        end
    end
end

function immut_upd_slow()
    n = 1000000
    a = Array(xyimmut, n)
    for i = 1 : n
        a[i] = xyimmut(div(i,2),  0.0, 0.0, false)
    end
    numtri = 100
    for tri = 1 : numtri
        sum = 0
        for i = 1 : n
            @inbounds x = a[i].x
            sum += myfun(x)
            @inbounds a[i] = xyimmut(a[i].x, a[i].y, a[i].z, true)
        end
    end
end

end


  

Reply via email to