HN Theater @HNTheaterMonth

The best talks and videos of Hacker News.

Hacker News Comments on
CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

CppCon · Youtube · 9 HN points · 21 HN comments
HN Theater has aggregated all Hacker News stories and comments that mention CppCon's video "CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”".
Youtube Summary
http://CppCon.org

Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/cppcon/cppcon2016

Lifetime safety means writing code that, by construction, is guaranteed to eliminate two things: (a) use of null/dangling pointers (including pointerlike things such as references, iterators, views, and ranges), and (b) leaks (including the rare 1% case where we’re tempted to admit the possibility of an ownership cycle or need to support lock-free concurrent data structures).

Last year, my CppCon 2015 talk “Writing Good C++14… By Default” focused on (a), null/dangling, because it's the more difficult and usually more serious problem. I gave an overview of a new approach of using static analysis rules to eliminate use of null and dangling in C++. That work continues and we’re in the process of writing down the formal rules for the approach that I showed last year.

This year, the focus will be on (b), leaks: The talk aims to begin with a set of simple rules, the “5-minute talk” to demonstrate that a handful of rules can be taught broadly to programmers of all levels, and results in code that is clean and free of leak bugs by construction.

But, since we’ll still have 85 minutes left, we can use the time to spelunk through a series of “Appendix” code examples, in which we'll demonstrate "why and how" to apply those rules to a series of increasingly complex/difficult situations, and that are aimed at increasingly advanced and “clever” (note: not always a good thing) programs and programmers. We’ll address questions such as: How should we represent Pimpl types? How should we represent trees – what should the child and parent pointer types be, and (when) should they be unique and when shared? How should we deal with “intra-module” or “encapsulated” cycles when you control all the objects in the cycle, such as all the nodes within a Graph? And what about “inter-module” or “compositional” cycles when you don’t know in advance about all the objects that could be in the cycle, such as when combining libraries written by different people in a way that may or may not respect proper layering (notoriously, using callbacks can violate layering)? The answers focus on cases where we have solid guidance, and then move toward some more experimental approaches for potentially addressing the ~1% of cases that aren’t yet well covered by unique_ptr, shared_ptr, and weak_ptr.

Herb Sutter
Software architect, Microsoft
Author, chair of the ISO C++ committee, software architect at Microsoft.

Videos Filmed & Edited by Bash Films: http://www.BashFilms.com

*-----*
The CppCon YouTube Channel Is Sponsored By:
SonarSource: https://www.sonarsource.com/
*-----*
HN Theater Rankings

Hacker News Stories and Comments

All the comments and stories posted to Hacker News that reference this video.
Jul 01, 2022 · 5 points, 0 comments · submitted by LordNibbler
Several attempts have been made already, the problem is overcomming the community's resistance to have one in the box, recently they have removed the APIs introduced in C++11.

It is also interesting how Herb Sutter avoiding making the comparision between deferred deallocation and GC algorithms on his talk until the last section.

"CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”"

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

Feb 19, 2022 · pjmlp on What Is Rust's Hole Purpose?
We have to agree to disagree then.

Having to create borrow checker friendly data structures doesn't bring anything to the table when writing GUI applications (including design tooling), distributed applications (Akka, Orleans, Erlang style, RDMS SP), CLI tooling, compiler tooling....

In what concerns languages with automatic memory management and support for value types, my list is somehow different, D, Nim, C#, F#, Swift, Go, Linear Haskell, Ocaml with Effects, Eiffel, Common Lisp.

And I leave Herb's talk regarding those issues with RC types,

"CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

https://youtu.be/JfmTagWcqoE

tialaramex
We certainly do disagree on this.

Even the first chunk of Herb's talk, before you get to reference counting - illustrates why you might feel more comfortable in another language compared to C++, as once again C++ has the wrong defaults. Rust's defaults are better.

Nullable shouldn't be the default. It's really hard to undo this bad decision, it's not one of the easy ones where I think C++ realistically could (but still won't) just fix it, but it's still a mistake. By getting this wrong C++ adds an unnecessary burden.

Since Rust doesn't get this wrong that burden isn't present. I am never worrying that the Box<Thing> in a Rust structure might not be there, by definition it's a Thing in a Box, if it might not be there that would be written Option<Box<Thing>> and the compiler would let me know if I ever forgot to account for the None case.

And it keeps happening. Later Herb wants an array of known-at-runtime size on the heap, so, he makes one with unique_ptr but of course by default C++ doesn't remember how big this is, so he needs to make sure he tracks it separately, more anxiety. Rust can Vec::into_boxed_slice() and the resulting slice automatically remembers how big it is, even though you've given up the vector's ability to grow or shrink. The machine representation ends up the same if you do it right in both languages, but C++ gives the programmer another thing to worry about.

taylodl
This would have been more relevant had Rust existed 30-35 years ago when C++ was ascending to supremacy. As it is, C++ is being pushed further and further into niche use cases and Rust is competing with C++ in those ever-decreasing niche use cases. That was my point in the first place - Rust seems like it's too little, too late. Add Go to the mix and there's even less of a reason to use Rust. It feels like Rust is obsolete before it even got started.
pjmlp
Except that still doesn't make Rust better than the alternatives with automatic memory management outside kernels, drivers and domains where Ada/Spark is being used nowadays.

Even Google isn't comfortable into pushing Rust into app developers for their OSes, despite the amount of adoption at kernel and driver.

So when Android comes out with Rust Compose, Fuchsia does Rustter, or Native Cloud folks migrate in mass to Rust, then I might eat my hat.

Naturally you might refer to Rust/WinRT, whose tooling is a joke versus C++/WinRT, let alone using .NET alongside Blend and VS.

Doable sure, then again there are people that also insist into using COM from C, regardless of how sane that might look like in practice.

So unless Rust reaches a state where ownership is transparent to app developers and distributed computing stacks, we will keep on disagreeing.

tialaramex
My assertion is that Rust gets enough of this other stuff right that unlike C++ it doesn't start out more painful than languages I've used with GC. I can only speak for my own experience writing software in these languages and so that doesn't allow for questions like whether Rust programmers are rare or expensive which might influence whether you make a .NET thing or a Rust thing.

When things get more complicated sure, Rust can mean you spend too much time figuring out how lifetimes should best work compared to a GC environment. But even a fancy GC doesn't promise to release unused resources promptly, so for non-memory resources you can end up needing to care about lifetimes anyway, and the GC languages do not prioritise this problem.

FWIW the phrase you probably wanted is "en masse" from the French meaning "as a body" not in mass.

