<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 17 April 2014 22:28, Michel Fortin via Digitalmars-d <span dir="ltr"><<a href="mailto:digitalmars-d@puremagic.com" target="_blank">digitalmars-d@puremagic.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On 2014-04-17 03:13:48 +0000, Manu via Digitalmars-d <<a href="mailto:digitalmars-d@puremagic.com" target="_blank">digitalmars-d@puremagic.com</a>> said:<br>

<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Obviously, a critical part of ARC is the compilers ability to reduce<br>
redundant inc/dec sequences. At which point your 'every time' assertion is<br>
false. C++ can't do ARC, so it's not comparable.<br>
With proper elimination, transferring ownership results in no cost, only<br>
duplication/destruction, and those are moments where I've deliberately<br>
committed to creation/destruction of an instance of something, at which<br>
point I'm happy to pay for an inc/dec; creation/destruction are rarely<br>
high-frequency operations.<br>
</blockquote>
<br>
You're right that transferring ownership does not cost with ARC. What costs you is return values and temporary local variables.<br></blockquote><div><br></div><div>Why would they cost? If a function receives a reference, it will equally release it on return. I don't see why a ref should be bumped to pass it to a function?</div>
<div>Return values I can see, because return values are effectively copying assignments. But if the assignment is to a local, then the close of scope implies a dec, which would again cancel out.<br></div><div><br></div><div>
<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
While it's nice to have a compiler that'll elide redundant retain/release pairs, function boundaries can often makes this difficult. Take this first example:<br>
<br>
        Object globalObject;<br>
<br>
        Object getObject()<br>
        {<br>
                return globalObject; // implicit: retain(globalObject)<br>
        }<br>
<br>
        void main()<br>
        {<br>
                auto object = getObject();<br>
                writeln(object);<br>
                // implicit: release(object)<br>
        }<br>
<br>
It might not be obvious, but here the getObject function *has to* increment the reference count by one before returning. There's no other convention that'll work because another implementation of getObject might return a temporary object. Then, at the end of main, globalObject's reference counter is decremented. Only if getObject gets inlined can the compiler detect the increment/decrement cycle is unnecessary.<br>
</blockquote><div><br></div><div>Well in most cases of accessors like this, it would inline properly. It's a fairly reliable rule that, if a function is not an inline candidate, it is probably also highly unlikely to appear in a hot loop.</div>
<div><br></div><div>I don't follow why it needs to retain before returning though. It would seem that it should retain upon assignment after returning (making it similar to the situation below). Nothing can interfere with the refcount before and after the function returns.</div>
<div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
But wait! If writeln isn't pure (and surely it isn't), then it might change the value of globalObject (you never know what's in Object.toString, right?), which will in turn release object. So main *has to* increment the reference counter if it wants to make sure its local variable object is valid until the end of the writeln call. Can't elide here.<br>

<br>
Let's take this other example:<br>
<br>
        Object globalObject;<br>
        Object otherGlobalObject;<br>
<br>
        void main()<br>
        {<br>
                auto object = globalObject; // implicit: retain(globalObject)<br>
                foo(object);<br>
                // implicit: release(object)<br>
        }<br>
<br>
Here you can elide the increment/decrement cycle *only if* foo is pure. If foo is not pure, then it might set another value to globalObject (you never know, right?), which will decrement the reference count and leave the "object" variable in main the sole owner of the object. Alternatively, if foo is not pure but instead gets inlined it might be provable that it does not touch globalObject, and elision might become a possibility.<br>
</blockquote><div><br></div><div>Sure, there is potential that certain bits of code between the retain/release can break the ability to eliminate the pair, but that's why I think D has an advantage here over other languages, like Obj-C for instance. D has so much more richness in the type system which can assist here. I'm pretty confident that D would offer much better results than existing implementations.</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
I think ARC needs to be practical without eliding of redundant calls. It's a good optimization, but a difficult one unless everything is inlined. Many such elisions that would appear to be safe at first glance aren't provably safe for the compiler because of function calls.</blockquote>
<div><br></div><div>I'm very familiar with this class of problem. I have spent much of my career dealing with precisely this class of problem.</div><div>__restrict addresses the exact same problem with raw pointers in C, and programmers understand the issue, and know how to work around it when it appears in hot loops.</div>
<div><br></div><div>D has some significant advantages that other ARC languages don't have though. D's module system makes inlining much more reliable than C/C++ for instance, pure is an important part of D, and people do use it liberally.</div>
</div></div></div>