There's another piece of the picture. You really want numbers and many
numeric types to be immutable. So since we wanted user-defined numeric
types, that means we need user-defined immutable types (not just the
built-in immutable types that C and Java have). If you also decide that you
only want a single, consistent set of semantics for all types, the
apparently inescapable conclusion is to have mutable and immutable rather
than value types and reference types.


On Tue, Aug 5, 2014 at 9:19 PM, Tim Holy <[email protected]> wrote:

> 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