News for hackers. Stuff that matters.
Re:Rust: Dropping heavy things in another thread can make yo... by fpgaminer on Saturday May 30, 2020 @01:58PM (#23363153)
Some important things I think people should note before blindly commenting:

* The example code is obviously contrived. The real gist is that massive deallocations in the UI thread cause lag, which the example code proves. That very thing can easily happen in the real world.

* I didn't see any difference on my machine between a debug build and a release build.

* The example is preforming 1 _million_ deallocations. That's why it's so pathological. It's not just a "large" vector. It's a vector of 1 million vectors. While that may seem contrived, consider a vector of 1 million strings, something that's not too uncommon, and which would likely suffer the same performance penalty.

* Rust is not copying anything, nor duplicating the structures here. In the example code the structures would be moved, not copied, which costs nothing. The deallocation is taking up 99% of the time.

* As an aside, compilers have used the trick of not free-ing data structures before, because it provides a significant performance boost. Instead of calling free on all those billions of tiny data structures a compiler would generate during its lifetime, they just let them leak. Since a compiler is short lived its not a problem, they get a free lunch (pun unintended), and the OS takes care of cleaning up after all is said and done. My point is that this post isn't theoretical, we do deallocation trickery in the real world.

Re:Rust: Dropping heavy things in another thread can make yo... by ckcheng on Saturday May 30, 2020 @04:31PM (#23364412)
>As an aside, compilers have used the trick of not free-ing data structures before, because it provides a significant performance boost. Instead of calling free on all those billions of tiny data structures a compiler would generate during its lifetime, they just let them leak. Since a compiler is short lived its not a problem, they get a free lunch (pun unintended), and the OS takes care of cleaning up after all is said and done. My point is that this post isn't theoretical, we do deallocation trickery in the real world.

This reminds me of the exploding ultimate GC technique [1]:

> on-board software for a missile...chief software engineer said "Of course it leaks". ... They added this much additional memory to the hardware to "support" the leaks. Since the missile will explode when it hits its target or at the end of its flight, the ultimate in garbage collection is performed without programmer intervention.

[1]: https://devblogs.microsoft.com/oldnewthing/20180228-00/?p=98...

Re:Rust: Dropping heavy things in another thread can make yo... by all-fakes on Saturday May 30, 2020 @07:45PM (#23365871)
Yes, Java also has a garbage collector that does nothing, called the Epsilon GC, intended for short-lived programs and references for garbage collector benchmarks.[0]

[0]: https://blogs.oracle.com/javamagazine/epsilon-the-jdks-do-no...

Re:Rust: Dropping heavy things in another thread can make yo... by inopinatus on Saturday May 30, 2020 @08:29PM (#23366108)
The bumper sticker is “Ada programmers prove the algorithm terminates.”
Re:Rust: Dropping heavy things in another thread can make yo... by blub on Sunday May 31, 2020 @04:18AM (#23368076)
I guess the bigger question is why they use dynamic memory allocation in the first place.
Re:Rust: Dropping heavy things in another thread can make yo... by rubber_duck on Saturday May 30, 2020 @02:56PM (#23363633)
>As an aside, compilers have used the trick of not free-ing data structures before, because it provides a significant performance boost. Instead of calling free on all those billions of tiny data structures a compiler would generate during its lifetime, they just let them leak. Since a compiler is short lived its not a problem, they get a free lunch (pun unintended), and the OS takes care of cleaning up after all is said and done. My point is that this post isn't theoretical, we do deallocation trickery in the real world.

And then someone tries to use your compiler as a service (code analysis, change triggered compiler) and it's a dead end

Re:Rust: Dropping heavy things in another thread can make yo... by qzw on Saturday May 30, 2020 @04:00PM (#23364123)
Well, then that’s not the original use case anymore, and it’ll have to be re-engineered. In the meantime it may have been used for years and the perf difference may have saved many developer-years collectively across its user base. Surely you’re not suggesting that the compiler developers should be prematurely optimizing for future use cases that they may not even have envisioned.
Re:Rust: Dropping heavy things in another thread can make yo... by rubber_duck on Saturday May 30, 2020 @04:16PM (#23364281)
Avoiding leaks is not optimisation, it's a matter of correctness - not freeing memory is an optimisation based on a very shortsighted assumption that is not practical for any new language (modern languages are expected to come with language server support)
Re:Rust: Dropping heavy things in another thread can make yo... by nelhage on Saturday May 30, 2020 @11:38PM (#23366978)
We used precisely this optimization in [sorbet](https://sorbet.org), a brand-new type checker for Ruby, which also contains a high-performance LSP server.

We wrote the entire thing (and tested, using ASAN and fuzzers and other techniques) to avoid leaking memory, and then strategically inserted [the equivalent of a rust `mem::forget`](https://github.com/sorbet/sorbet/blob/0aae56e73c7680ec6053b3...) into the end of the `main` driver during standalone mode, to avoid calling those destructors when we're about to exit anyways.

This optimization is definitely still relevant for new systems today.

Re:Rust: Dropping heavy things in another thread can make yo... by jpitz on Saturday May 30, 2020 @06:53PM (#23365555)
Correctness means adherence to the spec, not some contrived absolute truth.
Re:Rust: Dropping heavy things in another thread can make yo... by viraptor on Saturday May 30, 2020 @07:21PM (#23365726)
But the spec usually has some implicit assumptions. Usually it's "app doesn't leak memory" in the same way nobody explicitly specifies "result of an addition of natural numbers should match ...".

We don't go around saying "oh, you didn't want modulo 5 arithmetic? You should've put that in the spec, not rely on some contrived absolute truth".

Re:Rust: Dropping heavy things in another thread can make yo... by jpitz on Saturday May 30, 2020 @07:30PM (#23365786)
Okay, but we're talking about an application, a piece of software who's primary intention is to run for a short period of time, parse a text file and transform it. It will be ran many many many times a day by developers whose time is expensive. Language servers are a very new concept in terms of being part of the day-to-day tool chains for most developers. Trading garbage collection for compile time was absolutely a winning strategy for compiler writers. It made them money.
Re:Rust: Dropping heavy things in another thread can make yo... by pjmlp on Monday June 01, 2020 @03:13AM (#23376032)
Language servers go back all the way to Xerox PARC workstations and Lisp machines.

C++ had its very first language servers via Lucid's Energize C++ and Visual Age for C++ v4.

Here is the 1993 video and related paper from Lucid.

https://www.youtube.com/watch?v=pQQTScuApWk

http://www.dreamsongs.com/Cadillac.html

And some information regarding VA, unfortunately most is missing from online world.

https://www.ecomstation.it/pido2/home/esterni/vac40os2.pdf (codestore)

http://www.edm2.com/index.php/VisualAge_C%2B%2B_4.0_Review

It is also why Delphi, VB and C++ Builder were already such a pleasure to use versus the Makefile and vi world of UNIX.

Re:Rust: Dropping heavy things in another thread can make yo... by viraptor on Sunday May 31, 2020 @03:32AM (#23367915)
"We made dodgy choices to be competitive" is a valid explanation. They don't start being dodgy when new use cases come around and technical debt bites you in the butt.

I see not freeing as a clever bug/workaround with nice positive side effects. Not as a clever solution.

Re:Rust: Dropping heavy things in another thread can make yo... by nitwit005 on Sunday May 31, 2020 @03:00AM (#23367783)
But the memory doesn't leak, it gets freed when the program exits.

The user doesn't care that each allocation is paired with another call to free it up. They just care if the program runs quickly and doesn't use too much memory overall.

Re:Rust: Dropping heavy things in another thread can make yo... by viraptor on Sunday May 31, 2020 @03:27AM (#23367894)
Unless the system doesn't have enough memory to allocate for large compilation units and starts swapping allocated/dead memory and slowing down massively. Unless the user runs a CI where as many things should be compiling at the same time as possible and slowly freeing memory in 3 concurrent processes is better than running only 1 at a time. Unless they have spare cores, but no spare memory. Unless ...

> They just care ...

It all depends on who "they" are and what they want to achieve.

Re:Rust: Dropping heavy things in another thread can make yo... by jashmatthews on Saturday May 30, 2020 @07:40PM (#23365848)
It’s usually the opposite! Correctness work usually has the implicit assumption of infinite memory.
Re:Rust: Dropping heavy things in another thread can make yo... by rwallace on Sunday May 31, 2020 @05:56AM (#23368427)
> nobody explicitly specifies "result of an addition of natural numbers should match ..."

Yes they do. In most programming languages, by default integer arithmetic is modulo 2^64 (at best). If you want arbitrary precision arithmetic, you have to explicitly specify that.

Re:Rust: Dropping heavy things in another thread can make yo... by jashmatthews on Saturday May 30, 2020 @06:43PM (#23365490)
Why do you say that? Even if you call free immediately after a piece of memory is no longer needed, malloc won’t release that immediately anyway.

If this is incorrect, then every modern malloc implementation is incorrect.

Re:Rust: Dropping heavy things in another thread can make yo... by random314 on Saturday May 30, 2020 @05:29PM (#23364918)
You have not provided any refutation to the OPs argument.
Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Saturday May 30, 2020 @05:24PM (#23364865)
I am suggesting they apply good practices. I'd never imagine that compilers were actually doing what was stated -- sounds awful.

I understand it's tradeoffs and we all have real-world limitations to contend with -- but again, of all the corners that could be cut that's exactly the one I didn't imagine they would.

Nasty.

Re:Rust: Dropping heavy things in another thread can make yo... by correnos on Saturday May 30, 2020 @07:08PM (#23365651)
Of the three compilers I've worked on in-depth, only one of them had a "normal" memory management scheme.

One of them was unburdened by any thought of freeing stuff, and relied entirely on the application exiting for cleanup. This was very convenient to work with, and never ended up posing an issue.

Another used a series of allocation arenas, where certain arenas would be cleared at certain points in the compiler pipeline. This made for both speedy alloc/freeing and avoided leaks, since you weren't at risk of "forgetting" a data structure. It was also a major headache to keep track of exactly what the longest lifetime of a long-lived datastructure might be, and to pick an arena that won't be cleared in the meantime. Unfortunately the programs compiled with this compiler were large enough that we certainly couldn't have gotten away with just leaking memory; we sometimes OOMed as-is!

The third used standard C++ memory management. This compiler was quite simple, and the vast majority of its data used stack-based lifetimes. For a more complex compiler this would've become a headache.

I think that all of these compilers chose the correct allocation strategy for what they were doing. "Good practices" aren't as universal as we might like to believe, they depend entirely on the context in which a tool is designed to operate. And yes, we can guard to some extent against that context changing, but for the most part that's why we keep getting paid.

Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Saturday May 30, 2020 @07:53PM (#23365930)
Judging by the downvotes I am getting (with zero explanation as to why), I'd say that many misunderstood the "good practices" part and took offence, as if I said there's only one good practice.

I was taught -- including at the start of my career when I used exclusively C/C++ (about 18.5y ago) -- to take care of all resources I was using and not rely on runtimes.

I understand and appreciate different usages but to me doing a proper cleanup was the sane default for most programmers. And that's all what I was saying.

Obviously, as one digs deeper in a specialised area where more and more efficiency is demanded then they have to reach for tools that most of us wouldn't normally. That's quite normal and was always interesting for me to read about.

Re:Rust: Dropping heavy things in another thread can make yo... by grayclhn on Saturday May 30, 2020 @09:36PM (#23366463)
I didn't downovte, but asides like, "sounds awful" and "nasty" definitely read as judgemental. Your other comments on this thread make you seem much more open minded than this one did.
Re:Rust: Dropping heavy things in another thread can make yo... by thebean11 on Saturday May 30, 2020 @06:07PM (#23365209)
Can you articulate why it's a bad practice? If it works better than alternatives and it's documented, not really sure what the issue is.

I don't think it's even that uncommon. I believe some HFT firms run Java with a huge amount of RAM and GC disabled, and get around it by just rebooting the software occasionally.

To me writing software like that is fair game, I don't see the point in being dogmatic about "how things should be done".

Re:Rust: Dropping heavy things in another thread can make yo... by nitrogen on Wednesday June 03, 2020 @04:19PM (#23408148)
This thread has a lot of branches, so I am just picking one for this general reply:

I recall reading somewhere, years ago, that some OSes couldn't be relied upon to release unfreed memory when a process terminated. In those contexts, fastidious freeing would be important even in short-lived processes.

In my own C code I tend to free everything so that I get a clean trace from Valgrind and don't risk masking legitimate bugs, but I typically write long-running daemons.

Re:Rust: Dropping heavy things in another thread can make yo... by jfkebwjsbx on Sunday May 31, 2020 @01:00AM (#23367322)
It is bad practice if your code cannot turn on deallocation for debugging purposes or library-like usage.
Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Saturday May 30, 2020 @06:25PM (#23365347)
Mostly because I look at it from the angle of one-off / general purpose / CLI programs. If one such has to run for 10-30 seconds and its memory just keeps growing and growing with the idea of throwing it all away at the end and letting the OS handle it, it might become disruptive for other programs on the machine.

For specialised apps and servers it's of course a perfectly good practice.

Re:Rust: Dropping heavy things in another thread can make yo... by rcxdude on Saturday May 30, 2020 @05:31PM (#23364944)
Deallocation at the end of a program's execution can substantially add to its runtime, and it's entirely waste. It's a much more common strategy than you might think.
Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Saturday May 30, 2020 @05:37PM (#23364990)
You are right, I indeed didn't know it was that common.

But still, in a world where languages and runtimes are also judged by their ability to run in lambda/serverless setups, I'd think this practice will start being obsolete, wouldn't you think?

(What I mean is that I imagine that any serverless function that runs in severely constrained and measured environments like the AWS Lambda would gain a significant edge over the competition if it did an eager cleanup. Should allow more of them to work in parallel?)

Re:Rust: Dropping heavy things in another thread can make yo... by setr on Saturday May 30, 2020 @07:32PM (#23365793)
I don't understand; a lambda function executes and closes, and any leak gets handled by lambda runtime (by freeing the whole lambda function)-- exactly the same as running a cli tool. It seems to me that the server-less context is actually exactly where you'd employ this strategy: the programs by definition cannot be long-lived, so unless they generate so much garbage as to oom in that timeline, there is no need to deallocate anything yourself.

The leak to watch out for is reduced to the persisted data (eg in S3) between executions

Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Saturday May 30, 2020 @07:49PM (#23365903)
Yeah, this is what I was saying -- that it could produce too much garbage if no manual free-ing of memory is done.

I suppose, judging by the downvotes, that some find that perspective naive so I'll just cut it here because no productive discussion is happening. :(

Re:Rust: Dropping heavy things in another thread can make yo... by setr on Sunday May 31, 2020 @10:31AM (#23369796)
the purpose/pattern is to ignore deallocation when the program finishes -- not during runtime. If you have 30MB of space, and your program will only ever use 20MB, why bother?

You only need to care when you're at risk of OOM'ing during runtime. If you're generating too much garbage, you either need to start cleaning up after yourself, or you need to generate less of it -- but it doesn't matter which choice you take from the perspective of the OS or the server-less function.

The only reason I can think of for server-less to impact the validity of this pattern is from it's lower memory capacity -- but otherwise it's the same constraints. And anyways, it's probably not nice to generate a few GB of garbage even if the user has the space

Re:Rust: Dropping heavy things in another thread can make yo... by folmar on Saturday May 30, 2020 @06:21PM (#23365315)
Most compiler are not designed to run in daemon mode, specifically it's non-issue since their startup is normally fast. And the compiler and runtime are a different thing.
Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Saturday May 30, 2020 @06:26PM (#23365357)
I realise that, but nowadays language servers are a pretty normal practice in no small amount of areas.
Re:Rust: Dropping heavy things in another thread can make yo... by smabie on Saturday May 30, 2020 @09:48PM (#23366522)
You mean like LSP? Why would a compiler and a language server be integrated in the first place? Clearly a LSP server wouldn't use a no-freeing strategy, but there's no reason why it would cause issue with a compiler. A compiler must terminate, assuming parsing isn't turing complete (like Perl) and that the source code is finite. This is why it's okay for it to leak memory.
Re:Rust: Dropping heavy things in another thread can make yo... by steveklabnik on Sunday May 31, 2020 @09:07AM (#23369331)
> Why would a compiler and a language server be integrated in the first place?

Because a language server needs to do a lot of the same work as a compiler.

This is an eventual hope for rustc. For now, the latest language server and it share a bunch of libraries, but language servers are effectively compiler frontends.

Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Sunday May 31, 2020 @08:23AM (#23369099)
Yep, I guess I conflated compilers and LSP. I imagined compilers would have daemon modes. Seems like I was wrong.
Re:Rust: Dropping heavy things in another thread can make yo... by michaelcampbell on Sunday May 31, 2020 @08:16AM (#23369056)
> I'd never imagine that compilers were actually doing what was stated -- sounds awful.

And how many millions of iterations have been done successfully in that "awful" system?

The very fact that you never imagined it I think says a lot.

Re:Rust: Dropping heavy things in another thread can make yo... by pdimitar on Sunday May 31, 2020 @08:20AM (#23369079)
Well, I have been taught to take good manual care of all used resources. It is kind of a cognitive shock when you see all your training hand-waved away with "let the OS handle my mess of allocated objects that I'll never call `free` on". It's kind of disappointing on some level. :)

As I acknowledged in other comments of mine downthread, I understand that different situations require different tradeoffs. It's just that forgoing memory deallocation wasn't one of them in my head.

Re:Rust: Dropping heavy things in another thread can make yo... by ycombobreaker on Saturday May 30, 2020 @04:14PM (#23364261)
In a world where processes can fork-and-exec, nothing about "as a service" changes that. The compiler would just be reinvoked as needed. Converting it into a persistent process breaks a lot more than just allocation optimizations.
Re:Rust: Dropping heavy things in another thread can make yo... by rubber_duck on Saturday May 30, 2020 @04:18PM (#23364300)
But you want to share state and only update state incrementally on edit to get any reasonable level of performance for stuff like language server code analysis.
Re:Rust: Dropping heavy things in another thread can make yo... by all-fakes on Saturday May 30, 2020 @06:06PM (#23365193)
I'm not sure what the performance overhead would be, but that state can easily be stored on disk.
Re:Rust: Dropping heavy things in another thread can make yo... by tsimionescu on Sunday May 31, 2020 @06:28AM (#23368555)
I'd bet it's a lot harder to rework the compiler to update its state in place than it is to rework it to free memory.
Re:Rust: Dropping heavy things in another thread can make yo... by yrro on Saturday May 30, 2020 @03:47PM (#23364002)
As distasteful as leaky code is, is it that bad to run it in a separate process? You get a bit more robustness against crashes as well.
Re:Rust: Dropping heavy things in another thread can make yo... by rubber_duck on Saturday May 30, 2020 @04:21PM (#23364329)
You want to be able to incrementally update state to get performance out of incremental code analysis (eg. language server implemention for IDE)
Re:Rust: Dropping heavy things in another thread can make yo... by folmar on Saturday May 30, 2020 @06:27PM (#23365365)
There are more ways to go then that: AST fragment caching, intermediate representation fragment caching and so on. Incremental updates fit some languages better than the others.
Re:Rust: Dropping heavy things in another thread can make yo... by hinkley on Saturday May 30, 2020 @03:35PM (#23363920)
One of my “favorite” snags in perf analysis is that periodicity in allocations can misattribute the cost of allocations to the wrong function.

If I allocate just enough memory, but not too much, then pauses for defragmentation of free space may be costed to the code that calls me.

A solution to this that I’ve seen in soft real time systems is to amortize cleanups across all allocations. Every allocation performs n steps of a cleanup process prior to receiving a block of memory. In which case most of the bad actors have to pay part of the cost of memory overhead.

Might be good for Rust to try something in that general realm, or in the cleanup side may be easier to tack on. On free, set a ceiling for operations and queue what is left. That would at least peak shave.

Re:Rust: Dropping heavy things in another thread can make yo... by rini17 on Saturday May 30, 2020 @05:28PM (#23364915)
Doing unrelated cleanup sounds like flushing CPU cache per every allocation.
Re:Rust: Dropping heavy things in another thread can make yo... by hinkley on Saturday May 30, 2020 @06:50PM (#23365536)
This post is already about unrelated cleanup, so I'm not sure what other escape hatch you imagine people taking. Do you have a suggestion?

You can tell that it's unrelated cleanup because if it were related, then the cost of freeing wouldn't be noteworthy. It would be cache hot because you would be visiting it for the second time. In which case we'd be talking about why you are scanning a giant object on an event loop in the first place. That's not what's at issue. What's at issue is that you've been handed this great bomb of uncached data from someone else and now you're stuck doing the janitorial work.

Freeing an object of arbitrary size is effectively an unbounded operation. Cache invalidation has a very high cost, sure, but it's still bounded.

Putting a limit on the amount of work you do, you could stop before purging the entire cache. You could use a smaller limit on doing work for someone else and control invalidation there, too.

Re:Rust: Dropping heavy things in another thread can make yo... by papaf on Saturday May 30, 2020 @02:20PM (#23363331)
This deallocation trick is neat but in C and C++ you could use a memory pool to do this.

In theory, you could also use a memory pool in Rust but I think the standard library uses malloc without some way of overriding this behaviour.

Re:Rust: Dropping heavy things in another thread can make yo... by vvanders on Saturday May 30, 2020 @02:50PM (#23363582)
You can also use the typed-arena crate[0] or roll your own if you're feeling like cracking open unsafe.

[0] https://crates.io/crates/typed-arena

Re:Rust: Dropping heavy things in another thread can make yo... by all-fakes on Saturday May 30, 2020 @07:51PM (#23365916)
Typed-arena is a bit limited, because all the allocated values must be of the same type. bumpalo[0] removes that restriction, but it also doesn't run destructors, which can lead to memory and other resource leaks. Another worry I have with bumpalo is that I don't think it's been reviewed thoroughly for unsoundness.

Still, bumpalo has been used to great effect in dodrio[1], a React-like library for Rust with really good performance.

[0]: https://crates.io/crates/bumpalo

[1]: https://github.com/fitzgen/dodrio

Re:Rust: Dropping heavy things in another thread can make yo... by orf on Saturday May 30, 2020 @02:32PM (#23363437)
You can change the global allocator in any rust project. You can write your own easy enough, or use one like jemalloc
Re:Rust: Dropping heavy things in another thread can make yo... by josephg on Saturday May 30, 2020 @07:28PM (#23365772)
Sure; but when you’re using arenas and things like that you usually you will want different objects allocated into different pools (or with different lifetime properties). Rust only lets you pick one allocator for the entire process, so you can’t specify “all the children of this data structure go in arena A, and this other allocation goes into a traditional heap”.

It’s more awkward, but I much prefer Zig’s approach here where everything that allocates takes an allocator as a parameter. Usually the allocator is specified at compile time - in which case zig can generate identical code to the rust compiler. But when you want the flexibility, it’s there.

Aside from compilers, this is heavily used in video games where there’s often a lot of objects that get allocated per frame, and can be discarded all together. And in that case rust’s lifetime tracking would be a huge asset. The dovecot email server (C) also makes superb use of a mix of memory containers for different tasks. Given how messy email parsing is, dovecot is an absolute pleasure to read.

Re:Rust: Dropping heavy things in another thread can make yo... by steveklabnik on Saturday May 30, 2020 @07:40PM (#23365843)
Not in an abstract way, but you can do this. The compiler has (had? It’s been a while) two different major arenas, in my understanding. There’s just no non-global allocator trait yet, so it’s not easy to abstract over.
Re:Rust: Dropping heavy things in another thread can make yo... by andrepd on Saturday May 30, 2020 @08:05PM (#23365993)
>everything that allocates takes the allocator as a parameter

This is also the C++ approach

Re:Rust: Dropping heavy things in another thread can make yo... by afiori on Sunday May 31, 2020 @07:04AM (#23368718)
As someone that is interested in the topic and in Zig's "provide your own allocator" approach I have a question: would it be possible to make an allocator wrapper that moves values to be deallocated to a different thread?

As far as I know it would require both Rust's borrowing semantics and Zig's architectural choice.

From purely my own fan-boy perspective Zig approach is something that I would have really liked for Rust to adopt (I have no idea of which one came first chronologically).

Re:Rust: Dropping heavy things in another thread can make yo... by steveklabnik on Sunday May 31, 2020 @09:08AM (#23369337)
It is not an allocator wrapper, but https://docs.rs/defer-drop/1.0.1/defer_drop/index.html
Re:Rust: Dropping heavy things in another thread can make yo... by francasso on Saturday May 30, 2020 @07:26PM (#23365759)
I don't get this. Usually I either don't care about how stuff is allocated, or when I care I want to be specific about what allocator should be used where. In the second situation just setting a global allocator wouldn't cut it most of the time, I would want support for customization when I instantiate a data structure.
Re:Rust: Dropping heavy things in another thread can make yo... by bauerd on Saturday May 30, 2020 @02:53PM (#23363605)
Think with LD_PRELOAD it is always possible to override the allocator?
Re:Rust: Dropping heavy things in another thread can make yo... by jfkebwjsbx on Sunday May 31, 2020 @12:57AM (#23367307)
That wouldn’t help. The point is to use local allocators which have extra information about the data structures and usage of the memory.

This is routinely done in medium to large C++ programs for different reasons (performance, debuggability...).

Re:Rust: Dropping heavy things in another thread can make yo... by dan-robertson on Saturday May 30, 2020 @06:52PM (#23365551)
Only if it is dynamically linked
Re:Rust: Dropping heavy things in another thread can make yo... by wongarsu on Saturday May 30, 2020 @05:28PM (#23364909)
In a toy raytracer I once wrote in C++, switching from malloc to custom memory pools for small fixed-size objects was a big performance boost. Making free() a noop was another big performance boost, both for deallocation and allocation. Turns out sequentially handing out memory from a big chunk of memory is much easier than keeping track of and reusing empty slots, and it keeps sequentially allocated objects in sequential memory locations.
Re:Rust: Dropping heavy things in another thread can make yo... by folmar on Saturday May 30, 2020 @06:28PM (#23365374)
jemalloc is the answer to the question you did not ask
Re:Rust: Dropping heavy things in another thread can make yo... by estebank on Saturday May 30, 2020 @02:57PM (#23363647)
In Rust you could just call `mem::forget` on whatever heavy thing that you're no longer using is before it would get dropped, but then the programmer is effectively responsible for that memory leak not becoming a problematic leak during refactors.

Edit: this will also break any code that relies on Drop being called for clean up, but that is already a "suspect"/incorrect pattern because there are no assurances that it will ever run.

Re:Rust: Dropping heavy things in another thread can make yo... by kd5bjo on Saturday May 30, 2020 @04:45PM (#23364545)
> There are no assurances that it will ever run.

Yes and no. Whenever control leaves a code block, Rust automatically calls the drop() method of all values still owned by that block. There is no guarantee that control will exit every block (cf. Turing), but a moderately exceptional circumstance needs to occur for this not to happen, like an infinite loop.

Re:Rust: Dropping heavy things in another thread can make yo... by rcxdude on Saturday May 30, 2020 @05:35PM (#23364974)
There's also no guarantee the object won't be moved elsewhere, including into a context which never gets freed (for example, you can construct mem::forget using entirely safe code by forming a cycle of reference-counted boxes). That said, Rust has support for such a concept through the Pin trait (which essentially guarantees an object will not get moved until it is 'Unpinned').
Re:Rust: Dropping heavy things in another thread can make yo... by mcguire on Saturday May 30, 2020 @03:34PM (#23363917)
Isn't that a problem for the RAII approach?
Re:Rust: Dropping heavy things in another thread can make yo... by projektfu on Saturday May 30, 2020 @03:15PM (#23363802)
Yeah, I like Apple’s (Next’s) approach of pool allocation for each run through the event loop. Defer dealloc, drop pool at the end.
Re:Rust: Dropping heavy things in another thread can make yo... by saurik on Saturday May 30, 2020 @03:29PM (#23363887)
Apple's pools are for helping manage reference counts of returned objects (via autorelease) but aren't doing pool allocation (as any of those objects could escape, so you can't do the fast thing of just deallocating the pool). The normal memory allocator is used while in the scope of an autorelease pool.
Re:Rust: Dropping heavy things in another thread can make yo... by projektfu on Saturday May 30, 2020 @06:21PM (#23365309)
True, I was thinking the same after I posted. Maybe I’m thinking more of Apache’s approach and how it gets some of the benefits of a fork-per-request server without the overhead.
Re:Rust: Dropping heavy things in another thread can make yo... by andrekandre on Saturday May 30, 2020 @09:27PM (#23366420)
right, so if you do

@autoreleasePool { [[[SomeObject alloc] init] autorelease]; }//someobject is release after exiting this scope

//inside main runloop, not inside @autorelease{} [[[AnotherObject alloc] init] autorelease];

//AnotherObject will be release at end of main runloop

[[NotAutoreleased alloc] init]; //will leak past runloop iteration

is that understanding correct?

Re:Rust: Dropping heavy things in another thread can make yo... by mcguire on Saturday May 30, 2020 @03:33PM (#23363912)
Unless your destructors do more than deallocation, in which case you will leak whatever other resource you're managing.
Re:Rust: Dropping heavy things in another thread can make yo... by MaxBarraclough on Saturday May 30, 2020 @04:19PM (#23364302)
A pool can invoke destructors when it is cleared. Might take a bit of overhead (if the pool is to support arbitrary classes), but you could retain the fast pointer-bump allocation.
Re:Rust: Dropping heavy things in another thread can make yo... by ShroudedNight on Saturday May 30, 2020 @07:59PM (#23365959)
Not Rust, C++, but in case anybody wants an example: https://github.com/eclipse/omr/commit/fd99ca42fdbed76cc00e68...

TR::Region is the slab allocator used by the JIT in OpenJ9/OMR. The linked commit adds functionality for calling destructors of arbitrary types allocated in the Region.

Re:Rust: Dropping heavy things in another thread can make yo... by MaxBarraclough on Sunday May 31, 2020 @02:18PM (#23371420)
Interesting, thanks. Seems to require use of a common base class though, i.e. to use a class with this pool, you have to inherit from Destructable, or else create a subclass that does. Seems ugly. Perhaps that's the only way it can be done in C++ though.
Re:Rust: Dropping heavy things in another thread can make yo... by projektfu on Monday June 01, 2020 @03:45PM (#23382873)
It does create an object with that base class for the allocator but returns an object of the type you want from the allocator, using a template (Region::Instance<T>). So the API user only sees the types it's interested in.
Re:Rust: Dropping heavy things in another thread can make yo... by CoolGuySteve on Saturday May 30, 2020 @02:53PM (#23363608)
Even just keeping a free list and deallocating it’s elements at an idle time is probably cheaper and faster than spawning a thread.
Re:Rust: Dropping heavy things in another thread can make yo... by jomohke on Saturday May 30, 2020 @06:10PM (#23365223)
You can easily use a custom global allocator in Rust:

    #[global_allocator]
    static GLOBAL: MyAllocator = MyAllocator;
Re:Rust: Dropping heavy things in another thread can make yo... by pjmlp on Saturday May 30, 2020 @02:35PM (#23363457)
In C++/WinRT the same approach is taken, because you cannot just use a memory pool for COM.
Re:Rust: Dropping heavy things in another thread can make yo... by indemnity on Saturday May 30, 2020 @03:45PM (#23363986)
Isn’t a Rust “move” implemented as a bit wise copy (e.g. memcpy call)? I see people claiming move has no cost but I’m not sure that is true.
Re:Rust: Dropping heavy things in another thread can make yo... by justinpombrio on Saturday May 30, 2020 @03:52PM (#23364061)
EDIT: See kevincox's reply. Rust will bitwise copy the containing type, which is typically very cheap. For example, it you move a String, it will copy the String struct, which contains a couple pointers and a length (or something along those lines). Importantly, it will not copy the underlying char array.

I was thinking of the following code, where I believe the assignment to y is actually free. Though apparently this isn't called a "move".

    let x = <<large owned type like [char; 1000]>>;
    let y = x;
More info: https://doc.rust-lang.org/rust-by-example/scope/move.html
Re:Rust: Dropping heavy things in another thread can make yo... by kevincox on Saturday May 30, 2020 @04:19PM (#23364304)
Semantically it is a bitwise copy.

In the example from the article it is probably actually a copy because the value was originally on the parent thread's stack, which will be reused after the function returns, so the value will need to be copied to the new thread's stack.

However it is important to not that it isn't a deep/recursive bitwise copy. It just needs to copy the HashMap itself (which is probably a handful of words).

So yes, it is doing a bitwise copy, but this is also very cheap. It will be much, much cheaper than spawning the thread.

Re:Rust: Dropping heavy things in another thread can make yo... by ahupp on Saturday May 30, 2020 @04:52PM (#23364609)
That's not exactly true. In C++ terms, the example code is moving a value:

  std::map<...> foo;
  someFunction(std::move(foo));
And not moving a pointer like:

  std::unique_ptr<std::map<...>> foo = ...;
  someFunction(std::move(foo));
So it copies sizeof(std::map<...>), not a pointer.
Re:Rust: Dropping heavy things in another thread can make yo... by pjmlp on Monday June 01, 2020 @03:23AM (#23376073)
Not necessary, std::map::map(&&) is free to move just the internal pointers and bookkeeping data, only dumb implementations would move the memory blocks where the map data is actually stored, as per ISO C++ requirements.
Re:Rust: Dropping heavy things in another thread can make yo... by ahupp on Monday June 01, 2020 @02:01PM (#23381603)
The comment originally said it was the cost of a pointer copy, and I was pointing out it's not a pointer. eg., sizeof(std::map<std::string,std::string>) is 48, which is the "pointers and bookeeping" you mention.
Re:Rust: Dropping heavy things in another thread can make yo... by pjmlp on Tuesday June 02, 2020 @12:52AM (#23387593)
Ah sorry for the misinterpretation. Should have payed more attention.
Re:Rust: Dropping heavy things in another thread can make yo... by dathinab on Saturday May 30, 2020 @04:14PM (#23364250)
What is bit-wise copied is the pointer to the memory.

I.e. a `HashMap` struct, or `Vec` struct don't directly contain the data.

For example the `Vec` is defined internally as something similar to:

`struct Vec<T> { data: *mut [T], capacity: usize, len: usize, marker: PhantomData<T> }`

(Slightly simplified, not actual Vec type).

So a move of a Vec copies at most 3 usize (24 bytes on 64bit systems), similar thinks apply for a HashMap.

Additionally the copy can often be elided through compiler optimizations.

As a interesting side note a new empty Vec/HashMap will not actually allocate any memory, only once elements get added it will start doing so. This is why it crates vec's of vecs of length 1. Or else it wouldn't need to do "number of element" free calls.

Re:Rust: Dropping heavy things in another thread can make yo... by tsimionescu on Sunday May 31, 2020 @06:45AM (#23368633)
So the Vec type is not storing data on the stack, like C++'s std::vector can?
Re:Rust: Dropping heavy things in another thread can make yo... by cesarb on Sunday May 31, 2020 @07:42AM (#23368917)
Exactly, neither Rust's Vec nor Rust's String (which is actually a Vec in disguise) store data within the struct itself, it's always stored in an external allocation. This is intentional, to avoid extra branches on whether the data is inline or not. If you have a lot of small vectors, and using less memory is more important for your use case than avoiding the extra branches, there are alternatives like https://crates.io/crates/smallvec that you can use.
Re:Rust: Dropping heavy things in another thread can make yo... by steveklabnik on Saturday May 30, 2020 @04:03PM (#23364161)
Semantically, it is a bit wise copy, yes.

However, these copies can often be elided by optimizations.

Re:Rust: Dropping heavy things in another thread can make yo... by ndesaulniers on Saturday May 30, 2020 @08:35PM (#23366146)
> As an aside, compilers have used the trick of not free-ing data structures before

In Clang, this flag is `-Xclang -disable-free`. Not from a Jedi...

Re:Rust: Dropping heavy things in another thread can make yo... by GuB-42 on Sunday May 31, 2020 @10:29PM (#23374995)
I don't know if Rust can do it (unsafe?) but in C and C++, I sometimes end up writing a custom allocator. It is often one of the most significant optimizations.

For example, I had the "million strings" problem once, literally millions. The solution was to put every string into a single large buffer and the pointers in another buffer. Not only I could deallocate everything at once but I also saved a bit of RAM by not aligning (not needed for strings).

Re:Rust: Dropping heavy things in another thread can make yo... by jfkebwjsbx on Sunday May 31, 2020 @01:34AM (#23367443)
> That very thing can easily happen in the real world.

Only if badly designed. That is why it is contrived!

> While that may seem contrived, consider a vector of 1 million strings, something that's not too uncommon

A program dealing with a million elements of any kind should not be performing naive allocations to begin with.

> we do deallocation trickery in the real world

Skipping deallocations is an optimization, not a design pattern.

In other words, the code needs to keep the ability to perform the deallocation for debugging, testing, usage as a library, etc.