33 ms·
Uf, I really don't like intermixing mutability with allocation location. Those seem like two completely separate concerns. One thing that was really common for
by vvanders 9y ago
Uf, I really don't like intermixing mutability with allocation location. Those seem like two completely separate concerns.
One thing that was really common for us to do was to instance a weighted graph(something like this[1]) per-actor. This means that you might have 10-300 floating point values in a block indexed by the node they interact with. It was really common to see one, maybe two values change on a per-frame basis. With the constraint above I'm now copying 300 floating point values every time any node changes which would be brutal for performance. Or I'd take a potential cache miss each time I touched the array which could be even worse if it was a reference type.
To be clear, I'm not saying Julia isn't really good at what it does. My complaint stems from the fact that you can't claim performance good as/better than C without having all these tools at your disposal.
We haven't even gotten into things like restrict[2] where things like Rust's ownership model let you get it for free[3].
[1] https://docs.unrealengine.com/latest/INT/Engine/Rendering/Materials/Editor/ https://docs.unrealengine.com/latest/INT/Engine/Rendering/Ma...
[2] https://en.wikipedia.org/wiki/Restrict https://en.wikipedia.org/wiki/Restrict
[3] https://doc.rust-lang.org/nomicon/aliasing.html https://doc.rust-lang.org/nomicon/aliasing.html
- StefanKarpinski 9y ago> I really don't like intermixing mutability with allocation location. Those seem like two completely separate concerns. They're not. The semantics of value types and reference types are different in the presence of mutation. So if you want uniform object semantics in a language, then objects that can be implemented as values must be immutable. There are many languages that have kept these independent and bifurcated their type system instead (Java, C#), but it's been a source a great deal of pain and frustration (e.g. Java's `int` versus `Integer` awkwardness). Fortunately, there's nothing you can do with mutation that you can't do just as well by modifying and replacing an immutable value in a mutable cell – the compiler implements them the same way. Wrap an immutable in a mutable `Ref` and voila, you've got something equivalent to a mutable value type without exposing completely incompatible semantics in the language.
- vvanders 9y ago> They're not. The semantics of value types and reference types are different in the presence of mutation. I think C#'s overloaded term for "value-types" may have caused you to misinterpret the above. Let me be more clear, if a value lives on the stack or the heap is completely separable from if it is mutable or not, that's my objection. > ... just as well by modifying and replacing an immutable value in a mutable cell In terms of "correctness", sure. In terms of performance see my 300+ float block example above. I shouldn't have to copy, modify, copy when I can just mutate in-place(and be explicit about that rather than relying on language semantics). Also I can seem to find any mention in the docs about Ref's semantics aside from a passing mention in the FFI section.
- anon_342njlkesr 9y agoI had the same problem; my problem was not the giant number of reads, but rather that a naive replacement needs a read and has a dependency on the write-back; hence, a scatter where you modify immutable structures by replacing certain fields induces a stall on cache-miss. In my case, julia/llvm was smart enough to figure out that the read and write can be eliminated. Hence, the julia code that replaces an immutable with a copy where only few fields are changed generates the same @code_native as the obvious evil construction (figure out where the field is stored; unsafe_store! to the pointer). But I guess this optimization is unreliable, or at least it is not well documented when this optimization is guaranteed to happen. So the situation is not optimal, but also not as catastrophic as you would have guessed without reading the generated native code.
- StefanKarpinski 9y agoRight, so the way to address this is to provide guarantees that this kind of optimization /will/ occur and providing syntax for making writing "pseudo-mutating" code more convenient. There's a PR [1] for the latter, but it's been shelved while we focus on getting 1.0 out the door instead of adding new features. Optimization guarantees + convenient syntax provides everything you need without trashing the semantics of the language by bifurcating the type system into two incompatible kinds of values. [1] https://github.com/JuliaLang/julia/pull/21912 https://github.com/JuliaLang/julia/pull/21912