pjmlp
You keep missing the point why I use automatic memory management when describing languages like D and Swift instead of GC.

Exactly to make the point that those languages offer the same deterministic tooling and not mix with how Java does it.

tialaramex
I haven't written any Swift, or D but as I understand it Swift just chooses to do reference counting everywhere. Maybe it at least factors out the places where this is a waste of everybody's time? I can't tell.

I don't see that as any kind of upside over Rust. It sounds like one of those "my first Rust project" codebases full of Arc<Thing> when Box<Thing>, Thing or sometimes even just &Thing would have been appropriate.

In fact this then makes me realise you consider "it has reference counted smart pointers" to be "automatic memory management" but then you say Rust is suited for situations where that isn't possible, does that mean you didn't know Rust has reference counted smart pointers (two kinds even)? Or does it mean you consider all of Rust above core to be superfluous (Rc is in alloc) ? Either way this makes very little sense.

And so I went back and now I'm also puzzling what you think Value Types are and why you believe that's relevant. Is the problem that you bumped your head and now you think Rust is Java ?

About the same, Herb Sutter has a talk how that can even trigger stack overflow if the destructors are not written with care.

"CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

https://youtu.be/JfmTagWcqoE

By the way, C++/WinRT uses background threads to handle cascade deletions of COM references exactly because of this.

Yeah, right.

CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

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

Better making use of the best practices advised by Herb, otherwise those destructors are going to surprise you.

ncmncm
It has been more than thirty years since a destructor surprised me. Herb was unlikely to have been programming at that time.
pjmlp
So what are your credentials in the C++ community to top those from Herb Sutter?
ncmncm
Been on ISO C++ Standard committee for much longer than Herb? But I have no disagreements with Herb.
pjmlp
And authored impactful C++ books and ISO C++ papers such as?
ncmncm
I have no need to "top" Herb. He says nothing inconsistent with anything I have said.
pjmlp
Apparently yes, when you place in doubt his advices,

> It has been more than thirty years since a destructor surprised me. Herb was unlikely to have been programming at that time.

So apparently you have skills that top one of the major C++ community figures.

Being so, the C++ community at large would appreciate those valuable insights.

ncmncm
> apparently you have skills that top...

Hallucinate, much?

ncmncm
The only conclusion one can draw is that you are wholly unable to comprehend what you have just read, or aggressively don't care what it says. Either way, enough is enough.
eropple
It'll surprise him anyway, eventually. After all--humans aren't perfect programmers.
Not really, only marketing makes RC seem better than tracing GC.

https://github.com/ixy-languages/ixy-languages

https://forums.swift.org/t/swift-performance/28776

It is no wonder that Swift 5.5 brings aggressive compiler optimisations that can even break code that naively relies on basic ARC behaviour.

RC also has pauses, pretty lengthy ones with cascade deletions, and can even lead to stack overflows.

"CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default""

https://youtu.be/JfmTagWcqoE

Which is why as GC algorithm reference counting is only for toy implementations.

Any reference counting implementation that cares about performance in multicore environments is almost indistinguishable from a tracing GC implementation.

In fact, C++/WinRT, uses background threads to diminish the performance impact of calling destructor and cascading deletions.

https://devblogs.microsoft.com/oldnewthing/20191018-00/?p=10...

habibur
Game developers tell a different story.

But you can find on net whatever you seek, whatever you are looking for.

> Which is why as GC algorithm reference counting is only for toy implementations.

That's what I heard from last decade. And then experience has outgrown likes or dislikes.

pjmlp
The game developers that use Unreal C++?

The fact that Swift and Objective-C get outperformed in any research about automatic memory management is quite real, not urban myths.

> just because you don't like pointers, does not mean the rest of us can't use them perfectly safely.

I actually use pointers quite safely. I just no longer see a need to ever return an allocation from `new` to a raw pointer unless I'm implementing my own pointer class.

Herb Sutter's correct [0]. `std::unique_ptr` or `std::shared_ptr` or some other pointer container should __always__ hold new-allocated objects. Just like you should __always__ wear your seatbelt. It's no danger to you or anyone else, it's slightly inconvenient, and it saves a metric ton of headaches about "what if?". Because in reality a pointer container explicitly marks the scope of the allocation and if you're not wanting to use C++'s scoping rules then why are you using C++?

[0] https://www.youtube.com/watch?v=JfmTagWcqoE

On single core without threading.

If you add multi-threading and multi-core, there is lock contention for every single memory access to update the related counter.

Additionally, if there is a big data structure in use, reaching zero will trigger a cascade deletion events reaching stop-the-world like effect.

Which, depending on how the deletion was implemented, might trigger a stack overflow in very deep structures.

Herb Sutter has a very interesting talk about the bad issues of reference counting and how to work around them.

CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

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

There are multiple ways to optimize reference counting, like deferred counting, cycle collectors and a couple of others, which when you are done with all them, ends up being quite similar to having a tracing GC anyway.

cesarb
> Additionally, if there is a big data structure in use, reaching zero will trigger a cascade deletion events reaching stop-the-world like effect.

Even a deletion cascade won't stop the world: only the single thread which reached zero will be "stopped". While in tracing GC, a "stop-the-world" stops all threads, even the ones which aren't doing any allocation or deallocation at the moment.

pjmlp
High performance tracing GC algorithms don't stop all threads.

Also there are tracing GC that operate with local heaps.

zozbot234
There are concurrent GC's that don't pause all threads while running garbage collection. Plus the whole point of GC is that it's what you use when you just don't know at runtime which operations should involve "allocation or deallocation" of objects. From the POV of tracing GC, any "new" reference to an object might be the reference that extends its lifetime and prevents it being collected, and conversely when removing a reference.
imtringued
Those GCs are special exceptions. Most modern GCs just aim to have bearably low pause times.
zozbot234
> If you add multi-threading and multi-core, there is lock contention for every single memory access to update the related counter.

You only have to update the counter when creating (or destroying) new "owning" references that might outlive any other reference to the object. This is a rare operation, and doing it potentially from multiple threads in a way that requires atomic update is even rarer.

> Additionally, if there is a big data structure in use, reaching zero will trigger a cascade deletion events reaching stop-the-world like effect.

This is a side effect of deterministic disposal, of course. And it only happens when freeing up large object subgraphs, not just any and all "large data structures".

Yes, C++ implements reference counting in a non-ideal way. Swift is also quite bad. But one can do better.

