digitalmars.D - Precise GC state
- Temtaime (10/10) Nov 22 2017 Hi all !
- Adam Wilson (6/15) Nov 22 2017 +1
- =?UTF-8?B?Tm9yZGzDtnc=?= (4/6) Nov 22 2017 Only the Win32 build fails as
- Nicholas Wilson (3/9) Nov 22 2017 Thats a linker(?) limitation for OMF (or whatever is the win32
- Adam Wilson (9/21) Nov 22 2017 We really should be using the MSVC linker on Windows for both x86 and x6...
- =?UTF-8?B?Tm9yZGzDtnc=?= (8/10) Nov 23 2017 Was just fixed!
- Adam Wilson (16/25) Nov 23 2017 Rainer is awesome!
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (9/16) Nov 23 2017 But generational GC only makes sense if many of your GC objects
- Adam Wilson (41/55) Nov 23 2017 Sensible to whom? The great thing about D is that it works just we well
- Dmitry Olshansky (9/26) Nov 23 2017 The problem with generational GC is “write barriers” - pieces of
- jmh530 (3/5) Nov 25 2017 What about @safe?
- Dmitry Olshansky (7/12) Nov 26 2017 If all of the code is 100% @safe (not system and not trusted) you
- Ola Fosheim Grostad (9/11) Nov 26 2017 Well, you can if you carefully lock the gc runtime or if you dont
- jmh530 (7/13) Nov 26 2017 I was thinking you would use a generational or precise GC for
- Jonathan M Davis (26/40) Nov 26 2017 It wouldn't work. @safe code and @system code call each other all the ti...
- Ola Fosheim Grostad (11/14) Nov 26 2017 This is easy to fix, introduce a uniquely owned type (isolated)
- jmh530 (3/6) Nov 26 2017 I see. Fair enough.
- Dmitry Olshansky (27/43) Nov 26 2017 Wishful thinking. Memory flows through a program and things
- Ola Fosheim Grostad (15/18) Nov 26 2017 No, you dont. Nobody in their right mind would do so in C++ as a
- Dmitry Olshansky (10/20) Nov 26 2017 Last time I check shared_ptr can be safely shared across threads,
- Ola Fosheim Grostad (14/21) Nov 26 2017 The controlblock can, but it is crazy to use shared_ptr for
- Dmitry Olshansky (14/37) Nov 27 2017 Then watch Herb’s Sutter recent talk “Leak freedom by default”.
- Ola Fosheim Grostad (8/14) Nov 27 2017 He could be, I havent seen it... Shared_ptr isnt frequently used,
- Ola Fosheim Grostad (2/2) Nov 27 2017 Btw, it would improve the discourse if people tried to
- Dmitry Olshansky (23/37) Nov 27 2017 Ahhhaaahhha.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (56/79) Nov 27 2017 Not really. Unique_ptr is, though.
- Dmitry Olshansky (43/112) Nov 27 2017 I’ve seen a tech giant that works on uber high-performance things
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (66/95) Nov 27 2017 Look, I am not against "high level" features, but shared_ptr is
- Jonathan M Davis (8/14) Nov 27 2017 I don't understand this. I would expect most modern C++ programs to be u...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (9/13) Nov 27 2017 You get this:
- Ola Fosheim Grostad (5/7) Nov 27 2017 Actually, seems like the common implementation uses 16 bytes, so
- Temtaime (3/3) Feb 03 2018 And 2k18 passes and there will be no precise gc this year it
-
Petar Kirov [ZombineDev]
(13/32)
Nov 26 2017
What I think Dmitry meant is that shared_ptr
uses atomic - Ola Fosheim Grostad (7/9) Nov 26 2017 Well, the compiler can in theory ellide atomics if it csn prove
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (13/17) Nov 27 2017 Just to expand a bit on this: What is lost here is that what has
- Temtaime (5/5) Nov 27 2017 Please stop this flame and make first real step into bringing
- Ola Fosheim Grostad (6/9) Nov 27 2017 If so, why are you here? But you are fundamentally wrong. Precise
- codephantom (10/11) Nov 27 2017 Can you elaborate?
- Ola Fosheim Grostad (10/13) Nov 27 2017 Precise scanning of pointers makes sense when you have many
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (51/70) Nov 23 2017 A GC makes most sense when the compiler fail at disproving that
- codephantom (4/6) Nov 24 2017 Given all the bullshit bugs I have to deal with, I'm starting to
- Dmitry Olshansky (22/37) Nov 24 2017 I think you could in any case. What precise allows is to copy
Hi all ! https://github.com/dlang/druntime/pull/1603 Can someone investigate and bring it to us ? 4 years passed from gsoc 2013 and there's still no gc. Many apps suffers from false pointers and bringing such a gc will help those who affected by it. It seems all the tests are passed except win32 because of optlink failures. Maybe there's some chance to accelerate this PR ? Thanks all
Nov 22 2017
On 11/22/17 02:53, Temtaime wrote:Hi all ! https://github.com/dlang/druntime/pull/1603 Can someone investigate and bring it to us ? 4 years passed from gsoc 2013 and there's still no gc. Many apps suffers from false pointers and bringing such a gc will help those who affected by it. It seems all the tests are passed except win32 because of optlink failures. Maybe there's some chance to accelerate this PR ? Thanks all+1 -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
Nov 22 2017
On Wednesday, 22 November 2017 at 10:53:45 UTC, Temtaime wrote:Hi all ! https://github.com/dlang/druntime/pull/1603Only the Win32 build fails as Error: more than 32767 symbols in object file What's wrong?
Nov 22 2017
On Wednesday, 22 November 2017 at 13:23:54 UTC, Nordlöw wrote:On Wednesday, 22 November 2017 at 10:53:45 UTC, Temtaime wrote:Thats a linker(?) limitation for OMF (or whatever is the win32 object file format).Hi all ! https://github.com/dlang/druntime/pull/1603Only the Win32 build fails as Error: more than 32767 symbols in object file What's wrong?
Nov 22 2017
On 11/22/17 05:44, Nicholas Wilson wrote:On Wednesday, 22 November 2017 at 13:23:54 UTC, Nordlöw wrote:We really should be using the MSVC linker on Windows for both x86 and x64. I propose we change the default behavior of -m32 to point to MSVC, keep the -m32mscoff, but mark it as deprecated, and add a -m32omf flag to retain the current behavior. -- Adam Wilson IRC: LightBender import quiet.dlang.dev;On Wednesday, 22 November 2017 at 10:53:45 UTC, Temtaime wrote:Thats a linker(?) limitation for OMF (or whatever is the win32 object file format).Hi all ! https://github.com/dlang/druntime/pull/1603Only the Win32 build fails as Error: more than 32767 symbols in object file What's wrong?
Nov 22 2017
On Wednesday, 22 November 2017 at 13:44:22 UTC, Nicholas Wilson wrote:Thats a linker(?) limitation for OMF (or whatever is the win32 object file format).Was just fixed! What improvements to D's concurrency model is made possible with this precise GC? I recall Martin Nowak saying at DConf 2016 that a precise GC will enable data with isolated or immutable indirections to be safely moved between threads
Nov 23 2017
On 11/23/17 02:47, Nordlöw wrote:On Wednesday, 22 November 2017 at 13:44:22 UTC, Nicholas Wilson wrote:Rainer is awesome! That is certainly one aspect that it will enable. I would focus on a generational GC first for two reasons. The first is that you can delay the scans of the later gens if the Gen0 (nursery) has enough space, so for a lot of programs it would result in a significant cut-down in collection times. The second is that you still typically have to stop the execution of the thread on the Gen0 collection (the objects most likely to be hot). So with a non-generational concurrent collector you have to stop the thread for the entirety of the scan, because you have no way to know which objects are hot and which are cold. -- Adam Wilson IRC: LightBender import quiet.dlang.dev;Thats a linker(?) limitation for OMF (or whatever is the win32 object file format).Was just fixed! What improvements to D's concurrency model is made possible with this precise GC? I recall Martin Nowak saying at DConf 2016 that a precise GC will enable data with isolated or immutable indirections to be safely moved between threads
Nov 23 2017
On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:I would focus on a generational GC first for two reasons. TheBut generational GC only makes sense if many of your GC objects have a short life span. I don't think this fits well with sensible use of a language like D where you typically would try to put such allocations on the stack and/or use RAII or even an arena.The second is that you still typically have to stop the execution of the thread on the Gen0 collection (the objects most likely to be hot). So with a non-generational concurrent collector you have to stop the thread for the entirety of the scan, because you have no way to know which objects are hot and which are cold.How are you going to prove that references are all kept within the generation in D? There is some very costly book keeping involved that simply don't work well with D semantics.
Nov 23 2017
On 11/23/17 13:40, Ola Fosheim Grøstad wrote:On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:Sensible to whom? The great thing about D is that it works just we well for low-level people who need to wring the most out of the metal, AND the high-level productivity minded apps people, who don't. RAII+stack allocations make sense when I care about WHEN an object is released and wish to provide some semblance of control over deallocation (although as Andrei has pointed out numerous time, you have no idea how many objects are hiding under that RAII pointer). But there are plenty of use cases where I really don't care when memory is released, or even how long it takes to release. For example, all of the D apps that I've written and use in production, GC-allocate everything. I don't have a single struct in the code. But I don't care, because the program is so short lived and the working set so small that there will never be GC collection. And it's not like this is an uncommon or unwise pattern in D, DMD itself follows this exact pattern. Obviously my pattern isn't "wrong" or else DMD itself is "wrong". It's just not your definition of "correct". Another use case where RAII makes no sense is GUI apps. The object graph required to maintain the state of all those widgets can be an absolute performance nightmare on destruction. Closing a window can result in the destruction of tens-of-thousands of objects (I've worked on codebases like that), all from the GUI thread, causing a hang, and a bad user experience. Or you could use a concurrent GC and pass off the collection to a different thread. (See .NET)I would focus on a generational GC first for two reasons. TheBut generational GC only makes sense if many of your GC objects have a short life span. I don't think this fits well with sensible use of a language like D where you typically would try to put such allocations on the stack and/or use RAII or even an arena.Again, which semantics? If you compile with -betterc, the bookkeeping and it's overhead simply don't exist. Your arguments are based on a presupposition that D should only be used a certain way; a way that, I am sure, mirrors your own usage patterns. D supports a multitude of different usage patterns, some of which look nothing like what you are holding up as "correct". And this is what makes D special. To remove or dismiss as invalid those usage patterns would be detrimental to those of us who use them and be catastrophic to D in general. As a community, can we please stop it with the subjective judgements of what is and is not "sensible" in D, and start supporting the people using it, however they are using it, even if we are sure that they are "wrong"? -- Adam Wilson IRC: LightBender import quiet.dlang.dev;The second is that you still typically have to stop the execution of the thread on the Gen0 collection (the objects most likely to be hot). So with a non-generational concurrent collector you have to stop the thread for the entirety of the scan, because you have no way to know which objects are hot and which are cold.How are you going to prove that references are all kept within the generation in D? There is some very costly book keeping involved that simply don't work well with D semantics.
Nov 23 2017
On Friday, 24 November 2017 at 05:34:14 UTC, Adam Wilson wrote:On 11/23/17 13:40, Ola Fosheim Grøstad wrote:The problem with generational GC is “write barriers” - pieces of code compiler needs to insert around every write of a pointer. Now given that we can cast pointer to integer and back (also unions), I don’t see how it would work unless we put write barriers everywhere on the off-chance it did wrote a pointer from OLD to NEW space. A better GC is a great direction. Generational one is not feasible unless we disallow quite a few of our features.On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:Sensible to whom? The great thing about D is that it works just we well for low-level people who need to wring the most out of the metal, AND the high-level productivity minded apps people, who don't.I would focus on a generational GC first for two reasons. TheBut generational GC only makes sense if many of your GC objects have a short life span. I don't think this fits well with sensible use of a language like D where you typically would try to put such allocations on the stack and/or use RAII or even an arena.
Nov 23 2017
On Friday, 24 November 2017 at 05:53:37 UTC, Dmitry Olshansky wrote:A better GC is a great direction. Generational one is not feasible unless we disallow quite a few of our features.What about safe?
Nov 25 2017
On Sunday, 26 November 2017 at 04:01:31 UTC, jmh530 wrote:On Friday, 24 November 2017 at 05:53:37 UTC, Dmitry Olshansky wrote:If all of the code is 100% safe (not system and not trusted) you have a different language where write barriers would be cheaper to implement. Sadly you can’t “skip” write barriers in your system code because it may run as part of larger safe. Which is where they are the most costly.A better GC is a great direction. Generational one is not feasible unless we disallow quite a few of our features.What about safe?
Nov 26 2017
On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky wrote:Sadly you can’t “skip” write barriers in your system code because it may run as part of larger safe. Which is where theyWell, you can if you carefully lock the gc runtime or if you dont modify existing scannable pointers that points to existing objects (e.g. You could fill an empty array of pointers in unsafe code or pointers that point to something unscannable.), but all unsafe code would need vetting. So it isnt impossible technically, but it is impossible without a change of philosophy.
Nov 26 2017
On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky wrote:If all of the code is 100% safe (not system and not trusted) you have a different language where write barriers would be cheaper to implement. Sadly you can’t “skip” write barriers in your system code because it may run as part of larger safe. Which is where they are the most costly.I was thinking you would use a generational or precise GC for safe code and then fall back to the normal GC with system/ trusted code. Not sure if that's possible or not, but in my head it would be a separate heap from the safe code. Certainly would be added complexity.
Nov 26 2017
On Sunday, November 26, 2017 18:58:04 jmh530 via Digitalmars-d wrote:On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky wrote:It wouldn't work. safe code and system code call each other all the time (using trusted where necessary), and they freely exchange stuff that was allocated on the GC heap. Heck, it's not even possible to have per-thread heaps (much as that would be desirable), because stuff can be passed between threads, and you can freely cast between shared and non-shared. You do have to be careful about it if you don't want to have problems, but just like safe code can call trusted code such that it's possible for safe code be calling code that was vetted for memory safety by a programmer rather than the compiler, you can have code that casts to and from shared and works perfectly well so long as the programmer vets it properly. We can't even have different heaps for immutable and mutable stuff, because it's very common to construct something as mutable and then cast it to immutable (either explicitly or because it's returned from a pure function which is able to do the cast implicitly, because it knows that the return value couldn't possibly have been passed into the function and thus had to have been allocated inside it). D's type system protects you from all kinds of dumb mistakes, but it has way too many backdoors for the kind of stuff that requires that things be locked down like they would be inside a VM like Java has. Ultimately, if you're talking about a GC and what it can and can't do, I expect that there's very little difference between C and D in terms of what you types of GC you can get away with. D does protect the programmer, but ultimately, it lets you do just about anything that C lets you do if you really want to, and any GC that we use has to work with that. - Jonathan M DavisIf all of the code is 100% safe (not system and not trusted) you have a different language where write barriers would be cheaper to implement. Sadly you can’t “skip” write barriers in your system code because it may run as part of larger safe. Which is where they are the most costly.I was thinking you would use a generational or precise GC for safe code and then fall back to the normal GC with system/ trusted code. Not sure if that's possible or not, but in my head it would be a separate heap from the safe code. Certainly would be added complexity.
Nov 26 2017
On Sunday, 26 November 2017 at 19:11:08 UTC, Jonathan M Davis wrote:We can't even have different heaps for immutable and mutable stuff, because it's very common to construct something as mutable and then cast it to immutable (either explicitly orThis is easy to fix, introduce a uniquely owned type (isolated) that only can transition to immutable. So it is more about being willing to tighten up the semantics. Same thing with GC, but everything has a cost. That said Adam has a point with getting more users, it isnt obvious that the costs wouldnt be offset by increased interest. in by gradually expanding into more performance oriented programming mechanisms... We'll see.
Nov 26 2017
On Sunday, 26 November 2017 at 19:11:08 UTC, Jonathan M Davis wrote:It wouldn't work. safe code and system code call each other all the time (using trusted where necessary), and they freely exchange stuff that was allocated on the GC heap. [snip]I see. Fair enough.
Nov 26 2017
On Sunday, 26 November 2017 at 18:58:04 UTC, jmh530 wrote:On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky wrote:Wishful thinking. Memory flows through a program and things allocated in safe get passed to system (including the last reference cases) and vise versa. Also in D generational hypothesis may not bear as much value due to stack-allocation, libc heap allocations for temporaries likely via RAII. Not to mention cheap (thread-local) Ref Counting, C++ and many other language have to use atomics which makes RC costly. Basically the GC heap is not all of the heap, so a young objects is even smaller part of that. Lastly it is telling that new low-latency concurrent GCs do away without explicit generations. I’m thinking a more efficient way to takle temporaries would be to add Regions to GC. So the moment you enter a region all GC things are allocated in a special segment, once you leave it free the segment. The last trick is to tweak memory protection as guard pages over it. Boom! Now if somebody touches it - DanglingPointerException :) Not a panacea but: - libraries using too much of GC can be controlled (unless they build global state) - manual memory management without memory corruption - fast bump allocation, which is what all of sensible VM languages do And no need to create entire separate heap with its own generational schema.If all of the code is 100% safe (not system and not trusted) you have a different language where write barriers would be cheaper to implement. Sadly you can’t “skip” write barriers in your system code because it may run as part of larger safe. Which is where they are the most costly.I was thinking you would use a generational or precise GC for safe code and then fall back to the normal GC with system/ trusted code.Not sure if that's possible or not, but in my head it would be a separate heap from the safe code.And then I do append in system on array allocated in safe. Boom!Certainly would be added complexity.
Nov 26 2017
On Monday, 27 November 2017 at 05:47:49 UTC, Dmitry Olshansky wrote:likely via RAII. Not to mention cheap (thread-local) Ref Counting, C++ and many other language have to use atomics which makes RC costly.No, you dont. Nobody in their right mind would do so in C++ as a general solution. Seems there is trend in doing D-advocacy based on the assumption that programmers using other languages are crazy these days. In C++ sync is manual, which is the only efficient way to do it. Proving correctness for an efficient general solution is an unsolved theoretical problem. You can do it for high level mechanisms, but not low level atm. Rust and Pony claims to have solutions, but they are not general. D most certainly does not have it and never will. When threading is a libray type then you cannot achieve more in D than you can achieve in C++, i.e. Shared is not going to do more than a C++ library type with a separate static analysis tool.
Nov 26 2017
On Monday, 27 November 2017 at 06:36:27 UTC, Ola Fosheim Grostad wrote:On Monday, 27 November 2017 at 05:47:49 UTC, Dmitry Olshansky wrote:Last time I check shared_ptr can be safely shared across threads, hence RC is takling synchronization and most likely atomics since locks won’t be any better.likely via RAII. Not to mention cheap (thread-local) Ref Counting, C++ and many other language have to use atomics which makes RC costly.No, you dont. Nobody in their right mind would do so in C++ as a general solution. Seems there is trend in doing D-advocacy based on the assumption that programmers using other languages are crazy these days.In C++ sync is manual, which is the only efficient way to do??? shared_ptr is nowhere manual. Sorry but the rest of the of post is take a direction into the fog, that I don’t want to follow. My post is about particular primitive in C++ std, what could be done instead or in addition to is not important.
Nov 26 2017
On Monday, 27 November 2017 at 06:47:00 UTC, Dmitry Olshansky wrote:Last time I check shared_ptr can be safely shared across threads, hence RC is takling synchronization and most likely atomics since locks won’t be any better.The controlblock can, but it is crazy to use shared_ptr for anything more than high level ownership. It is a general solution with weak pointers and extra indirection, not a typical RC implementation for datastructures.There is an upcoming atomic_shared_ptr, but it is not in the standard yet.In C++ sync is manual, which is the only efficient way to do??? shared_ptr is nowhere manual.My post is about particular primitive in C++ std, what could be done instead or in addition to is not important.Oh, but it is. 1. D currently does not provide what you says it does. 2. Sane C++ programmers rarely use shared_ptr for more than exchsnging ownership (suitable for sharing things like bitmap textures). There are plenty of other RC implementations for tracking memory. So you compare apples and oranges.
Nov 26 2017
On Monday, 27 November 2017 at 07:03:01 UTC, Ola Fosheim Grostad wrote:On Monday, 27 November 2017 at 06:47:00 UTC, Dmitry Olshansky wrote:Then watch Herb’s Sutter recent talk “Leak freedom by default”. Now THAT guy must be out of his mind :) I have no idea what are your typical C++ programmers.Last time I check shared_ptr can be safely shared across threads, hence RC is takling synchronization and most likely atomics since locks won’t be any better.The controlblock can, but it is crazy to use shared_ptr for anything more than high level ownership. It is a general solution with weak pointers and extra indirection, not a typical RC implementation for datastructures.atomic_shared_pointer is nothing but what you seem to imply. It’s not manual sync for one.There is an upcoming atomic_shared_ptr, but it is not in the standard yet.In C++ sync is manual, which is the only efficient way to do??? shared_ptr is nowhere manual.You keep spreading FUD on this forum, I’m still not sure of your reasons though.My post is about particular primitive in C++ std, what could be done instead or in addition to is not important.Oh, but it is.1. D currently does not provide what you says it does.RefCounted!T2. Sane C++ programmers rarely use shared_ptr for more than exchsnging ownership (suitable for sharing things like bitmap textures).Including across threads btw.There are plenty of other RC implementations for tracking memory.Like what? In standard C++ all you’ve got is unique_ptr and shared_ptr (and weak but that besides the point). There is maybe a ton of reimplementations of said things.
Nov 27 2017
On Monday, 27 November 2017 at 14:35:03 UTC, Dmitry Olshansky wrote:Then watch Herb’s Sutter recent talk “Leak freedom by default”. Now THAT guy must be out of his mind :)He could be, I havent seen it... Shared_ptr isnt frequently used, it is a last resort,atomic_shared_pointer is nothing but what you seem to imply. It’s not manual sync for one.Err... That was my point... Only assignment and reset is protected in shared_ptr, all other methods require manual sync.You keep spreading FUD on this forum, I’m still not sure of your reasons though.And there we go ad hominem as usual... With no argument to back it up... Bye.
Nov 27 2017
Btw, it would improve the discourse if people tried to distinguish between language constructs and library constructs...
Nov 27 2017
On Monday, 27 November 2017 at 15:56:09 UTC, Ola Fosheim Grostad wrote:On Monday, 27 November 2017 at 14:35:03 UTC, Dmitry Olshansky wrote:Ahhhaaahhha. Really, shared_ptr is the most contagious primitive of modern C++. To quote MS STL guy “I’m surprised we had no non-inteusive ref-counted ptr in std lib for so long”. Going Native videos are full of questions on that.Then watch Herb’s Sutter recent talk “Leak freedom by default”. Now THAT guy must be out of his mind :)He could be, I havent seen it... Shared_ptr isnt frequently used, it is a last resort,For the benefit of others, let me destroy that: - shared_ptr allows to share T with thread-safe ownership, ref-counts are accounted atomically (sharing copies of shared_ptr pointing to the same block). Copy of shared_ptr is thread safe and does the count. - atomic_shared_prt would also allow one to initialize a shared variable (eg global) of type shared_ptr safely from multiple threads The manual synchronization part comes in if you try to work with payload T itself. THAT is manual. Since C++ doesn’t see a difference at type level of shared vs local, there is no thread-local variation of shared_ptr. It would be too unsafe even for those guys, contrary to what Ola responses imply.atomic_shared_pointer is nothing but what you seem to imply. It’s not manual sync for one.Err... That was my point... Only assignment and reset is protected in shared_ptr, all other methods require manual sync.Well I can clearly see misinformation and describe it as such. See your point about atomic_shared_ptr.You keep spreading FUD on this forum, I’m still not sure of your reasons though.And there we go ad hominem as usual... With no argument to back it up... Bye.
Nov 27 2017
On Monday, 27 November 2017 at 17:16:50 UTC, Dmitry Olshansky wrote:Really, shared_ptr is the most contagious primitive of modern C++.Not really. Unique_ptr is, though.To quote MS STL guy “I’m surprised we had no non-inteusive ref-counted ptr in std lib for so long”. Going Native videos are full of questions on that.Yeah, they tend to pretend that C++ is a high level language, it makes for good talks. Let me put it this way, C++ is an acceptable low level language, but it is a rather poor high level language. So if you talk with people who use C++ as a high level language you probably see different usage patterns.Oh well, looks like reset and assignment isn't required per the spec to be protected against races either. That doesn't weaken my point, the control block is protected but not the methods of shared_ptr. I hardly ever use shared_ptr. In practical programming you usually need to exchange more than a single entity so you need manual sync anyway.Err... That was my point... Only assignment and reset is protected in shared_ptr, all other methods require manual sync.For the benefit of others, let me destroy that:- shared_ptr allows to share T with thread-safe ownership, ref-counts are accounted atomically (sharing copies of shared_ptr pointing to the same block). Copy of shared_ptr is thread safe and does the count.Not sure if I follow that description. Shared shared_ptr's are not thread safe, a shared control block is. (Technically you share a pointer, not an object of type T, but that is a minor detail.) shared_ptr is pointing to a shared control block that in turn has a pointer that points to the resource. This control block contains two counters. These counters are incremented and decremented atomically. If you access the same shared_ptr from two threads then you have a potential race condition per the spec.- atomic_shared_prt would also allow one to initialize a shared variable (eg global) of type shared_ptr safely from multiple threadsThe main point is that the methods of atomic_shared_ptr are protected against races. It is needed because you usually would have have ownership pointers embedded in a another shared object. So having a protected control-block is not sufficient outside the trivial toy-program.The manual synchronization part comes in if you try to work with payload T itself. THAT is manual.No, none of the methods on shared_ptr guarantees that races won't happen. And in general you usually want to access more than one unit in a critical section. So in C++ you typically need manual sync. There are no suitable language features to get around that in C++ and library features are inadequate in the general sense. Go and Pony have language features that are helpful. C++ doesn't.Since C++ doesn’t see a difference at type level of shared vs local, there is no thread-local variation of shared_ptr. It would be too unsafe even for those guys, contrary to what Ola responses imply.Sigh. I implied that shared_ptr on a single thread is mostly useless. But this: struct OurSharedCache { shared_ptr<T> something; shared_ptr<T> somethingelse } Not safe.Well I can clearly see misinformation and describe it as such. See your point about atomic_shared_ptr.What about it? «If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur» http://en.cppreference.com/w/cpp/memory/shared_ptr «The class template atomic_shared_ptr provides thread-safe atomic pointer operations over a std::shared_ptr.» http://en.cppreference.com/w/cpp/experimental/atomic_shared_ptr
Nov 27 2017
On Monday, 27 November 2017 at 18:29:56 UTC, Ola Fosheim Grøstad wrote:On Monday, 27 November 2017 at 17:16:50 UTC, Dmitry Olshansky wrote:I’ve seen a tech giant that works on uber high-performance things making heavy use of STL, and being fond of C++14 “high-level” features. I almost took their job offer, it is a nice place. Comparatively I’ve worked at Google where none of the new goodies are in use. There are some great things in their “std”, but most have horrible interfaces and people routinelly discuss how having at least STL-like primitives would be a huge boon.Really, shared_ptr is the most contagious primitive of modern C++.Not really. Unique_ptr is, though.To quote MS STL guy “I’m surprised we had no non-inteusive ref-counted ptr in std lib for so long”. Going Native videos are full of questions on that.Yeah, they tend to pretend that C++ is a high level language, it makes for good talks. Let me put it this way, C++ is an acceptable low level language, but it is a rather poor high level language. So if you talk with people who use C++ as a high level language you probably see different usage patterns.That must be why you seriously have no idea how people use it. Otherwise you’d know that nobody shares a reference to shared pointer but rather a copy. The whole point of smart pointer is to avoid naked references.Oh well, looks like reset and assignment isn't required per the spec to be protected against races either. That doesn't weaken my point, the control block is protected but not the methods of shared_ptr. I hardly ever use shared_ptr. In practical programming youErr... That was my point... Only assignment and reset is protected in shared_ptr, all other methods require manual sync.For the benefit of others, let me destroy that:They are if you use them as intended - value types, that pretend to be pointers.- shared_ptr allows to share T with thread-safe ownership, ref-counts are accounted atomically (sharing copies of shared_ptr pointing to the same block). Copy of shared_ptr is thread safe and does the count.Not sure if I follow that description. Shared shared_ptr's are not thread safea shared control block is. (Technically you share a pointer, not an object of type T, but that is a minor detail.)No, you actually share an object and the last one to decrement the ref will destroy it. Now that is actually thread-safe thanks to atomic counter.shared_ptr is pointing to a shared control block that in turn has a pointer that points to the resource. This control block contains two counters. These counters are incremented and decremented atomically.That is my original point, which you now violently agree with :)If you access the same shared_ptr from two threads then you have a potential race condition per the spec.Just underlines that you don’t understand how it is supposed to be used.Sufficient for what? Smart pointers are about ownership not protecting from races. I also don’t understand what are you trying to prove. My point was: C++ has to do atomic counting, because it has no concept of shared vs local. All of things you’ve said just prove it or are talking about something I’m not discussing here.- atomic_shared_prt would also allow one to initialize a shared variable (eg global) of type shared_ptr safely from multiple threadsThe main point is that the methods of atomic_shared_ptr are protected against races. It is needed because you usually would have have ownership pointers embedded in a another shared object. So having a protected control-block is not sufficient outside the trivial toy-program.Just as you quoted below - all const are, including copy & destructor.The manual synchronization part comes in if you try to work with payload T itself. THAT is manual.No, none of the methods on shared_ptr guarantees that races won't happen.You did imply it’s useless on single thread and not used in concurrency because you need manual sync. I’d argue you don’t need a sync to access shared_ptr, you’d need it for object it points to. It still solves the ownership and deterministic destruction in the presense of concurrent shared_ptrs of the same object. But again - you countered a different point all together, see above.Since C++ doesn’t see a difference at type level of shared vs local, there is no thread-local variation of shared_ptr. It would be too unsafe even for those guys, contrary to what Ola responses imply.Sigh. I implied that shared_ptr on a single thread is mostly useless.But this: struct OurSharedCache { shared_ptr<T> something; shared_ptr<T> somethingelse }Not that I talked about it. This has nothing to do with shared_ptr and/or things I stated originally.Copy & destruction is actually fine, which you seem to ignore. Also accessing const methods of payload is fine. New C++ implies const == thread safe btw, at least in all of STL.Well I can clearly see misinformation and describe it as such. See your point about atomic_shared_ptr.What about it? «If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur»
Nov 27 2017
On Monday, 27 November 2017 at 20:13:35 UTC, Dmitry Olshansky wrote:I’ve seen a tech giant that works on uber high-performance things making heavy use of STL, and being fond of C++14 “high-level” features.Look, I am not against "high level" features, but shared_ptr is nothing like the thread local ref-counting you were talking about which was at least introduced by Walter and Andrei as a competing solution to tracking of borrowed pointers in Rust.That must be why you seriously have no idea how people use it. Otherwise you’d know that nobody shares a reference to shared pointer but rather a copy. The whole point of smart pointer is to avoid naked references.I have an idea of how people use it… why all the ad hominem? I just don't find it very useful. In practice I'm usually better off exchanging through a facade and having the exchanged resource wrapped up in a custom type. It is more maintainable and easy to deal with in terms of correctness.They are if you use them as intended - value types, that pretend to be pointers.Sure. Not that this is how they are defined to be used. It is a very limited use case.Ok, sure, you share an ownership relation to an object, but it isn't general enough, which was what I tried to suggest. For instance, if you have a resource on a GPU with an integer ID that is also a resource, but shared_ptr does not work (unless you accept extra indirections). So, I'd rather see an owned<T>, which also can handle non-pointed-to-objects. Then one might ask, why not just have "unique<owningptr<T>>" or "shared<owningptr<T>>" that also would work with "unique<Resource>" and "shared<Resource>"? Then you can build generic ADTs with a generic notion of ownership . The basic idea is that an owned resource that has to go through a pointer isn't really necessary or as generic a type as it could have been.a shared control block is. (Technically you share a pointer, not an object of type T, but that is a minor detail.)No, you actually share an object and the last one to decrement the ref will destroy it. Now that is actually thread-safe thanks to atomic counter.That is my original point, which you now violently agree with :)Ok, then I didn't get your original point.Just underlines that you don’t understand how it is supposed to be used.I know how it can be used. That doesn't mean that it is as generally useful as it should be to be useful for what I want to do.I also don’t understand what are you trying to prove. My point was: C++ has to do atomic counting, because it has no concept of shared vs local.That I disagree with. In C++ correct syncing is entirely dependent on the programmer. Even if atomic_shared_ptr provide some guarantees these guarantees will not hold if you depend on more than one entity. So there is no mechanism in C++ that provide something to ensure correctness when it comes to concurrency. I.e. you totally depend on the programmer's ability. You can create a concept of D-style local/shared by just creating a wrapper type and use a lint-style verification. This is the route C++ is heading in general for various properties related to pointer types it seems. (e.g. wrap with borrowed, owned, nullable etc). Which is also basically what D has, except shared is a builtin, but semantically I see no difference. To get a semantic difference that matter you need concurrency to be part of the language itself. If you cannot express propositions about concurrency in the language, then there is a very limited ability to do something interesting with it.You did imply it’s useless on single thread and not used in concurrency because you need manual sync. I’d argue you don’t need a sync to access shared_ptr, you’d need it for object it points to.Listen, if you build a shared graph with shared_ptrs then you are in trouble. So you would need atomic_shared_ptrs. That's a perfectly reasonable use case. You are presuming that the shared_ptr objects are thread local and that they are not embedded in other objects that are reachable by another thread. Those a very limiting presumptions.It still solves the ownership and deterministic destruction in the presense of concurrent shared_ptrs of the same object.But it doesn't solve correctness. You still rely on the programmer to get correctness. Which is a tall order when you use encapsulation. That only works if you never embed shared_ptr in a class.Copy & destruction is actually fine, which you seem to ignore. Also accessing const methods of payload is fine. New C++ implies const == thread safe btw, at least in all of STL.As far as I can see from the description at cppreference.com there is no guarantee of freedom for races for anything if both threads reference the same shared_ptr object (not the control block but the object pointing to the control block). You can obviously transmit it to another thread using a mutex-like/messaging-like setup if that is what you mean.
Nov 27 2017
On Monday, November 27, 2017 15:56:09 Ola Fosheim Grostad via Digitalmars-d wrote:On Monday, 27 November 2017 at 14:35:03 UTC, Dmitry Olshansky wrote:I don't understand this. I would expect most modern C++ programs to be using shared_ptr as the default for most pointers and thus use it heavily. Any time that I've been able to use at least TR1 for C++, I've used it everywhere, and when I've been stuck with C++98, I've used a home-grown equivalent. shared_ptr is a _huge_ boon for avoiding memory problems. - Jonathan M DavisThen watch Herb’s Sutter recent talk “Leak freedom by default”. Now THAT guy must be out of his mind :)He could be, I havent seen it... Shared_ptr isnt frequently used, it is a last resort,
Nov 27 2017
On Monday, 27 November 2017 at 18:00:59 UTC, Jonathan M Davis wrote:I don't understand this. I would expect most modern C++ programs to be using shared_ptr as the default for most pointers and thus use it heavily.You get this: shared_ptr -> control_block -> object Instead of this: unique_ptr -> object my_ref_ptr ->objectshared_ptr is a _huge_ boon for avoiding memory problems.Yes, if you don't mind the inefficiency, i.e. if you are doing high-level programming in C++.
Nov 27 2017
On Monday, 27 November 2017 at 18:32:39 UTC, Ola Fosheim Grøstad wrote:You get this: shared_ptr -> control_block -> objectActually, seems like the common implementation uses 16 bytes, so that it has a direct pointer as well. So twice the size of unique_ptr.
Nov 27 2017
And 2k18 passes and there will be no precise gc this year it seems. Great D language. Better to think to move from it away.
Feb 03 2018
On Monday, 27 November 2017 at 06:36:27 UTC, Ola Fosheim Grostad wrote:On Monday, 27 November 2017 at 05:47:49 UTC, Dmitry Olshansky wrote:What I think Dmitry meant is that shared_ptr<T> uses atomic instructions for the reference counting (though you need to use atomic_shared_ptr, or atomic_* function if you want to modify the shared_ptr itself) and you can't opt out of that even if you're not sharing the shared_ptr with other threads. On the other hand in D, a properly designed SharedPtr(T) will use atomic instructions for reference counting, only if it's payload type T is has the 'shared' type qualifier. And if you have 'shared(SharedPtr(T))', only then you'll have atomic instructions for the SharedPtr struct itself, but unlike shared_ptr<T>, you won't have access to the non-thread safe methods.likely via RAII. Not to mention cheap (thread-local) Ref Counting, C++ and many other language have to use atomics which makes RC costly.No, you dont. Nobody in their right mind would do so in C++ as a general solution. Seems there is trend in doing D-advocacy based on the assumption that programmers using other languages are crazy these days. In C++ sync is manual, which is the only efficient way to do it. Proving correctness for an efficient general solution is an unsolved theoretical problem. You can do it for high level mechanisms, but not low level atm. Rust and Pony claims to have solutions, but they are not general. D most certainly does not have it and never will. When threading is a libray type then you cannot achieve more in D than you can achieve in C++, i.e. Shared is not going to do more than a C++ library type with a separate static analysis tool.
Nov 26 2017
On Monday, 27 November 2017 at 06:59:30 UTC, Petar Kirov [ZombineDev] wrote:the shared_ptr itself) and you can't opt out of that even if you're not sharing the shared_ptr with other threads.Well, the compiler can in theory ellide atomics if it csn prove that the memory cannot be accessed by another thread. But it kinda is missing the point that if it only is in a single thread then it would typically only have only one assignment. Shared_ptr is for holding a resource not for using it...
Nov 26 2017
On Monday, 27 November 2017 at 07:09:25 UTC, Ola Fosheim Grostad wrote:But it kinda is missing the point that if it only is in a single thread then it would typically only have only one assignment. Shared_ptr is for holding a resource not for using it...Just to expand a bit on this: What is lost here is that what has been proposed for D is to have a RC solution to solve what Rust does with borrowed pointers. Not unlike Swift. In C++ life time management of borrowed pointers is fully manual, so it relies on algorithmic design rather than a programming mechanism. Although for C++ there is upcoming wrapper-types to allow for separate static analysis tooling that is comparable to Rust. The intended usage is not comparable. (In C++ you typically will have per-type or per-application RC, wrapped up where you need it by encapsulation and move-semantics.)
Nov 27 2017
Please stop this flame and make first real step into bringing precise GC to us. Current GC in D is shit and all this speaking won't improve situation. The PR is not merged although it passed all the tests.
Nov 27 2017
On Monday, 27 November 2017 at 09:38:52 UTC, Temtaime wrote:Please stop this flameThere is no flaming.Current GC in D is shit and all this speaking won't improve situation.If so, why are you here? But you are fundamentally wrong. Precise GC will not bring a general improvement, for that you need advanced pointer analysis. So you need a change of philosophy to get a performant GC: semantic changes.
Nov 27 2017
On Monday, 27 November 2017 at 09:38:52 UTC, Temtaime wrote:Current GC in D is shitCan you elaborate? "D is totally useless"..."Dlang is a toy in outer space"... "GC in D is shit" .. I'm very open minded to these different argument styles, and occassionaly make use of them myself. But in a discussion about GC, some technical details might prove to be very useful to those of us following this discussion. I encourage you to further refine your argument... ;-) https://en.wikipedia.org/wiki/Argument
Nov 27 2017
On Monday, 27 November 2017 at 10:13:41 UTC, codephantom wrote:But in a discussion about GC, some technical details might prove to be very useful to those of us following this discussion.Precise scanning of pointers makes sense when you have many cachelines on the GC with no pointers in them. But if you mostly have pointers (a large graph or a tree) then it makes little difference. You need to add a more extensive whole program type analysis where you prove that the GC memory heap isnt reachable from a type... I.e. "pointers reachable through class T can provably never point into the a GC heap in this specific program, so therefore we can ignore all pointers to T".
Nov 27 2017
On Friday, 24 November 2017 at 05:34:14 UTC, Adam Wilson wrote:RAII+stack allocations make sense when I care about WHEN an object is released and wish to provide some semblance of control over deallocation (although as Andrei has pointed out numerous time, you have no idea how many objects are hiding under that RAII pointer). But there are plenty of use cases where I really don't care when memory is released, or even how long it takes to release.A GC makes most sense when the compiler fail at disproving that an object can be part of cycle of references that can be detached. So it makes most sense for typically long lived objects. Imagine if you spend all the effort you would need to put into getting a generational GC to work well into implementing pointer analysis to improve automatic deallocation instead… In order to speed up generated code that allows a generational GC to run you still would need a massive amount of work put into pointer analysis + all the work of making a state of the art GC runtime. Most people consider designing and implementing pointer analysis to be difficult. The regular data flow analysis that the D compiler has now is trivial in comparison. Might need a new IR for it, not sure.Obviously my pattern isn't "wrong" or else DMD itself is "wrong". It's just not your definition of "correct".Well, you could redefine the semantics of D so that you disallow unsafe code and possibly some other things. Then maybe have generational GC would be easy to implement if you don't expect better performance than any other high level language.Another use case where RAII makes no sense is GUI apps. The object graph required to maintain the state of all those widgets can be an absolute performance nightmare on destruction. Closing a window can result in the destruction of tens-of-thousands of objects (I've worked on codebases like that), all from the GUI thread, causing a hang, and a bad user experience. Or you could use a concurrent GC and pass off the collection to a different thread. (See .NET)Sounds like someone didn't do design before the started coding and just kept adding stuff. Keep in mind that OS-X and iOS use reference counting for all objects and it seems to work for them. But they also have put a significant effort into pointer-analysis to reduce ref-count overhead, so still quite a lot more work for the compiler designer than plain RAII.Your arguments are based on a presupposition that D should only be used a certain way;No, it is based on what the D language semantics are and the stated philosophy and the required changes that it would involve. I have no problem with D switching to generational GC. Like you I think most programs can be made to work fine with the overhead, but then you would need to change the philosophy that Walter is following. You would also need to either invest a lot into pointer analysis to keep a clean separation between GC-references and non-GC references, or create a more unforgiving type system that ensure such separation. I think that having a generational GC (or other high level low-latency solutions) probably would be a good idea, but I don't see how anyone could convince Walter to change his mind on such issues. Especially as there are quite a few semantic flaws in D that would be easy to fix, that Walter will not fix because he like D as it is or thinks it would be too much of a breaking change. You would need to change the D philosophy from "performant with some convenience" to "convenience with some means to write performant code". I agree with you that the latter philosophy probably would attract more users. It is hard to compete with C++ and Rust on the former. But I am not sure if Walter's goal is to attract as many users as possible.
Nov 23 2017
On Friday, 24 November 2017 at 07:48:03 UTC, Ola Fosheim Grøstad wrote:But I am not sure if Walter's goal is to attract as many users as possible.Given all the bullshit bugs I have to deal with, I'm starting to think it's the opposite.
Nov 24 2017
On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:I think you could in any case. What precise allows is to copy object during collection if there is no conservative pointers to the object, of course.a precise GC will enable data with isolated or immutable indirections to be safely moved between threadsI would focus on a generational GC first for two reasons. The first is that you can delay the scans of the later gens if the Gen0 (nursery) has enough space, so for a lot of programs it would result in a significant cut-down in collection times.This is like saying that having a large heap will allow you to cut-down collection time. The reason gen0 is fast is that it's mostly dead and you limit the amount of memory Gen0 scans to that of live set in Gen0 heap + remebered set in old gen. It has nothing to do with delaying the scan, in fact Gen0 scans are way more frequent then scans of a simple single-gen GC.The second is that you still typically have to stop the execution of the thread on the Gen0 collection (the objects most likely to be hot).If your Gen0 collector is Stop the World, like all of Java collectors except for the most recent ones - ZGC and Shenandoah. Ironically both are not generational :)So with a non-generational concurrent collector you have to stop the thread for the entirety of the scan,False again. Any "mostly" concurrent GC can scan with a super small pause that is typically used only to mark stacks of Threads. See also the 2 collectors I mentioned. Go's GC is also non-generational (and SUUUPER popular) to put the last nail in the coffin of "I need generational GC because you can't do X without it?".because you have no way to know which objects are hot and which are cold.Why would that stop collector? There is no need to know what is hot, and garbage is always cold.
Nov 24 2017