The truth lies somewhere between:

immutable TwoInts
    a::Int
    b::Int
end
function update1!(a)
    @inbounds for i = 1:length(a)
        a[i] = TwoInts(a[i].a,i+2)
    end
    a
end
function update2!(A)
    @inbounds for i = 1:size(A,2)
        A[2,i] = i+2
    end
    A
end
a = [TwoInts(i,i+1) for i = 1:10^7];
A = reinterpret(Int, a, (2, length(a)))
update1!(a)
update2!(A)
@time update1!(a)
@time update2!(A)


julia> include("/tmp/update.jl")
elapsed time: 0.038554672 seconds (13848 bytes allocated)
elapsed time: 0.389896915 seconds (0 bytes allocated)
elapsed time: 0.33763039 seconds (0 bytes allocated)

So there is some (small) advantage to mutability _if_ you're going to mutate 
the values. What you're missing, Steve, is how immutables interact on a larger 
scale. If the compiler knows that a value won't change, quite a few 
optimizations are possible (http://en.wikipedia.org/wiki/Immutable_object). 
Your example just doesn't happen to capture any of those advantages.

--Tim

On Tuesday, August 05, 2014 04:12:46 PM [email protected] wrote:
> Jason,
> 
> You say that probably no improvement would be possible if 'fast' updating
> were available for immutable in my code.  In this case, why is there such a
> huge difference between fast and slow updating for the non-immutable case?
>  Is there some optimization available in the 'immutable' case but not the
> 'type' case, or is this a compiler bug?
> 
> -- Steve
> 
> On Tuesday, August 5, 2014 5:38:17 PM UTC-4, [email protected] wrote:
> > 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