5 ms·
In what situation would you want to do this?
by augustk 10y ago
In what situation would you want to do this?
- ekidd 10y agoThis like this are extremely common in performance-critical code. In this case, you want to: 1. Allocate both X and Y in the same block of memory, maybe on the stack, perhaps in a heap. In inner loops, making calls to malloc is pure performance poison. Most common garbage collected languages are completely unable to do this, with the admirable exception of the C# and the other .NET languages (and possibly some others). 2. You want to access Y by reference, so that you can work with it without needing to make a copy. Again, this is very common in performance-critical code. You can do this in C, C++ and other existing "systems" languages. 3. You don't want to accidentally keep using Y once X (and hence the underlying storage for Y) is destroyed. This is a subtle and vile bug that can memory corruption, once-a-week crashes on production, and week-long debugging sessions, among other headaches. This is where C and C++ fail, and where Rust nails it.
- rbehrends 10y ago> In inner loops, making calls to malloc is pure performance poison. Most common garbage collected languages are completely unable to do this, with the admirable exception of the C# and the other .NET languages (and possibly some others). Modern GCs use bump allocators and (ideally) combine allocations of objects and their subobjects where that is possible.
- ekidd 10y agoEven with a bump allocator, you're still putting large amounts of data into the nursery generation. If you're in a performance critical inner loop (for each line of a 50 gigabyte CSV file, or for each scan-line in an image codec), this is still a bad idea, performance-wise. You still can't beat static stack allocation, where you increment a pointer once to create a stack frame, and decrement it to free memory. In really fast Rust code, my goal is usually to entirely eliminate all heap allocation in favor of zero-copy parsers. This can be tricky—especially for streaming I/O using buffers, because a single chunk of input might get split over two buffers—but Rust's borrow checker makes it possible to maintain references into other people's buffers without shooting yourself in the foot.
- jerven 10y agoWith java scalar replacement (possible from escape analysis/partial or not) calling new Point(x,y) in a hot loop does not allocate an object on heap at all (or even the stack) unless it escapes. In the newer JITs that escaping allocation will be deferred until the last possible point in time. This is actually a nice point about JVM languages, _new_ has defined behavior and can thus be elided if the effect is not observable. malloc (or new in C++, although I am not sure about the spec there) do not and eliding a call to new may break the language specification as function is not called that should have been. I don't know what Rust does here, and what is allowed in regards to the new operator. i.e. must a local variable be on the stack or can it generate direct values in machine registers?
- Rusky 10y agoRust doesn't have a new operator (its equivalent to malloc is Box). When that's used, it forces the value onto the heap- but that's not what you would write most of the time. Local variables that are not explicitly heap-allocated (and don't have their address taken) can be on the stack or in registers, and this is true of both Rust and C/C++.