Definitely! At least by me. Besides generics I miss immutable
references the most (or a guarantee that the compiler optimizes away
needless copying the way you describe it).
-CP
I think the reasoning is as follows:
If a reference shouldn't be used to manipulate an object... don't use it to manipulate an object. You only need to follow your own rules within your own packages, since "private" data can be protected from other packages using interfaces and non-exported variables/fields.
If an object is too big to be copied efficiently... don't copy it. Most of the time receivers and parameters should be passed by pointer, unless they are small (near the size of pointers).
Ryanne
- from my phone -
you might be surprised by the size of the overhead of copying
larger structures by value - it's lower than you might think.
i did a few tests, timing loops of this form:
func benchn(t *testing.B) {
var x Fn
for i := t.N; i > 0; i-- {
foon(x)
foon(x)
foon(x)
foon(x)
}
}
where Fn and foon are defined as follows (with the constant explicitly
written out):
type Fn struct {
x int[2 ** n]
}
func foon(x Fn) { }
here are my results (note that numbers are per-call,
not per iteration, to account for my loop unrolling):
bench0 100000000 5 ns/op
bench1 100000000 6.75 ns/op
bench2 50000000 16.5 ns/op
bench3 50000000 16.25 ns/op
bench4 20000000 19.25 ns/op
bench5 20000000 26 ns/op
bench6 10000000 40.25 ns/op
note that there's almost no increase in
call overhead between 4 and 32 ints in the struct.
in fact 4 and 8 are almost identical - i wonder why that is.
this is on an intel macbook.
I'm guessing that the performance difference (or more like, the lack
of performance difference) has to do with cache misses. There is an
informative presentation at http://www.infoq.com/presentations/click-crash-course-modern-hardware
about what happens inside modern CPUs. During the time that one word
that is not cached is retrieved from memory, the CPU will execute
hundreds of instructions. So even if it takes many instructions to
copy a large structure by value, if the value is cached, there is no
significant performance difference.
I think the reasoning is as follows:
If a reference shouldn't be used to manipulate an object... don't use it to manipulate an object. You only need to follow your own rules within your own packages, since "private" data can be protected from other packages using interfaces and non-exported variables/fields.
If an object is too big to be copied efficiently... don't copy it. Most of the time receivers and parameters should be passed by pointer, unless they are small (near the size of pointers).
Ryanne
- from my phone -
Presumably if you add immutable reference types to the language,
let's say @T instead of *T, you allow a value of *T to be assigned
to a variable (ditto passed as a parameter) of type @T?
Do you let a value of type func (*T)W to be assigned to a variable
of type func (@T)W? How about assigning a func()(*T) to a
variable of type func()@W?
Does a func (X) Spoo (@T)W implement an interface method
Spoo(*T)W?
Is an []*T assignable to a []@T?
I'm not saying that you can't work out a clear, consistent set of
rules for immutable references. I am saying that the cost of adding
them may be more than expected.
--
Chris "allusive" Dollin
Because a modern Intel processor has multiple load/store units so passing something in the L1 cache consumes almost zero time. (But we all know that a lot of zeros add up to some measurable penalty...) The more important feature would be to define (or just implement in the compiler) some "sync" closure passing/calling where the function would not be scheduled bu the Go scheduler but would be called (or inlined) directly.
Yes.
> Do you let a value of type func (*T)W to be assigned to a variable
> of type func (@T)W?
Yes.
> How about assigning a func()(*T) to a variable of type func()@W?
Allowed.
> Does a func (X) Spoo (@T)W implement an interface method
> Spoo(*T)W?
Yes.
> Is an []*T assignable to a []@T?
Yes.
> I'm not saying that you can't work out a clear, consistent set of
> rules for immutable references. I am saying that the cost of adding
> them may be more than expected.
I think there is just one general principle underlying immutable
references, which is that you allow a *T on the RHS for a
corresponding @T on the LHS but not vice versa.
Cheers,
C.P.
I'm not sure whether I've understood the idea of immutable references
the right way, and how it relates to covariance and contravariance.
If we have function that uses a mutable reference:
func A( t *T) {
t.Mutate()
}
and then we assign that function to a variable:
var x func(@T) = A
will the following code mutate the parameter?
var immutableT @T = new(T)
x(immutableT)