> This conflation causes a performance hit.

You claim this, but it is not supported by your data. In fact, we saw the
opposite:

> testimmut.type_upd_fast()
0.618769232 seconds

> testimmut.immut_upd_slow()
0.351634871

Why? Perhaps related is that neither 1 nor 3 in your list of assumptions is
necessarily true.

1. While semantically this statement is correct, LLVM is extremely good at
this particular optimization. In fact, while C/C++ does not impose this
restriction, llvm does! So while the C language spec does not require this
restriction, it ends up that llvm (via the clang compiler) can do better at
optimizing C code after adding this restriction.

3. While ref-counting is a form of garbage collection, it is not the form
used by Julia. Julia uses a simple mark-and-sweep GC, currently (there is a
pull request targeted for early 0.4 that will change this to an incremental
mark-and-sweep GC). This is generally unrelated, as it is possible to have
a boxed immutable type handled by the GC, whereas the reverse has not yet
been implemented.

So, while your suggestion that `type` and `immutable` are not the only way
categories for data access, they were chosen for Julia as being simpler
mental models than the traditional C distinction of structs and pointers
(as well as being more amenable to garbage collection and generic
programming).


On Tue, Aug 5, 2014 at 5:38 PM, <[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