On 2014-04-17 03:13:48 +0000, Manu via Digitalmars-d <digitalmars-d@puremagic.com> said:

Obviously, a critical part of ARC is the compilers ability to reduce
redundant inc/dec sequences. At which point your 'every time' assertion is
false. C++ can't do ARC, so it's not comparable.
With proper elimination, transferring ownership results in no cost, only
duplication/destruction, and those are moments where I've deliberately
committed to creation/destruction of an instance of something, at which
point I'm happy to pay for an inc/dec; creation/destruction are rarely
high-frequency operations.

You're right that transferring ownership does not cost with ARC. What costs you is return values and temporary local variables.

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:

        Object globalObject;

        Object getObject()
        {
                return globalObject; // implicit: retain(globalObject)
        }

        void main()
        {
                auto object = getObject();
                writeln(object);
                // implicit: release(object)
        }

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.

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.

Let's take this other example:

        Object globalObject;
        Object otherGlobalObject;

        void main()
        {
                auto object = globalObject; // implicit: retain(globalObject)
                foo(object);
                // implicit: release(object)
        }

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.

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.

--
Michel Fortin
michel.for...@michelf.ca
http://michelf.ca

Reply via email to