pjmlp
When one does better it usually ends up with some form of tracing GC implementation, as per most CS research papers, it just gets to call it reference counting with optimizations to feel better.
zozbot234
That's definitely true of some enhanced variations on reference counting. The changes I mentioned though do not involve any tracing of live data at runtime. (Although once one introduces the possibility of general cycles, it seems hard to avoid the requirement of tracing some live data.)
Apr 15, 2019 · pjmlp on Swift Generics Evolution
Yet all the languages with a tracing GC wipe the floor of Swift's reference counting implementation.

"35C3 - Safe and Secure Drivers in High-Level Languages"

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

https://github.com/ixy-languages/ixy-languages

The subject of Herb's talk was actually going through all the reference counting problems to introduce in the end a lightweight implementation of deferred pointers, which isn't nothing more than basic tracing GC.

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

dep_b
Well if you think a driver is somewhat similar to a front-end implementation to begin with then it's OK. If "wiping the floor" means what I see on a day by day basis comparing JVM based UI's versus the same thing built in Swift you're probably talking about something different to begin with.

I'm talking about predictable performance and guaranteed minimum performance. I don't think you're speaking about the same thing.

The video seems to be interesting anyway, so thanks for that.

pjmlp
Actually it is, that network packet is not going to wait.

"Wiping the floor" means getting last place against all tracing GC languages used to research the mentioned paper.

Yes, many JVM based UIs do suck, mostly because the authors didn't bother to learn how to use Swing properly by reading books like Filthy Rich Clients.

The thing with Swift UIs, is that they are actually C and C++ UIs, given that those are the languages used to implement Core Graphics, Core Animation and Metal. Additionally Cocoa is still mostly Objective-C, with performance critical sections making use of NSAutoreleasePool like in the old good NeXTSTEP days.

Coming back to Java, factory automation and high integrity systems are perfectly fine with it.

https://www.aicas.com/cms/

https://www.ptc.com/en/products/developer-tools/perc

dep_b
Well try to use C++ without the C part, see how that goes. C is a part of Objective-C and you can write Swift code that will compile byte for byte into code as performant as C. It's ugly, it's unsafe but it's part of the language and if you need it it's there.

You sound like those people that complain that Objective-C is so slow compared to C because NSArray is much slower than it's C counterpart. Objective-C is always exactly as fast as C since you can always write C in Objective-C.

And if all Java based UI's suck despite the fact that it's the most popular programming language and despite the fact that it powers the most popular operating system you might start suspecting there's something wrong with it, but nope, you've read a paper and saw a video. About a kernel driver. OK.

pjmlp
The funny part is that the languages with tracing GC that wipe Swift's memory management on the paper aren't even Java, rather OCaml, Go and C#.

Your focus on Java, without spending one second reading the paper benchmarks, from a well respected researcher in the CCC community, just demonstrates a typical defensive reaction among reference counting proponents.

Using C++ without the C part is the whole point of modern C++ and the Core Guidelines. In fact there are several benchmarks where the C++ version gets better optimized.

As for Android, it isn't Java as we know it, and any travel through /r/androiddev will teach Google might have tons of PhDs, but they certainly don't work on Android, given how a lot of things are implemented.

dep_b
I'm kind of baffled you never got back to the first statement I made. It's like you got totally lost in your mission to prove that Swift sucks. I never claimed magic performance in Swift, I said ARC might be slower but it's a lot more predictable with a better minimum performance.

The only thing I see is "here's a ton of text and video to slough through made by people smarter than me", nothing concrete that undermines my original statement in any way.

And yes C++ cannot exist without C as it is an extension to C. You can write pure C in a C++ file and it will still work. You can write pure C in an Objective-C file and it will still work. You can write some ugly bastard syntax version of C in Swift and it will still work.

I'm very much willing to accept Swift's performance for a device driver (possibly the first device driver ever written in Swift) is worse than in many other languages if you stick to the safe bits of Swift. It just doesn't have much to do with what I wrote.

Mar 20, 2019 · pjmlp on Java 12
Very predicatable indeed.

Go and C# running circles around Swift.

"35C3 - Safe and Secure Drivers in High-Level Languages"

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

Possible stack overflows and unexpected pauses due to heavily nested data structures.

"CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default."

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

Herb Sutter has a nice talk about how RAII can cause stack overflows or stop-the-world pauses if used badly.

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

1. I think you may not realize what state of the art tracing GCs can accomplish. IBM's Metronome has pause times down to hundreds of microseconds.

