September 26, 2021
On Sunday, 26 September 2021 at 13:29:26 UTC, Paul Backus wrote:
> On Sunday, 26 September 2021 at 07:49:46 UTC, Daniel N wrote:
>> // I hope this works, otherwise it violates the 'law of least surprise'
>> auto c = new Class()
>> scope(exit) destroy(c);
>>
>> If scope(exit) also works, then I don't think there is any problem...
>
> Destroy on a class reference doesn't call the destructor; it just sets the reference to null.

Correction: I remembered wrong; it actually does call the destructor. So this will work for classes with non-trivial destructors.
September 26, 2021
On Sat, Sep 25, 2021 at 07:13:50PM -0700, Walter Bright via Digitalmars-d wrote:
> On 9/25/2021 6:02 PM, Steven Schveighoffer wrote:
> > As I said, the optimizer is fighting you the entire way.
> 
> I'd reframe it as the user is trying to impose his own semantics on the optimizer :-)

IMO, those are just the symptoms.

The core of the problem is that we failed to inform the GC of a reference to some object (because said reference was passed into C land and no longer exists in D land).

GC.addRoot is precisely the ticket that solves this core issue: it informs the GC about said reference. **This is what addRoot is for.**

All the other elaborate attempts to "fix" this problem without using GC.addRoot is just skirting around the issue without actually addressing the problem.  No wonder it feels likes "the optimizer is fighting you the entire way."  Actually, the optimizer is NOT trying to fight you; it's merely telling you that **the semantics expressed in your code is not what you think it is**.  It's simply reducing your code to its actual semantics as defined by the specs. The fact that this reduction isn't what you expect is a sign that the original code doesn't actually mean what you think it means.

It's like trying to prevent a boolean expression from reducing to a constant value by adding more clauses to it.  If there's already a tautology in your expression, it's not gonna change no matter what else you try to add to it.  Fix the tautology, and all of the problems go away. There's no need for all the other fluff.

Seriously, guys, just use GC.addRoot. **That's what it's for.**  Stop trying to cure cancer with Tylenol already! :-D


[...]
> > But I still am wishing for a simple "keep this alive" mechanism that doesn't add too much cruft, and is guaranteed to keep it alive.
> 
> That's exactly what addRoot() is for.

If calling addRoot causing "performance problems" (or whatever other objections one may raise), we can look into improving its performance in various ways. But avoiding to call it in the first place is not a solution.


T

-- 
"Computer Science is no more about computers than astronomy is about telescopes." -- E.W. Dijkstra
September 26, 2021

On 9/25/21 10:13 PM, Walter Bright wrote:

>

On 9/25/2021 6:02 PM, Steven Schveighoffer wrote:

>

As I said, the optimizer is fighting you the entire way.

I'd reframe it as the user is trying to impose his own semantics on the optimizer :-)

Selecting semantics that enable aggressive optimizations is always a dance between user predictability and high performance. High performance usually wins.

Disabling dead assignment elimination would have a catastrophic deleterious effect on optimizations. A lot of template bloat would remain.

>

But I still am wishing for a simple "keep this alive" mechanism that doesn't add too much cruft, and is guaranteed to keep it alive.

That's exactly what addRoot() is for.

OK.

https://github.com/dlang/dlang.org/pull/3102

-Steve

September 26, 2021

On Friday, 24 September 2021 at 15:31:46 UTC, Steven Schveighoffer wrote:

>

On 9/24/21 11:25 AM, deadalnix wrote:

>

On Thursday, 23 September 2021 at 19:54:56 UTC, Steven Schveighoffer wrote:

>

You can. But wouldn't you prefer just pushing something on the stack?

Not really. If the optimizer can remove dead stack pushes, then program will become 2x slower instantly in addition of consuming more stack memory.

You think pushing on the stack is going be 2x slower than calling GC.addRoot?

-Steve

If the optimizer isn't free to optimize thing away from the stack, yes, it's pretty much guaranteed.

September 26, 2021

On Sunday, 26 September 2021 at 19:13:57 UTC, deadalnix wrote:

>

On Friday, 24 September 2021 at 15:31:46 UTC, Steven Schveighoffer wrote:

>

On 9/24/21 11:25 AM, deadalnix wrote:

>

On Thursday, 23 September 2021 at 19:54:56 UTC, Steven Schveighoffer wrote:

>

