Newbie initial comments on D language - scope

Michel Fortin michel.fortin at michelf.com
Tue Feb 5 05:18:06 PST 2008


On 2008-02-05 04:09:31 -0500, "Janice Caron" <caron800 at googlemail.com> said:

> On 05/02/2008, Michel Fortin <michel.fortin at michelf.com> wrote:
>> Basically, you need to:
>> 
>> 1. Load the object's pointer in a register
>> 2. Load the "scope" flag from memory by offseting the object's pointer
>> 3. Branch depending on that flag:
>> a. if not scope, go to 4.
>> b. if scope, do whatever is needed to increment the reference count
>> atomically, then go to 4
>> 4. Write the pointer to its new location.
> 
> Not quite.

Well, the algorithm above checks for the presence of a scope flag on 
the object to only reference-count objects whith are flagged scope at 
runtime. And I didn't bother elaborate the code in caes 4.b., doing the 
actual reference counting, because it's not that important.

But now I realise I forgot to check for the scope flag on the second 
object and failed to check for null pointers. Basically, what I should 
have written is this (now in D with a special atomic statement):

	A a;
	B b;
	if (a && a.isScope)
	{
		debug if (a.refCount == 0)
			throw new RefCountException();
		atomic { --a.refCount; }
		if (a.refCount == 0)
			delete a;
	}
	if (b && b.isScope)
	{
		atomic { ++b.refCount; }
	}
	a = b;

As you can see, there are four conditions that *must* be evaluated 
whether or not the object is scope at runtime (a, a.isScope, b, 
b.isScope), two of them requiring dereferencing the object and loading 
some of its memory (for each object's scope flag). This is the real 
drawback in Edward's proposal as the rest of the code wouldn't be 
executed if the isScope flag is set to false. Since branching is often 
an expensive operation on processors because of the instruction 
pipeline, doing the assignment with a runtime isScope flag would be 
much slower.

Janice, your code demonstrate how to do reference counting, but I don't 
see a scope flag anywhere.


> That's the situation currently. Assignment of classes is very fast.
> /But/, this is what it would change to under your scheme:
> 
>     A* pa;
>     B* pb;
>     if (pa->refCount)
>     {
>         if (pb->refCount)
>         {

Shouldn't you check for null too? That'd be:

	if (pa && pa->refCount)
	{
		if (pb && pb->refCount)
		{

>             atomic { if (--pb->refCount == 0) delete pb; }
>             atomic { ++pa->refCount; }
>             pa = pb;
>         }
>         else
>         {
>             throw new RefCountException();
>         }
>     }
>     else
>     {
>         if (pb->refCount)
>         {

And again:

		if (pb && pb->refCount)
		{

>              throw new RefCountException();
>         }
>         else
>         {
>             pa = pb;
>         }
>     }
> 
> And that's just for /ordinary/ assignment. That looks like a
> phenomenal overhead to me.
> 
> Now just /imagine/ how complicated it gets
> if you've overloaded opAssign in various complicated ways. (e.g
> structs assigned from classes, classes assigned from structs, etc.)
> 
> I think I'd rather not have that overhead added to every single class
> assignment.

Well, that's the idea of a reference-counted object: you add this 
overhead to make sure the object is destroyed as soon as it can. It can 
be usefull in many cases, when a class holds a scarse resource for 
instance. But you're completly right to not want this overhead for 
regular objects, and that's why an object being reference-counted must 
be a compile-time property, not decided by a runtime-evaluatable flag.

-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/




More information about the Digitalmars-d mailing list