Escaping the Tyranny of the GC: std.rcstring, first blood

Dmitry Olshansky via Digitalmars-d digitalmars-d at puremagic.com
Sun Sep 28 03:40:23 PDT 2014


28-Sep-2014 00:16, Andrei Alexandrescu пишет:
> On 9/27/14, 12:50 PM, Dmitry Olshansky wrote:
>> 27-Sep-2014 23:14, Andrei Alexandrescu пишет:
>>> On 9/27/14, 2:38 AM, Dmitry Olshansky wrote:
>>>>
>>>> Okay it serves no good for me to make these tiny comments while on the
>>>> go.
>>>>
>>>> As usual, structs are value types, so this feature can be mis-used, no
>>>> two thoughts abouts it. It may need a bit of improvement in
>>>> user-friendliness, compiler may help there by auto-detecting common
>>>> misuse.
>>>
>>> I still don't understand what "this feature" is after reading your long
>>> post twice.

Must be my fault, I'll try once again.

>>>
>>> So structs are still value types and you replace postblit/destroy with
>>> calls to opInc/opDec? That's it? How does this enable anything more
>>> interesting than ctors/dtors?
>>>
>>
>> Compiler is aware that opInc and opDec are indeed ref-countinng ops,
>> meaning that opInc + opDec = no op. I claim that this is enough to get
>> "ARC" going.
>
> You give marginal details but still don't describe the thing. When are
> they called and what do they have that ctors/dtors/postblit don't?

The key point is that the change is small, that's why (maybe) it's hard 
to grasp. The whole thing is a bit of lowering and a "hint" to the 
compiler. It reuses the same mechanism that structs already have.

Okay a few examples of lowering to get things going:

http://dpaste.dzfl.pl/3722d9d70937

(note I think there could be better lowerings and simpler set of 
primitives to bootstrap ARC-ed type)

Now why would we need this trivial lowering?

Typical postblit can be anything unless the compiler has full source 
code, dtor can be anything as well.

With opInc/opDec compiler generates postblit/dtor on his own, in doing 
so it decorates user-defined dtor that actually clears resources.

What being in control gives to the compiler:

1. Compiler always has the source of generated parts, so they can be 
inlined (and should be)

2. Can do typical algebra optimization on opInc/opDec, no matter what's 
inside opInc and opDec (this is a contract between programmer and compiler).
	 e.g opInc(10) followed by OpDec(1) is opInc(9)

3. Also opInc and opDec do not alter object in any capacity, nor are 
they affected by any method calls on this object (another contract)

4. 1 + 2 = win, as by inlining postblits/dtors we expose opInc/opDecs to 
the optimizer pass which the would fold them using basic algebra 
optimizations.

Motivating example:

struct RC(T) { ... } //implemented with or without proposed feature

void foo(RC!int arg); // no source available, sorry;)

void bar(RC!int my)
{	
	foo(my);
	writeln("Bar: ", *my);	
}

which is the same as

{
	{
		auto __tmp = my; // a postblit
		foo(__tmp);
	}//dtor
	writeln("Bar: ", *my);
}

And assuming the same names for inc/dec of count and no exceptions, can 
be simplified back to:
{
	{
		auto __tmp = my;
		my.opInc(1); // or __tmp.opInc() it's the same count
		foo(__tmp);
		if(my.opDec(1)) my.__dtor();
	}
	writeln("Bar: ", *my);
}

Ideally with assumptions I stated above will look like this:
{
	{
		auto __tmp = my;
		foo(__tmp);
		if(my.opDec(0)) my.__dtor();
	}
	writeln("Bar: ", *my);
}

Now here if compiler can optimize ref-count operations completely on his 
own without assumptions stated above then we are done and no special 
opInc/opDec required.

I bet my hat it can't for the simple reason that all bets are off if any 
of the following is happening:
1. No source available thus e.g. postblit is opaque call
2. Not "everything" in between opInc/opDec can be inlined impairing 
optimizer (since 'my' is passed to foo, it may be modified)

I might be wrong though - Walter?

>
> FWIW the language always "understands" when to elide postblit/dtor calls.

Yes, which immediately gives us part of ARC advantages such as NRVO 
elides ref-count bump. The other part comes from batching up multiple 
incs/decs to one call and eliminating redundant pairs.


I might be wrong but RCObject proposal is harder to construct then this 
"feather-weight ARC" as it would have to "force" the same lifetime 
management as we have for structs on some class instances that never had 
it before!

That being said I think it's valuable to have RCObject and exceptions 
being ref-counted.

-- 
Dmitry Olshansky


More information about the Digitalmars-d mailing list