2. It only takes freeing a tree with a few thousand nodes for it to become an issue. It happens in C++, too (heck, there've been cases where chained destructor calls overflowed the stack [1]). The reason why you don't hear more about it is because pause times just aren't that big a deal for most applications. In forum debates, people always discuss triple A video games and OS kernels and such, but in practice, only a minority of programmers actually have to deal with something even approaching hard real time requirements. Generally, most applications optimize more for throughput rather than pause times.

3. Yes, and it can be a problem for C/C++, too. It's rare, but not non-existent. Note that pools can actually make fragmentation worse for long-running processes.

4. Weak references work if you get them right. But for long-running processes, even a single error can accumulate over time.

> On the GC side, it seems like you typically get bursty, unpredictable performance, in both time and memory. Modern GCs work very hard to keep collection pauses as short as possible, but almost inevitably that means keeping garbage around for longer, which means using a lot of memory.

This ... is not at all how garbage collectors work, especially where real time is concerned. Not even remotely. I recommend "The Garbage Collection Handbook" (the 2011 edition) for a better overview. And ultra-low pause times are generally more of an opt-in feature, because they're rarely needed.

[1] E.g. Herb Sutter's talk at C++Con 2016: https://www.youtube.com/watch?v=JfmTagWcqoE&t=16m23s

throwaway91111
One note, in video games allocations are a major source of slowdown; don't allocate in your inner loop! Use object pools and arena allocators.
rbehrends
This is because naive can-do-it-all allocations in C/C++ can be expensive, not because allocations are inherently expensive. In C/C++, you have:

1. A call of a library function that typically cannot be inline.

2. Analysis of the object size in order to pick the right pool or a more general allocator to allocate from.

3. A traditional malloc() implementation needs to also use a global lock; thread-local allocators are comparatively rare.

4. For large objects, a complex first-fit/best-fit algorithm with potentially high complexity has to be used.

Modern GCs typically use a bump allocator, which is an arena allocator in all but name. In OCaml or on the JVM, an allocation is a pointer increment and comparison.

Even without bump allocators, it's easy for a GC implementation to automatically turn most allocations into pool allocations that can be inlined.

Also: much as people love to talk about video games, video games with such strict performance requirements are not only just a part of the video game industry, they are a tiny part of the software industry.

iainmerrick
In OCaml or on the JVM, an allocation is a pointer increment and comparison.

That's true, but if (hopefully rarely) the object turns out to be needed later, it has to be copied to another heap, and that takes time and memory. Pointers need to be redirected and that takes a little work too.

Bump allocators are definitely a huge win, as good as anything you can do in C/C++ and much more convenient for the programmer, but they're not a completely free lunch.

iainmerrick
> > almost inevitably that means keeping garbage around for longer, which means using a lot of memory.

> This ... is not at all how garbage collectors work, especially where real time is concerned.

Hmm, I'm certainly no GC expert, but is it really not the case that GC tends to be memory-hungry? Not exotic academic systems, but the languages people use day-to-day.

Most of my experience with GCs is in languages like Java and C#. Java in particular can be very fast but always seems to be memory-hungry, using like 4x the memory you'd need in C++. I haven't spent a huge amount of time fine-tuning the GC settings (it seems like Oracle is working to simplify that -- good!) but the defaults seem to assume at least 2x memory usage as elbow room for the GC.

That's on the server. On mobile, I've worked with iOS and Android and iOS undeniably gets the same work done with much less memory. Flagship Android phone have 4GB of memory and need it, whereas Apple hasn't felt the need to bump up memory so quickly even after going 64-bit across the board.

The last I heard about real-time GC, with guaranteed space and time bounds, it sounded like it was theoretically solved, but not used much in practice because it was too slow. That was a number of years ago though. Has that situation changed? Are there prominent languages or systems with real-time GC?

iainmerrick
Looking up IBM's Metronome led me to the Jikes RVM (https://en.wikipedia.org/wiki/Jikes_RVM), which sounds so cool that I wonder why it isn't being used everywhere?

The PowerPC (or ppc) and IA-32 (or Intel x86, 32-bit) instruction set architectures are supported by Jikes RVM.

Ah, no ARM and no x64, that'd be it.

What's keeping this kind of GC technology back from the mainstream?

rbehrends
> Jikes RVM

The Jikes RVM is designed for research, not production. It's pretty impressive, but (inter alia) does not implement all of Java and does not support as many platforms.

> What's keeping this kind of GC technology back from the mainstream?

The fact that successful commercialization is possible; the GC tech that you see in Metronome and C4 is seriously non-trivial and not easy to reproduce unless you spend money on it; and it's also technology that businesses are willing to pay for.

At the same time, only a minority of open source use cases really require this kind of hard real-time GC, so there's little pressure to create an open source equivalent. Shenandoah is the one open source GC that does try to compete in this space, and it is trading away some performance for getting ultra-low pause times.

I'll add that this is difficult only because of concurrency and arbitrary sharing of data between threads. If you have one heap per thread, then it becomes much, much easier (and is a solved problem if soft real-time is all you need).

Reference counting isn't deterministic in the presence of large complex data structures.

You will know when memory gets deleted, but not how long it will take, nor how much stack space the destructors will require to run.

Enjoy Herb Sutter's "Leak-Freedom in C++... By Default." at CppCon 2016, where he explains how those issues affect C++ and goes on to implement a tracing GC.

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

I highly recommend watching this presentation: https://www.youtube.com/watch?v=JfmTagWcqoE

Herb Sutter: Leak-Freedom in C++ by default

Feb 08, 2017 · realharo on React Native at Instagram
>With modern C++ language features, the "complicated language with the manual memory management" a lot less obvious or necessary

Not completely though, you still have to think a lot more about ownership and object lifetimes (https://www.youtube.com/watch?v=JfmTagWcqoE), which is a lot better than manual new/delete, but still not as simple as just having a GC.

Plus you have to deal with lifetimes of objects proxied to Java code, where something in the Java code becomes the object's owner. Though you can use a bindings generator like djinni (https://github.com/dropbox/djinni) to handle this for you (with some tradeoffs).

>Debugging is only complicated on Android

It's gotten slightly better recently, but it still sucks.

With Swift it's not trivial either, it's not that easy to set breakpoints into C++ code, stack traces on crashes are often not helpful (so any unhandled C++ exception that you didn't catch and convert into a Swift error is hard to track down, etc.)

Feb 07, 2017 · beached_whale on In Defense of C++
Herb Sutter presented one in a talk a little while ago. https://github.com/hsutter/gcpp https://www.youtube.com/watch?v=JfmTagWcqoE

So it's totally doable in library without cost to those that do not need it.

justin66
That looks like a great talk, as all of Sutter's talks are. Thanks.
I was mainly talking about the C++ Guideline Support Library and GSL profile "checkers" that have been in process for the last couple of years.

https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-core-gu...

https://github.com/Microsoft/GSL

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

I recently watched Herb Sutter's talk "Leak-Freedom in C++", described in the article [1], and I can't help but notice that C++ community has a strong bias against garbage collection. ... And then he describes the very problem you can't solve without GC: memory management for graphs with cycles. Solving this problem is equivalent to implementing GC. Of course, having your own specialized GC may help, but you may also benefit from your own memory allocation.

Why can't you acknowledge that there are problems that have GC as the only and best solution?

(Note: it's possible to mix memory management approaches, e.g only pointers to graph nodes are being garbage collected and everything else has a nice hierarchical ownership.)

[1] https://www.youtube.com/watch?v=JfmTagWcqoE

CJefferson
My usual argument is as follows:

There are indeed cases where you have to use GC on some part of your program (as you say, some graph with cycles). However, such things are still only a small part of a program, and most of your memory allocations can still be handled without a GC.

No program language I am aware of (and I don't claim to know all) are good at saying "GC this 5%, I'll explicitly handle everything else". In C++ the "GC" bit is painful, in most other languages the "I'll do everything else" bit is painful.

Of course, there is an argument that just GCing everything is as efficient as bothering with manual memory management, so why bother at all? I'm still to be convinced about that, particularly because GC systems tend to want to use quite a bit more memory, so they have room to breathe.

pjmlp
Where to place a record (struct) in Modula-3:

    VAR
      stackOrGlobalMem: RECORD ... END;

      heapGC : REF RECORD ... END;

      somewhereManual : UNTRACED REF TO RECORD ... END;

      rawMem : ADDRESS;

Also valid for any other data type in the language.

So one can take advantage of GC productivity, while making use of C++ like features for manual allocation when required.

I remember reading a paper with a generics module for using smart references as well.

There are other not so well know GC enabled system programming languages with similar feature sets.

EDIT: like was missing

ingenter
> No program language I am aware of (and I don't claim to know all) are good at saying "GC this 5%, I'll explicitly handle everything else"

I haven't used rust in production, but I think it allows you to do precisely this, using the following crate: https://github.com/Manishearth/rust-gc.

> Of course, there is an argument that just GCing everything is as efficient as bothering with manual memory management

I specifically said that it should be possible to mix GC with other types of memory management.

The problem is that I often hear from C++ crowd "GC is bad, mmkay!", without mentioning that there are specific cases where you have to use/implement GC.

None
None
haberman
> Why can't you acknowledge that there are problems that have GC as the only and best solution?

GC is traditionally bundled into a language runtime in a way that imposes global costs, and cannot be opted out of. The GC interrupts your program in a way you have no direct control over and scans the global heap of all objects you have ever created.

C++'s philosophy is zero-cost, opt-in abstractions. So naturally anything that you can't opt out of is going to rub C++ programmers the wrong way.

Implementing GC within the language, in a way that is entirely opt-in, is fine. Any muttering under the breath when discussing this is, IMO, just acknowledging that most of our experiences are of "bad" GC, to the point that we almost don't want to use the same word when referring to "good" GC.

titzer
> C++'s philosophy is zero-cost, opt-in abstractions.

It's a nice philosophy, but unfortunately C++ itself often fails to deliver unless the programmer is an absolute expert on the underlying semantics. E.g. forget to pass a vector by reference or as a pointer, and you get a complete O(n) copy underneath. With other data structures, one can implement efficient copy constructors to make this pretty cheap. When an abstraction leaks details of the underlying implementation that then lead to huge non-obvious costs, that is not an abstraction.

Another example is that C++ heap allocation relies on an underlying allocator, which has overhead in managed chunks of memory, size classes, etc. The underlying allocator's alloc() and free() operations are not constant time. In fact, they are almost always outperformed by the allocation performance of GC'd languages, where bump-pointer allocation and generational collection make these overheads very, very cheap.

nanolith
Every data structure in C++ has an allocator override. If you want to use a bump pointer allocator, you can use a bump pointer allocator. In fact, this is a very common optimization in game software during time-critical frame rendering. Allocate a large chunk of memory to act as a flywheel, then use a bump allocator against this chunk of memory while performing data-heavy crunching, then reset the bump pointer at the end of the render cycle. All memory is freed at once, without the use of a more intrusive garbage collector.

As they say, C++ gives you enough rope to hang yourself. It's pretty unapologetic about not being a language meant for everyone. But, sometimes, one needs to drop down to a lower level language to boost performance. I like to apply the Pareto Principle: 80% in a higher level language, and 20% in a language like C or C++.

titzer
Sure, we use our own arena allocators with varying degrees of success in my current project (V8--JavaScript VM implemented in >800KLOC C++). The fact that C++ allows you to peer behind the curtain is more its own admission that it is inadequate to meet all use cases. It always allows one to resort to "manual" control. Usually that manual control comes at the cost of literally violating the language semantics and wandering into the territory of undefined behavior. As tempting as this manual control is, my 15 years of C++ versus my 10 years of Java and JVM implementation makes me feel like C++ causes far more trouble than it's worth, especially for projects that have no business at all trying to outdo the defaults.
jjnoakes
> Usually that manual control comes at the cost of literally violating the language semantics and wandering into the territory of undefined behavior.

What makes you say this?

nanolith
I think that's mostly your opinion. One can remain very tight to the language semantics and still get a lot of fine-grained control over performance in C++.

No language can meet all use cases. Every language has features that it is best suited for, and features that are a bit of a pain. For languages like C and C++, the pain points are higher-level programming semantics. For languages like Java / JVM, the pain points are fine-grained control and low-level bit twiddling.

I recommend an 80/20 split. Write 80% of your code in a high-level language of choice that mostly meets your goals. Profile the hell out of it, then use that language's FFI to refactor the slow bits in a lower level language like C/C++.

There will be constraints, such as system level programming or embedded system programming, where a language like C/C++/Rust can't be avoided. But for general purpose work, the Pareto Principle works pretty well for balancing time-to-market with performance.

chubot
Along the same lines, the thing that is annoying me now is return value optimization. It's apparently required by the standard in specific circumstances, but there's no (obvious) syntactic indication of whether you are using it.

So I'm sticking with out params for now.

daemin
You can implement allocators which are very fast, much faster than generic malloc and free. Then people that do care about this level of performance can write their own ones, optimised specifically for the platform they are using and problems they encounter.
titzer
It'd be nice if people actually replied with arguments instead of downvoting.
haberman
C++ is an expert's language, no doubt about it. But wielded properly, it does deliver on its promise pretty well, especially with recent language evolution.

C++ lets you use a bump allocator if you want. malloc()/free() are just functions that you don't pay for unless you call them. That is the point! And as others have pointed out, many common data structures let you swap out the calls to malloc()/free() for something else if you want to.

wyldfire
> C++ community has a strong bias against garbage collection

Well, it's a bit of a selection bias on your part IMO. The people who use C++ have seen the benefits of Java, python, C# and all the rest. And many of them(us) use Java, python, C# and stuff for meta-productivity build tools and the like. GC is great for some enormous set of problem domains. But for problems that cannot endure high or unstable latencies, GC is a bad solution. I'm writing C++ because it hits a sweet spot in the intersection of (low/deterministic latency + ubiquity among developers).

> Why can't you acknowledge that there are problems that have GC as the only and best solution?

There are, but I probably wouldn't have used C++ for them. In some rare cases I would but only because of momentum on an existing legacy solution.

kabdib
I write C++. I write a lot of it, I've been using it since 1987.

I love LISP and Smalltalk, but I'll never ship a product in those languages. I've written a ton of C# and Java and somewhat less Javascript; those environments are fine (though I found myself paying much closer attention to object lifetime than I wanted).

I'm dead set against garbage collecting C++ because the semantics just don't fit well with the available runtimes. A few times I've written garbage collected heaps for specific types of objects; crossing the boundary between the hand-allocation world of C++ and the dynamic world of a GC environment is not a happy or particularly efficient experience, especially in the presence of threads.

nadam
You choose C++ to be able to control performance more than in lots of other languages. In a game engine or other very performance intensive software, people tend to think a lot about memory management. Whether in very rare cases, for a part of a system the optimal solution is something similar to a garbage collector: this might be possible, but quite irrelevant, because the important thing is not the final solution, but the fact that you can (sometimes quite creatively) control and constantly fine tune things, so that you can come up with an optimal solution.
pjmlp
Having been part of the community, but with experience in many other languages another common trait seem to be ignoring that many other languages also have features for writing cache friendly code just like C++, while being safer.

A good example is Modula-3, a systems programing language that while it does have GC by default, also allows for global and stack allocation, value types, bit and structure packing and if really necessary naked pointers (only allowed in unsafe modules).

Or Mesa/Cedar at Xerox PARC with its mix of RC/GC, which is basically the same as C++ _ptr<>() + deferred_ptr<>() just implemented at the language level.

Same applies to RAII, yes it was a very good invention in C++, but many other languages do offer good ways of doing resource management, for example bracket in Haskell or withResource in others.

vmarsy
I think the reason the C++ community has a strong bias against garbage collection as you say is because it has a bias against all sort of unpredictable/unwanted behavior. That's the essence of c++: pay only for what you need/want/use. Garbage collection wouldn't be appropriate in some situations.

So re-implementing your own GC algorithm in the special case where you need it is seen as acceptable. That deffered pointer could be part of the standard library as long as those who don't need to use it don't pay any performance overhead

winter_blue
I don't know if I would acknowledge that there are problems that have GC as the only and best solution. Perhaps there are certain specific memory management problems for which a GC is the best solution. But what if you avoid that entire class of problems by adopting certain programming paradigms, or by the design of your programming language? For instance, Rust ensures memory safety without a GC. And while I write C++, I rarely find myself directly call new -- most of my objects are allocated by STL containers and via stack building.
saucetenuto
Right, but then you also shouldn't be using the techniques described in the article, right? To put it another way, there's no case where deferred collection with smart pointers is better than real GC.
johncolanduoni
Rust is horrifically bad at dealing with non-trivial cyclical structures for a number of reasons, not having a GC being one of them.
wspeirs
Beyond "not having GC", what are those reasons? I'm new to Rust and curious...
johncolanduoni
Inherited mutability and no aliasing mean cyclical structures tend to be a fairly complicated mess of RefCell/Cells. The borrow checker is very good at handling ordered lifetimes but cannot handle more complicated access patterns without the runtime checks in RefCells.

There are graph libraries, but these only help with structures that are graphs in the strict sense (all edges are created equal and not actually part of the structure) as opposed to structs with one or more fields that reference (and have shared ownership of) another node in the graph.

steveklabnik
See http://smallcultfollowing.com/babysteps/blog/2015/04/06/mode... and its linked post; they cover some of the ways you can write general graphs in Rust. It's a bit differently than you'd do it elsewhere.

Of course, if you're willing to give up some safety internally, you can also implement them the same was as you could in C or C++.

dmitrygr
Because GC wastes cycles on the user's machines instead of just hiring a competent developer who can manage memory properly. As we move to a more mobile world, wasted cycles matter more. So no. While GC is the definite answer to "how do I allow my dog to make a 'press this button for fart sound' app?" Is it most certainly not the definitive answer (though perhaps a passable one) to "how do I write good fast stable software?"
ingenter
You haven't answered my question. Please re-read it carefully.
dmitrygr
I did. You wanted acknowledgement that there are problems for which GC is best solution in C++. I explained why it is never best and likely never anything beyond "barely acceptable".
ingenter
Do you understand that the linked article, and the talk referenced in the article describe a way to implement garbage collection as a solution to (memory management for) data structures with cycles?

Do you think that this problem is not equivalent to "how do I allow my dog to make a 'press this button for fart sound' app?"?

Do you have a better solution to reclaiming data structures with cyclic pointers?

palunon
Pool allocation, and delete the whole thing at the end.

Of course, sometimes (most times ?) you can't do this, and you really need a GC.

There is a reason why TAOCP spends a whole chapter on GC IIRC...

johncolanduoni
How does a "good programmer" properly manage cyclical graphs without very specific, static guarantees about how they're used (e.g. All cycles are parent/child)? Because I'd love to know a way to do this without using/implementing the equivalent of a GC.
adrianratnapala
In most of the graph problems I have ever dealt with, the graph is constructed piecemeal but destroyed as a whole after you are finished with it.

It is true there are specific problems (e.g git repos) where you want a mutable graph over the long term -- but I have never actually had to deal with those cases, unless it was a special case with a obvious solution (like a doubly-linked list or tree).

nanolith
Even in the case of a mutable graph, one can use copy-on-write semantics to create a DAG of graph revisions that can be managed or compacted. I believe this was the strategy used by Subversion.

Compaction and collection can both be trivially performed on such a structure by making a deep copy of the latest revision of the graph to a new memory arena, just as is commonly done with a Cheney collector. Old locations are back-patched with pointers to the new locations, which solves the cycle problem.

gpderetta
hey, that's a generational GC :)
nanolith
Well, it's a Cheney GC. To be generational, there would need to be more than two heaps. But, that's hair splitting. Heh.

I doubt one would find many C++ folks who would disagree that GC is useful some of the time. It's all about controlling when and where GC is used, which sort of hits home with Sutter's point.

toast0
What are people using cyclical graphs for? Most of my daily work is in a sad language which has GC and structurally prohibits cyclical references[1], so what am I missing out on?

[1] unless you cheat...

gpderetta
In addition to what adrianratnapala said, it is perfectly fine to have specialized GCs for specific problems, and I suspect that any moderately complex C++ program in fact does (and no, that's not Greenspuring). The problem is using GC as the general memory management solution when 95% of the allocated objects have a single reference or a simple lifetime patter.

A not well known fact is that even the linux kernel as a GC implementation, used to collect file descriptors, but any suggestion to GCing all memory in the linux kernel wouldn't be well received.

johncolanduoni
I completely agree, but the OP has been pretty clear in this thread that a GC is never the best solution and I'd like to know his alternative.
mannykannot
I am not doubting that you are right, but this argument does not quite amount to a proof. That is because you would have to show that there are cases where the lifetimes of the graph's elements cannot be determined from consideration of the semantics of the problem.
jasode
>I can't help but notice that C++ community has a strong bias against garbage collection. [...]

>Why can't you acknowledge that there are problems that have GC as the only and best solution?

Your prelude and the followup question is not well-formed.

C++ programmers do not have a bias against GC as a specific problem-solving technique. In fact, expert C++ programmers can embrace GC so much that they can write an entire virtual machine[1] with GC and a DSL[2] for that vm that takes advantage of memory safety. Both the CLR vm and the (original) C# compiler were written by C++ programmers.

What the C++ community doesn't want is GC in the C++ base language itself or the standard runtime. That's a very different concept from a generalized "C++ bias against GC".

In other words, the following is unacceptable:

  std::string x = "Hello, " + fullname; // cpu cycles spent on GC
Those cpu cycles spent on constantly checking if "x" is no longer reachable is cpu power that's taken away from rendering frames of a 60fps game, or computing numeric equations or high speed quantitative trading. C++ programmers don't want GC as a global runtime that you can't opt out of. Also, global GC often requires 2x-3x the memory footprint of working memory which is extremely wasteful for the resource constrained domains that C++ is often used in.

Herb Sutter's presentation is compatible with "pseudo-GC-when-you-need-it" without adding GC to the entire C++ standard runtime.

[1]https://en.wikipedia.org/wiki/Common_Language_Runtime

[2]https://en.wikipedia.org/wiki/C_Sharp_(programming_language)

adrianratnapala
There is an advantage to universal GC: it can simplify interfaces.

For example, in C every time a `const char *` appears in an API there has to be an implicit contract about who owns the string and how to free it. A language like Rust improves on this by enforcing such contracts, but the contracts still complicate every interface.

In a GC'd language you can just forget about these issues and just treat strings (and other immutable objects) almost as if they were ordinary fixed-sized values.

steveklabnik
Nobody says GC has no advantages, the claim is that its disadvantages don't always offset them.
hackits
The way C/C++ get around the implicit ownership of const char * is everyone copies data around.
jdright
And C++ is all about choice trade-offs.

Put very simply: do your choice for your problem and deal with its consequences.

pkolaczk
"Also, global GC often requires 2x-3x the memory footprint of working memory which is extremely wasteful for the resource constrained domains that C++ is often used in."

Memory fragmentation in manual memory management often comes at a similar or higher cost, which is often "forgotten" and neglected, because it is hidden:

https://bugzilla.mozilla.org/show_bug.cgi?id=666058#c31

And fragmentation may grow over time much more than the typical GC overhead. Then you have to do a "forced GC" that is restarting the application. Often happens to my phone or browser. I'm not sure if it is more due to fragmentation or more due too plain old memory leaks.

ingenter
> Your prelude and the followup question is not well-formed.

Thank you for your feedback, I appreciate it.

> C++ programmers do not have a bias against GC as a specific problem-solving technique. Expert C++ programmers can embrace GC

Please watch this portion of the video, and the way the speaker is pronouncing the word "collect":

https://www.youtube.com/watch?v=JfmTagWcqoE#t=1h5m25s

Regarding general-purpose GC:

I don't support using GC for everything. If there is a problem that can only be solved with GC, it doesn't mean that we should express our entire running programs in terms of dynamic cyclic graphs. I believe we can do much better, with much fewer resources.

jasode
>Please watch this portion of the video, and the way the speaker is pronouncing the word "collect":

Herb was deliberately using circumlocution to avoid the phrase "garbage collection" so as to not poison the well. The latter part of the video explains his reasoning for presenting it that way:

https://www.youtube.com/watch?v=JfmTagWcqoE&t=1h32m55s

>I don't support using GC for everything. [...] I believe we can do much better, with much fewer resources.

This is a statement that sounds reasonable and balanced . How can anyone possibly disagree with it?!? The issue is that it's very difficult to take that universal wisdom and construct a real programming language that satisfies all the following properties seamlessly and elegantly:

1) language that has optional GC

2) has zero cost when not using it. This means no active tracing loop that checks object graphs and no large memory blocks reserved to allow reallocation and defragmention.

3) transparent syntax that's equally elegant for manual memory or GC in the same language

To make your GC-when-appropriate advice real, one has to show a real implementation. E.g. one can fork the GCC or LLVM compilers and add GC to it. Or fork the Java OpenJDK compiler and show how syntax can remove the GC on demand. Or construct a new language from scratch. There may be some lessons learned from the D Language backing away from GC. Also, Apple Objective-C retreated from GC as well.

gpderetta
Rust would be a perfect GC-optional language as, having per-thread heaps, GC using threads can be completely segregated from the non-using ones.
DougBTX
Microsoft's Managed C++ is frankly scary, but its gc root type seems pretty close to this.
DannyBee
Right, but they pay a different cost there that isn't listed: "It's not entirely compatible with the existing language"

private and multiple inheritance do not work, for example (yes, i know some of this is fixable, but some is not)

  public __gc class one { int i; };
  public __gc class two: private one { int h; i = h; }; //error

  __gc class a {};
  __gc class b {};
  __gc class c: public a, public b {}; //will produce an error
etc
piaste
Which one (or more) of those three properties does the D language fail to satisfy?
ingenter
> To make your GC-when-appropriate advice real, one has to show a real implmentation.

I haven't tested it closely, but I believe that Rust with this crate: https://github.com/Manishearth/rust-gc is an implementation for "GC only when you need it".

T-zex
Another aspect against GC is system determinism. Unmanaged runtime is more deterministic and is often faivored in real time applications.
wbl
There are real-time GCs that depend on using uniform blocks of memory. All real-time allocators are going to involve similar pain.
gpderetta
They are not free, though, as they trade throughput for a guaranteed latency upper bound.

edit: clarify

pjmlp
Only if malloc()/free() don't need to call OS APIs, if it happens good luck with being deterministic.
nanolith
One does not need to ever use malloc()/free() or new/delete in C++. For instance, it is very common to use just the DATA and BSS segments in embedded code, and rely on placement-new, which is always deterministic.

Dynamic memory allocation is also a choice in C++.

pjmlp
Going a bit off topic, that is something you can also do in Ada or SPARK, while having a more type safe language.

My problem when I used to code in C++ was working with others in corporate projects.

It didn't matter how I strived to write modern code, making use of best practices to write safe code, there were always team members that never moved beyond C with Classes and nevermind having some processes in place to avoid precisely that.

Even nowadays, I get the feeling most developers don't make any use of static analysis.

nanolith
Sadly, I don't think that's a problem that a language alone can solve. Ada, for instance, is awesome with its runtime enforcement of design contracts, but all a programmer needs to do is change the contract and chaos ensues.

I'm a huge fan of strong typing. I'm also actively trying to find ways to improve static analysis and formal methods. But, if you can't trust your developers, it all eventually breaks down.

I find that for most mature projects, a good developer needs 3-6 months of ramp-up time, which should include knowledge transfer, training, and restricted commit access. The point of this isn't to haze the developer, but instead to give him/her a chance to fully grok the intent of the mechanisms in the code base and to (hopefully) present and socialize better options in a controlled way. More and more, I've come to the conclusion that a strong team mechanic is one of the mandatory components of good software. DBC, code reviews, unit testing, formal analysis, and static analysis all help to reinforce this mechanism, but if the tribal knowledge of the team breaks down, then so ultimately will the quality of the software produced.

khalladay
At least with the real time applications that I have experience with, this is often mitigated with custom memory allocators which pre-allocate large contiguous blocks before performance critical sections and then parcels out segments of those blocks at runtime.
pjmlp
Which is also possible in GC enabled languages, so one can make use of a GC and then make use of similar technics for the code path that really requires them.

For example in real time JVMs

pjmlp
A GC API definition is already in the standard. It was added in C++11.
chrisseaton
I agree with you, but in this

> std::string x = "Hello, " + fullname; // cpu cycles spent on GC

you are already spending cycles on memory management (if C++ allocates character data on the heap which I think it does). You are searching for free space in the heap, possibly synchronising to do that, and so on.

With a GC you may even use less cycles here! For example a copying GC could mean that you can allocate with a simple thread local bump pointer.

So in your statement you are already paying an unknown cycle cost for memory management. Why do you care if it's GC?

Your answer is probably the variance in the number of cycles - the noticeable pauses - which is a reasonable concern.

to3m
malloc/free/etc. will only cost when they're called; GC can be a continuous expense even when there's no collection.
pkolaczk
Most GCs will not cost you a single cycle if you never allocate.
daemin
But in most GC languages there is nothing you can do without allocating. Creating an object is already allocating it on the heap, printing a string will also allocate.
None
None
pjmlp
Not in GC languages that also have value types.

For example you can do all of that in Modula-3, Active Oberon, Component Pascal and many others without allocating more than in C for example.

Mixing all GC enabled languages in the same box is a mistake.

pkolaczk
I don't know why they downvote you. Even in Java with no value types yet, there are ways to write useful code with no or almost no managed heap allocation. And if you don't need ultra low pauses, mixing these techniques, e.g. using managed heap for 80% of non-performance critical stuff and using careful manual dynamic allocation for the rest 20% (e.g. large data buffers) typically gets you all good things at once: high throughput, low pauses, almost no GC activity when it matters and convenience of writing most of the code.
DannyBee
This is technically correct (though in most gc's, if you allocate and keep a single byte, you pay for it with various barriers, etc, forever) but then, because they have good GC's that are like this, almost every GC language used allocates all the time.

So it would be more accurate to say "Most GC's will not cost you a single cycle if you and the underlying language runtime and execution environment do not allocate".

IE your statement, while true in theory, makes literally no practical difference if allocations happen all the time without you knowing or controlling it.

pjmlp
It is a matter how it is implemented.
fenesiistvan
I just recently wrote my own memory allocator for my own String. Now my String is at least 2x faster then the next fastest alternative (I have tried many alternatives for C++ strings including std::string of course). String allocation can be made very fast with thread local memory pools (and you just need a basic GC to free up memory if there are a lot of strings allocated in one thread but destroyed on another one).
T-zex
It is very likely that in this particular case small string optimization would allocate data on stack and no heap allocations would take place.
scott_s
> For example a copying GC could mean that you can allocate with a simple thread local bump pointer.

This is equally true for explicit memory allocation. The point is that on some allocations under GC, it will have to collect garbage. And collecting garbage will tend to be more expensive than explicit frees, because it usually has to do work to discover what is garbage.

daemin
Yes, but in this case we know when the allocations will occur, and when they will be freed. If using a GC we know when they will occur, but do not know when they will be freed. Which means that at some indeterminate point in the future there will be a large temporary slowdown due to processing the GC.

This is one of the bigger reasons people use C++ and even techniques within it to explicitly collect such items at a known point in time. (Techniques such as marking items as dead in an array but still keeping them in there until the end of frame, etc)

branchless
This is the advantage of C++ for me. Deterministic behaviour.
chrisseaton
But is it deterministic? Will the allocator always have a deterministic amount of work to do to find enough free space for your string characters? I'm not sure that's the case.
monocasa
Which allocator? For those who care, they replace the allocator.
None
None
junke
You can deactivate GC momentarily and reclaim memory when you want (end of frame, every ten frames, ...). Most of the time you can manage to write your code to minimize allocation, or make sure memory is allocated on the stack. Depending on your parameters and a little bit of profiling, you can manage to have a stable usage of memory over time and a bounded GC time.
titzer
> If using a GC we know when they will occur, but do not know when they will be freed. Which means that at some indeterminate point in the future there will be a large temporary slowdown due to processing the GC.

This just isn't true anymore. Incremental collectors can achieve pause times in the single-digit millisecond range, and concurrent collectors can achieve pause times in the single-digit to tens of microseconds range, even for super-high-allocation rate programs. There are even real-time collectors suitable for audio and other hard real-time applications.

Azul GC (a high performance concurrent compacting collector): https://www.azul.com/products/zing/pgc/

Metronome (a real-time GC with guaranteed maximum pause times): http://researcher.watson.ibm.com/researcher/view_group_subpa...

GC scheduling in V8 (hides GC pause times between frames, reducing animation jank): http://queue.acm.org/detail.cfm?id=2977741

The Go GC ships now with a very low-latency GC: https://blog.golang.org/go15gc

daemin
A millisecond is a huge amount of time, and in that time we can do so many more things more useful to the application than just collecting its trash.
titzer
Again downvotes. It'd be nice if people actually replied instead of downvoting.
paulsutter
> This just isn't true anymore. Incremental collectors can achieve pause times in the single-digit millisecond range

Single digit milliseconds is millions of instructions, which /is/ a large slowdown in some applications.

titzer
How much do you think a page fault costs?
paulsutter
Page faults cost zero. On locked pages.
Check all of Herb Sutter's CppCon videos from 2015-2016, especially this one: https://www.youtube.com/watch?v=JfmTagWcqoE

He does a great job of walking through the thought process of deciding which of the options makes sense in different situations.

Herb Sutter gave a really great talk on this:

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

He has 3 guidelines:

1. Prefer scoped lifetimes by default (local members)

2. Else prefer unique_ptr and containers

3. Else consider shared_ptr

His whole talk gives examples that show the flexibility of unique_ptr and how modern C++ can express ownership.

See slide 5 here:

https://github.com/CppCon/CppCon2016/blob/master/Presentatio...

Sep 26, 2016 · 4 points, 0 comments · submitted by rvalue
HN Theater is an independent project and is not operated by Y Combinator or any of the video hosting platforms linked to on this site.
~ yaj@
;laksdfhjdhksalkfj more things
yahnd.com ~ Privacy Policy ~
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.