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