Earlier quoted context omitted.
Reference counting is not a form of garbage collection. Both reference counting and garbage collection are types of automatic memory management.
This discussion crops up each time reference counting is brought up. In academia reference counting generally falls under the umbrella of GC. In the industry "GC" usually means "tracing GC". The terminology is irrelevant to the point I'm making. I did make a distinction between RC and the "regular magical kind" of GC.
Four years with Rust
171–180 of 199 posts
Re: Four years with Rust
#172Earlier quoted context omitted.
In Rust, you'd define an `enum` of two types. enum Either { Type1(T1), Type2(T2), } let's say you get a value of type Either then you can match on them: match value { Type1(v) => func(v), Type2(v) => func2(v), }
Does that let you work with the default type? What I was doing in c#: function(T1 thing) { //do normal T1 stuff here var foo = arg as T2; if (foo != null) foo.T2Stuff(); //more normal T1 stuff } If I'm understanding your example I would have to wrap all the T1 stuff in a match.
Rust doesn't have type inheritance, so the only way you can define relationships between types is to have something external tell you how to package them together, which is what the enum would do here. So it's not a parent-child relationship between the types, it's a new thing which says "here is a thing which can be a T1 or a T2 but not both at once".
Not having OO does require you to think somewhat differently about how to build your data structures in Rust. I've found my intuition from Haskell is a stronger guide when working with Rust, but since Rust's type system is much more like that in an ML-family language this makes quite a bit of sense! Sadly this does increase the learning curve for more mainstream languages.
(Rust does have trait inheritance, but all that says is that to implement trait T2 you have to also implement trait T1, and thus anything working with a T2 can assume that thing is also a T1).
Re: Four years with Rust
#173Earlier quoted context omitted.
Why do you still think the compiler can't host those out of loops? LLVM is perfectly capable of doing so in many cases and you've been told as much previously, could you make your claim more specific?
Where does LLVM do that? LLVM does hoist of invariant code out of loops in the Loop Invariant Code Motion and Loop Strength Reduction phases.[1] But that's not enough. This isn't an invariant situation. Consider a matrix multiply, the most common operation in number-crunching. You're indexing through three 2D matrices along both axes. The indices are usually controlled by FOR statements, so the compiler knows the ran…
for i in 0..X {
if !(i
It is capable of doing this with the various induction variable passes it has, showing that i There's also the IRCE (inductive range check elimination) [0] pass—which isn't listed in that document—that should help even more, if/when it is enabled.Hopefully you agree at this point that LLVM is perfectly capable of handling multidimensional arrays and matrices even if it doesn't have optimisation passes with names that obviously apply to them specifically. Most of the optimisations you keep talking about are basic consequences of other standard passes, a fact that has been pointed out many times to you before.
> Consider a matrix multiply, the most common operation in number-crunching
Also an operation you won't be implementing manually if you actually care about performance (better implementation: call a BLAS library). And, any high performance implementation will be doing more than the naive triple-nested loop (blocking, SIMD, etc.). Focusing on this operation is somewhat missing the forest for the trees.
> You're indexing through three 2D matrices along both axes. The indices are usually controlled by FOR statements, so the compiler knows the range the indices can take.
Yes exactly, the compiler knows the range of the indices.
> If the compiler knows about multidimensional arrays, it's easy to make those checks once at FOR loop entry.
This is a non-sequitur: the compiler can still make/move the checks based on what the code looks like, without having to have a hardcoded concept of arrays. Ensuring optimisation passes are powerful enough to handle these sort of relatively straight-forward cases is more general than relying on a language-level concept of multidimensional arrays, as it allows them to apply to cases which can't quite be implemented with an array directly.
> Although technically you could generate a special case
This is exactly what compilers do, see the IRCE documentation.
> The compiler can't optimize it that aggressively and fail early
It certainly can: these sort of assertions shouldn't ever trigger, and with appropriate top level assertions (e.g. asserting that the dimensions of the incoming matrices work for multiplication) the compiler can easily see this. Also, the compiler can hoist assertions early if this cannot be observed externally, e.g. the loop kernel only mutates locals.
> If all those optimizations really exist, why is there code like this (at https://github.com/SiegeLord/RustAlgebloat/blob/master/algeb...)?
That code is almost 2 years old, and so the compiler will have improved since then. Of course, the compiler may not have improved enough (e.g. IRCE still may not be enabled). Additionally, there's a lot of uncertainity/lack of clarity (as demonstrated by your own comments) about exactly what the compiler can do, and so people may use `unsafe` unnecessarily. I've certainly been guilty of this myself, and been glad when people have pushed back in code-review, making me work a bit harder to get equal (or better) performance in safe code.
[0]: http://llvm.org/docs/doxygen/html/InductiveRangeCheckElimina...
Re: Four years with Rust
#174Earlier quoted context omitted.
Why do you still think the compiler can't host those out of loops? LLVM is perfectly capable of doing so in many cases and you've been told as much previously, could you make your claim more specific?
Where does LLVM do that? LLVM does hoist of invariant code out of loops in the Loop Invariant Code Motion and Loop Strength Reduction phases.[1] But that's not enough. This isn't an invariant situation. Consider a matrix multiply, the most common operation in number-crunching. You're indexing through three 2D matrices along both axes. The indices are usually controlled by FOR statements, so the compiler knows the ran…
However, there's a trick. Take a look at https://github.com/rust-lang/rust/commit/6a7bc47a8f1bf4441ca... where the Rust developers had the same issue, and managed to avoid the bound checks without having to use unsafe code. You have to somehow make the optimizer see that both sides have the same length, so it'll elide both bounds checks. A slice is actually a pair of a pointer to the first element, and the length. Their trick copies the length from one slice to the other (after a bound check, obviously), so the compiler is sure that the lengths are the same.
Re: Four years with Rust
#175Earlier quoted context omitted.
Can you talk more about using Pony in a production environment? What are the performance aspects of it? How do you find writing 'non-actor' code - like just doing some string manipulation? I find the language fascinating but I'm learning Erlang and I don't really want to get started with Pony at the same time. Do you have experience with Erlang? what made you choose Pony? Sorry for the bombardment of questions but I…
I've never met anyone else using Pony in production, so I'll try and give the best answer I can. > What made you choose Pony? We wanted a Type Safe language to run a REST API frontend. That is to say, we wanted to have something that could redirect requests to the appropriate servers, at scale, whilst maintaining Type Safety in the server itself. We got hit by so many issues from JSON's weak/absent typing causing run…
Re: Four years with Rust
#176In today’s Rust, this would be an error, you need to write (e.f)(); At least the error tells you exactly what to do! If the programmer knows what the error is, and how to correct it, then their program should automatically do so, rather than molesting the user, as the purpose of computers is to speed up and automate. Why should the user have to foot the designers' bills? Another cardinal sin and a sign of lack of thi…
Besides, the change we are talking about happened 4 years ago, when Rust emphatically did NOT have any compatibility guarantees and was in a process of rapid experimentation. This has changed a whole lot – it's a stable language now, and has been for a year and a half.
Re: Four years with Rust
#177I would appreciate if anyone can share your dev setup for Rust. I tried Rust and Racer long time ago and it's not a pleasant experience.
Re: Four years with Rust
#178Earlier quoted context omitted.
This discussion crops up each time reference counting is brought up. In academia reference counting generally falls under the umbrella of GC. In the industry "GC" usually means "tracing GC". The terminology is irrelevant to the point I'm making. I did make a distinction between RC and the "regular magical kind" of GC.
Garbage collection requires a garbage collector, which reference counting doesn't have or need. This is true for both academia and industry.
Re: Four years with Rust
#179For those of us who are getting to the party 3 years late, thank you. Just my 2p for others learning: for me, Rc::RefCell was what I was missing, even after I thought I was up to speed. I was fine using Channels for inter-thread communication and I never needed Arc, but use of Rc is common in the Rust ecosystem and a lot of my early fights with the borrow checker weren't fights I needed to have. In situations where i…
> Rc is common in the Rust ecosystem I'm really surprised you think that's the case. Most rust codebases I've worked with use Rc very sparingly, if at all. If a codebase does use Rc there's usually one central thing that is Rc'd, with everything else using regular memory management. I have noticed that beginners coming from GCd languages often tend to structure their code in such a way that paints them into a corner…
Right.
In my case, I was using the nphysics library, which for unsurprising reasons uses Rc for entity handles.
Similarly, in my app's code, I used nphysics' proximity and contact handlers to watch for events that indicated a change in status. Those handlers needed to initialize/set values in a HashMap accessible (and periodically cleared by) the main sim loop -- 'one central thing' that is Rc'd.
It's not sprinkled everywhere in your codebase, and for obvious reasons most libraries don't need to use it. But in applications, a central loop + library-provided callbacks + some Rc'd state of doesn't seem too uncommon.
Re: Four years with Rust
#180Earlier quoted context omitted.
An exception (large complicated Rc-trees) is the rope in xi-editor. If you load a very large file, the operation of letting go of the last reference is potentially a large enough pause to have an effect on UI responsiveness. I've considered adding a mechanism that moves the object into a deallocation thread (or a deallocation task running in a thread pool) for this reason, although I'm not sure how important it is in…
> I've considered adding a mechanism that moves the object into a deallocation thread (or a deallocation task running in a thread pool) for this reason, although I'm not sure how important it is in the grand scheme of things. The one time I've done this in C++, delayed deallocation actually made major loads/unloads worse for us on mass batch operations - more cache thrashing perhaps? Since this involved GPU resources…