You can. But wouldn't you prefer just pushing something on the stack?

Not really. If the optimizer can remove dead stack pushes, then program will become 2x slower instantly in addition of consuming more stack memory.

You think pushing on the stack is going be 2x slower than calling GC.addRoot?

-Steve

If the optimizer isn't free to optimize thing away from the stack, yes, it's pretty much guaranteed.

Zen3 can actually promote a spill onto the stack into its physical register file.

September 26, 2021

On 9/26/21 3:13 PM, deadalnix wrote:

>

On Friday, 24 September 2021 at 15:31:46 UTC, Steven Schveighoffer wrote:

>

On 9/24/21 11:25 AM, deadalnix wrote:

>

On Thursday, 23 September 2021 at 19:54:56 UTC, Steven Schveighoffer wrote:

>

You can. But wouldn't you prefer just pushing something on the stack?

Not really. If the optimizer can remove dead stack pushes, then program will become 2x slower instantly in addition of consuming more stack memory.

You think pushing on the stack is going be 2x slower than calling GC.addRoot?

If the optimizer isn't free to optimize thing away from the stack, yes, it's pretty much guaranteed.

You realize what GC.addRoot does? It adds a pointer to a treap, through a virtual function call after taking a global lock. Pushing a stack item is going to be at least 10x faster, probably more compared to that. In the function call alone, there are a few stack pushes.

-Steve

September 26, 2021

On Sunday, 26 September 2021 at 20:39:48 UTC, Steven Schveighoffer wrote:

>

On 9/26/21 3:13 PM, deadalnix wrote:

>

On Friday, 24 September 2021 at 15:31:46 UTC, Steven Schveighoffer wrote:

>

On 9/24/21 11:25 AM, deadalnix wrote:

>

[...]

You think pushing on the stack is going be 2x slower than calling GC.addRoot?

If the optimizer isn't free to optimize thing away from the stack, yes, it's pretty much guaranteed.

You realize what GC.addRoot does? It adds a pointer to a treap, through a virtual function call after taking a global lock. Pushing a stack item is going to be at least 10x faster, probably more compared to that. In the function call alone, there are a few stack pushes.

-Steve

I think Amaury means that the code would be slower if the compiler is unable to register allocate at all vs. just for things returned by the allocator.

September 27, 2021

On Sunday, 26 September 2021 at 20:39:48 UTC, Steven Schveighoffer wrote:

>

You realize what GC.addRoot does? It adds a pointer to a treap, through a virtual function call after taking a global lock. Pushing a stack item is going to be at least 10x faster, probably more compared to that. In the function call alone, there are a few stack pushes.

-Steve

Sure, but it does so exclusively for one specific address. The optimizer would have to do so for every single thing that might be pointing to something that is owned by the GC.

However, one thing that could be done is to have an intrinsic to do so, so the optimizer knows exactly which references not to mess with. LLVM already has intrinsics for this at the IR level.

September 27, 2021

On 9/27/21 5:53 AM, deadalnix wrote:

>

On Sunday, 26 September 2021 at 20:39:48 UTC, Steven Schveighoffer wrote:

>

You realize what GC.addRoot does? It adds a pointer to a treap, through a virtual function call after taking a global lock. Pushing a stack item is going to be at least 10x faster, probably more compared to that. In the function call alone, there are a few stack pushes.

Sure, but it does so exclusively for one specific address. The optimizer would have to do so for every single thing that might be pointing to something that is owned by the GC.

However, one thing that could be done is to have an intrinsic to do so, so the optimizer knows exactly which references not to mess with. LLVM already has intrinsics for this at the IR level.

Yes, I'm not looking to have all dead stores kept, just ones that are important (as designated by the developer).

What I'm saying is, if you need a specific address saved for later in a function, wouldn't you prefer to store that on the stack rather than store it in the GC's roots? This would absolutely be opt-in, not automatic, as I don't see how we can do it automatically.

-Steve

September 27, 2021

On Monday, 27 September 2021 at 13:59:57 UTC, Steven Schveighoffer wrote:

>

What I'm saying is, if you need a specific address saved for later in a function, wouldn't you prefer to store that on the stack rather than store it in the GC's roots? This would absolutely be opt-in, not automatic, as I don't see how we can do it automatically.

If performance was important, you would want to allocate the object itself on the stack? The cost of GC allocation outweighs that of GC.addRoot by a lot.