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 > >
