6 ms·
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 agai
by ingenter 10y ago
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 https://www.youtube.com/watch?v=JfmTagWcqoE
- dmitrygr 10y agoBecause 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 10y agoYou haven't answered my question. Please re-read it carefully.
- dmitrygr 10y agoI 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 10y agoDo 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 10y agoPool 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 10y agoHow 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 10y agoIn 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 10y agoEven 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 10y agohey, that's a generational GC :)
- nanolith 10y agoWell, 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.
- CJefferson 10y agoMy 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.
- ingenter 10y ago> 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 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.
- deleted 10y ago[deleted]
- pjmlp 10y agoWhere 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
- nadam 10y agoYou 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.
- winter_blue 10y agoI 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.
- johncolanduoni 10y agoRust is horrifically bad at dealing with non-trivial cyclical structures for a number of reasons, not having a GC being one of them.
- wspeirs 10y agoBeyond "not having GC", what are those reasons? I'm new to Rust and curious...
- johncolanduoni 10y agoInherited 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 10y agoSee http://smallcultfollowing.com/babysteps/blog/2015/04/06/modeling-graphs-in-rust-using-vector-indices/ 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++.
- jasode 10y ago>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 https://en.wikipedia.org/wiki/Common_Language_Runtime [2]https://en.wikipedia.org/wiki/C_Sharp_(programming_language) https://en.wikipedia.org/wiki/C_Sharp_(programming_language)
- ingenter 10y ago> 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 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.
- 10y ago
- mannykannot 10y agoI 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.
- wyldfire 10y ago> 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 10y agoI 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.
- vmarsy 10y agoI 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
- haberman 10y ago> 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 10y ago> 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.
- titzer 10y agoIt'd be nice if people actually replied with arguments instead of downvoting.
- nanolith 10y agoEvery 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++.
- pjmlp 10y agoHaving 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.