Thursday 1 February 2018

Optimizing ARC the hard way?

The problem

In a recent blog post, Dalija Prasnikar talks about how hard it would be to optimize ARC code, because most ARC object references will be passed by value, but not as const. Passing as const would eliminate the need for an implicit __ObjAddRef() call each time the object is passed (and an __ObjRelease() call when the routine with the parameter ends).

OK, ARC (automatic reference counting) has been used for many types already, most of all strings, dynamic arrays and interfaces. Code passing strings and dynarrays is generally optimized already, by specifying such parameters as const. There are probably only a few exceptions. It is also customary to do it with interfaces, and the few exceptions probably don't make a big difference in performance. But it is far from customary to do this for objects. Until ARC, it didn't matter if you passed an object as const or not. But these days, in ARC, it can make a big difference. And objects are passed around a lot, e.g. the Sender parameter of events, etc.

Note that most runtime and FMX code doesn't use const to pass objects either, nor do any of the third parties. Dalija notes that it is not hard to add const to each object parameter, but that it would break huge amounts of code, not only Embarcadero's, but everyone's.

Switch?

I read that and have been thinking about it a little. I first thought of introducing a switch (sound familiar?) in the new ARC compilers, that would make const optional and reduce compiler complaints about interface changes, to make this transition easier. But that would still require a huge amount of code changes, even if it could perhaps be done at a slower pace.

Const by default?

But then I thought of something else. If an object is passed as call-by-value, to a method or function, inside that method or function, you very seldom change the reference by assigning a new object to it. In other words, inside such a method or function, you hardly ever do something like:

    ObjectParam.Free; // necessary in non-ARC, optional in ARC
    ObjectParam := TSomeObject.Create;

Yes, you often change the state of the object by setting properties or by calling methods, but that can be done on a const reference as well. Const means you can't change the reference, not that you can't change the state of the object it refers to.

For almost all practical purposes, such objects passed can be treated as if they were passed as const already. So it would perhaps make sense, in a new version of the ARC compilers, to treat every pass-by-value as const. This would make the code compatible with the non-ARC compilers, while still avoiding a truckload of reference counting calls. This would probably optimize ARC code quite a lot. Of course code already using const would be compatible too. And it would not apply to code using var or out, only to plain pass-by-value object reference parameters.

And the very few methods that do actually re-use a parameter like above should simply be rewritten to use a (new) local variable for the new object. I doubt there is lot of code that does this anyway, so this change would not break a lot of code at all.

So making all passed-by-value objects const by default would probably break very little code, and optimize a lot of ARC code. It would not affect non-ARC code.

As for the old compilers: they would remain non-optimized, but their code would not have to be changed. A simple recompile (and the occasional change) would suffice.

I'd love to hear about your thoughts.