Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Vtables organization

13 views
Skip to first unread message

Leopold Toetsch

unread,
Jan 16, 2004, 5:53:55 AM1/16/04
to P6I
PMCs use Vtables for almost all their functionality *and* for stuff that
in Perl5 term is "magic" (or they should AFAIK).

E.g. setting the "_ro" property of a PMC (that supports it[1]) swaps in
the Const$PMC vtable, where all vtable methods that would change the PMC
thrown an exception.
Or: setting a PMC shared, whould swap in a vtable, that locks e.g.
internal aggregate state on access. That is a non-shared PMC doesn't
suffer of any locking slowdown.
Tieing will very likely swap in just another vtable and so on.

The questions are:
- Where and how should we store these vtables?
- Are these PMC variants distinct types (with class_enum and name)
- Or are these sub_types (BTW what is vtable->subtype)? E.g. hanging off
from the "main" vtable?

Comments welcome,
leo


[1] This still needs more work: Real constant PMCs are allocated in a
separate arena which isn't scanned during DOD, *but* all items, that the
PMC may refer too have to be constant too, including Buffers it may use.
But swapping in the vtable is working.

Dan Sugalski

unread,
Jan 16, 2004, 2:12:33 PM1/16/04
to Leopold Toetsch, P6I
At 11:53 AM +0100 1/16/04, Leopold Toetsch wrote:
>PMCs use Vtables for almost all their functionality *and* for stuff
>that in Perl5 term is "magic" (or they should AFAIK).
>
>E.g. setting the "_ro" property of a PMC (that supports it[1]) swaps
>in the Const$PMC vtable, where all vtable methods that would change
>the PMC thrown an exception.
>Or: setting a PMC shared, whould swap in a vtable, that locks e.g.
>internal aggregate state on access. That is a non-shared PMC doesn't
>suffer of any locking slowdown.
>Tieing will very likely swap in just another vtable and so on.
>
>The questions are:
>- Where and how should we store these vtables?
>- Are these PMC variants distinct types (with class_enum and name)
>- Or are these sub_types (BTW what is vtable->subtype)? E.g. hanging
>off from the "main" vtable?

I was going to go on about a few ways to do this, but after I did I
realized that only one option is viable. So, let's try this on for
size:

Vtables are chained. That means each vtable has a link to the next in
the chain. It *also* means that each call into a vtable function has
to pass in a pointer to the vtable the call came from so calls can be
delegated properly. If we don't want this to suck down huge amounts
of memory it also means that the vtable needs to be split into a
vtable header and vtable function table body.

Downside there is that we have an extra parameter (somewhat pricey)
to all the vtable functions.
--
Dan

--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk

Benjamin K. Stuhl

unread,
Jan 16, 2004, 3:33:20 PM1/16/04
to Dan Sugalski, Leopold Toetsch, P6I
Dan Sugalski wrote:
> I was going to go on about a few ways to do this, but after I did I
> realized that only one option is viable. So, let's try this on for size:
>
> Vtables are chained. That means each vtable has a link to the next in
> the chain. It *also* means that each call into a vtable function has to
> pass in a pointer to the vtable the call came from so calls can be
> delegated properly. If we don't want this to suck down huge amounts of
> memory it also means that the vtable needs to be split into a vtable
> header and vtable function table body.
>
> Downside there is that we have an extra parameter (somewhat pricey) to
> all the vtable functions.

This is sort of icky. What about dynamically constructing vtables and caching
them? We'd probably need some sort of mangling scheme, but it wouldn't be too
hard; maybe just the ordered list of packages separated by NULs or something.
Then again, though, any sort of stacking runs into data collisions in the PMC.
So maybe we just need lots of vtable types. Ties should probably be their own
vtable (or one each for tied scalars, hashes, and arrays); read-only-ification
should maybe be a special case that actually does dynamically create (and
cache, if need be) vtables, replacing ->set_* and ->morph with functions
that throw an exception. For thread-sharing: proxies seem to be standard? What
other attributes or traits or whatever actually expect to still be able to
directly access the underlying variable? (Ties and overloads, for instance,
_don't_ - they have to provide their own backing storage.)

I suppose what I'm getting down to is that the intelligence should just be
in the compile-time PMC class generator, not dynamic in the runtime. Except
maybe for read-only-ification.

-- BKS

Dan Sugalski

unread,
Jan 16, 2004, 3:43:08 PM1/16/04
to Benjamin K. Stuhl, Leopold Toetsch, P6I
At 3:33 PM -0500 1/16/04, Benjamin K. Stuhl wrote:
>Dan Sugalski wrote:
>>I was going to go on about a few ways to do this, but after I did I
>>realized that only one option is viable. So, let's try this on for
>>size:
>>
>>Vtables are chained. That means each vtable has a link to the next
>>in the chain. It *also* means that each call into a vtable function
>>has to pass in a pointer to the vtable the call came from so calls
>>can be delegated properly. If we don't want this to suck down huge
>>amounts of memory it also means that the vtable needs to be split
>>into a vtable header and vtable function table body.
>>
>>Downside there is that we have an extra parameter (somewhat pricey)
>>to all the vtable functions.
>
>This is sort of icky. What about dynamically constructing vtables and caching
>them?

How would one wrap a vtable, then? If you do this, there's no way to
call back into the original (or earlier wrapping) entry.

Benjamin K. Stuhl

unread,
Jan 18, 2004, 8:27:15 PM1/18/04
to Dan Sugalski, P6I
Thusly did Dan Sugalski inscribe:

> At 3:33 PM -0500 1/16/04, Benjamin K. Stuhl wrote:
>
>> Dan Sugalski wrote:
>>
>>> I was going to go on about a few ways to do this, but after I did I
>>> realized that only one option is viable. So, let's try this on for size:
>>>
>>> Vtables are chained. That means each vtable has a link to the next in
>>> the chain. It *also* means that each call into a vtable function has
>>> to pass in a pointer to the vtable the call came from so calls can be
>>> delegated properly. If we don't want this to suck down huge amounts
>>> of memory it also means that the vtable needs to be split into a
>>> vtable header and vtable function table body.
>>>
>>> Downside there is that we have an extra parameter (somewhat pricey)
>>> to all the vtable functions.
>>
>>
>> This is sort of icky. What about dynamically constructing vtables and
>> caching
>> them?
>
>
> How would one wrap a vtable, then? If you do this, there's no way to
> call back into the original (or earlier wrapping) entry.

Other than the special case of :readonly, can you give me an example of when
you'd need to, rather than simply writing a PMC class that inherits from some
base? I'm having trouble thinking of an example of when you'd want to be able
to do that... After all, since operator overloading and tying effectively
_replace_ the builtin operations, what more does one need?

-- BKS

Luke Palmer

unread,
Jan 18, 2004, 9:10:10 PM1/18/04
to Benjamin K. Stuhl, Dan Sugalski, P6I
Benjamin K. Stuhl writes:
> Other than the special case of :readonly, can you give me an example
> of when you'd need to, rather than simply writing a PMC class that
> inherits from some base? I'm having trouble thinking of an example of
> when you'd want to be able to do that... After all, since operator
> overloading and tying effectively _replace_ the builtin operations,
> what more does one need?

Well, other than Constant, we need to be able to put locking on shared
PMCs. We'd like to add a debug trace to a PMC. We could even make any
kind of PMC sync itself with an external source on access, though that's
a bit of a pathological case.

Indeed, all of this can be done, however, by subclassing Ref. I think
the reason this isn't sufficient is that we want to change the actual
PMC into this new variant when it is possibly already in existence.
Like my C<supplant> was evilly trying to do. Perhaps there's a way to
get that working safely...

Luke

> -- BKS

Benjamin K. Stuhl

unread,
Jan 19, 2004, 8:37:05 AM1/19/04
to Luke Palmer, Dan Sugalski, P6I
Luke Palmer wrote:

The issue is that the PMC's original vtable assumes (and should, IMHO be
_able_ to assume) that it has total control over the PMC's data, so there is
nowhere in the PMC to put a lock or a handle to an external source or
anything. So you'd either need a Ref of some sort anyway or a global lookup
table, which seems to be an even worse idea.

Debug tracing, though, is a good question... I hate to pass an extra pointer
to every vtable call just for that, though...

-- BKS

Dan Sugalski

unread,
Jan 22, 2004, 2:51:33 PM1/22/04
to Benjamin K. Stuhl, Luke Palmer, P6I
At 8:37 AM -0500 1/19/04, Benjamin K. Stuhl wrote:
>Luke Palmer wrote:
>
>>Benjamin K. Stuhl writes:
>>
>>>Other than the special case of :readonly, can you give me an example
>>>of when you'd need to, rather than simply writing a PMC class that
>>>inherits from some base? I'm having trouble thinking of an example of
>>>when you'd want to be able to do that... After all, since operator
>>>overloading and tying effectively _replace_ the builtin operations,
>>>what more does one need?
>>
>>
>>Well, other than Constant, we need to be able to put locking on shared
>>PMCs. We'd like to add a debug trace to a PMC. We could even make any
>>kind of PMC sync itself with an external source on access, though that's
>>a bit of a pathological case.
>>
>>Indeed, all of this can be done, however, by subclassing Ref. I think
>>the reason this isn't sufficient is that we want to change the actual
>>PMC into this new variant when it is possibly already in existence.
>>Like my C<supplant> was evilly trying to do. Perhaps there's a way to
>>get that working safely...
>
>The issue is that the PMC's original vtable assumes (and should,
>IMHO be _able_ to assume) that it has total control over the PMC's
>data,

Well... I think I'll disagree here. The *class* vtable can assume
this. However that doesn't mean that any random vtable function can.

In addition to the thread autolocking front end and debugging front
end vtable functions, both of which can be generic, there's the
potential for tracing and auditing front end functions, input data
massaging wrappers, and all manner of Truly Evil front (and back) end
wrappers that don't need to actually access the guts of the PMC, but
can instead rely on the other vtable functions to get the information
that they need to operate.

Not that this necessarily mandates passing in the vtable pointer to
the functions, but the uses aren't exactly marginal.

Benjamin K. Stuhl

unread,
Jan 22, 2004, 10:37:13 PM1/22/04
to Dan Sugalski, Luke Palmer, P6I
Dan Sugalski wrote:
[sniping to reduce verbiage]

>> The issue is that the PMC's original vtable assumes (and should, IMHO
>> be _able_ to assume) that it has total control over the PMC's data,
>
>
> Well... I think I'll disagree here. The *class* vtable can assume this.
> However that doesn't mean that any random vtable function can.

Yes, that's what I meant by the original vtable. :-)

> In addition to the thread autolocking front end and debugging front end
> vtable functions, both of which can be generic, there's the potential
> for tracing and auditing front end functions, input data massaging
> wrappers, and all manner of Truly Evil front (and back) end wrappers
> that don't need to actually access the guts of the PMC, but can instead
> rely on the other vtable functions to get the information that they need
> to operate.
>
> Not that this necessarily mandates passing in the vtable pointer to the
> functions, but the uses aren't exactly marginal.

Going back to the idea of generating these vtables on the fly (and caching
them): each instance of a vtable gets a void* closure in the vtable itself,
so at a certain expense in extra vtables, one could hang a structure off
of that that includes a pointer to the original vtable. E.g. (pseudo-code)

if (we don't have a tracing PerlInt in our cache) {
TracePerlIntVtable = clone_vtable(interp, TraceVtable);
vtable_set_data(interp, TracePerlIntVtable, PerlIntVtable);
cache(TracePerlIntVtable);
}

TraceVtable::get_number(INTERP, self) {
FLOATVAL f;
// I don't have the headers in front of me to get the right field names...
VTABLE *my_vtbl = self->vtable;
VTABLE *old_vtbl = my_vtbl->private;

self->vtable = old_vtbl;
f = self->vtable->get_number(interp, self);
TRACE("%p->get_number() = %f", self, f);
self->vtable = my_vtbl;

return f;
}

With slightly more complicated closures, most of the listed uses can use this
method. It's memory-heavy, but not too bad if we need a fair number of PMCs of
each wrapped type and the cost gets amortized over them. And this does save an
argument to every PMC function and just makes the wrapping classes pay. Where
it's suboptimal is for classes like autolocking that really want a closure of
their own on every PMC. But then again, that would again probably get into
issues of data collisions (except for locking, which has its own reserved area).

Just some thoughts.

-- BKS

Dan Sugalski

unread,
Jan 23, 2004, 9:27:12 AM1/23/04
to Benjamin K. Stuhl, Luke Palmer, P6I
At 10:37 PM -0500 1/22/04, Benjamin K. Stuhl wrote:
>Dan Sugalski wrote:
>>In addition to the thread autolocking front end and debugging front
>>end vtable functions, both of which can be generic, there's the
>>potential for tracing and auditing front end functions, input data
>>massaging wrappers, and all manner of Truly Evil front (and back)
>>end wrappers that don't need to actually access the guts of the
>>PMC, but can instead rely on the other vtable functions to get the
>>information that they need to operate.
>>
>>Not that this necessarily mandates passing in the vtable pointer to
>>the functions, but the uses aren't exactly marginal.
>
>Going back to the idea of generating these vtables on the fly (and
>caching them): each instance of a vtable gets a void* closure in the
>vtable itself,
>so at a certain expense in extra vtables, one could hang a structure off
>of that that includes a pointer to the original vtable.

Which I thought of, but that only allows for one layer of
indirection, and doesn't allow the original vtable to hang any data
off its vtable data pointer. (Which exists, and is there for that
very reason) If you have two or three layers of vtable functions
installed then it becomes difficult and time-consuming to find the
right data pointer--if you allow the same vtable to be layered in
multiple times (and no, I don't know why you'd want to) then it
becomes essentially impossible.

Unfortunately the layers need to stay separate with separate data
attached, so if we allow layering and don't forbid the same layer in
there twice we have to pass in the pointer to the vtable actually
being called into, so the vtable functions can find the
layer-specific data.

Benjamin Kojm Stuhl

unread,
Jan 23, 2004, 10:54:42 AM1/23/04
to Dan Sugalski, Benjamin K. Stuhl, Luke Palmer, P6I

Well, that was why I had my suggested sample pseudocode restore the previous vtable pointer before calling down to the next function (and put itself back when that's done). This means that every vtable function knows that PMC->vtable is the vtable _for the current vtable function_, and so any vtable function can be confident that it is accessing the corect layer-specific data. It's a bit more complexity and 2 extra assignments in the wrapper vtable functions versus an extra parameter to _all_ vtable functions.

-- BKS

Dan Sugalski

unread,
Jan 23, 2004, 12:37:28 PM1/23/04
to Benjamin Kojm Stuhl, Luke Palmer, P6I
At 10:54 AM -0500 1/23/04, Benjamin Kojm Stuhl wrote:
>Well, that was why I had my suggested sample pseudocode restore the
>previous vtable pointer before calling down to the next function
>(and put itself back when that's done).

That has reentrancy issues, unfortunately. Potentially threading and
DOD issues as well. Keeping the vtable reasonably immutable's in our
best interests.

Matt Fowles

unread,
Jan 25, 2004, 6:01:53 PM1/25/04
to Dan Sugalski, P6I
All~

Of late it seems that everybody has been throwing around their own
little homegrown benchmarks to support their points. But many people
frequently point out that these benchmarks are flawed on one way or another.

I suggest that we add a benchmark/ subdirectory and create a canonical
suite of benchmarks that exercise things well (and hopefully fully).
Then we can all post relative times for runs on this benchmark suite,
and we will know exactly what is being tested and how valid it is.

Matt

Gordon Henriksen

unread,
Jan 25, 2004, 6:38:02 PM1/25/04
to Matt_...@softhome.net, Dan Sugalski, P6I

Well, there's already examples/benchmarks. If those programs are not at
all realistic, then more realistic benchmarks should be added.

Would be nice if there were a convenient way to run the lot of them and
collect the timing information, though.

Gordon Henriksen
mali...@mac.com

Luke Palmer

unread,
Jan 25, 2004, 6:14:32 PM1/25/04
to Matt Fowles, Dan Sugalski, P6I

Like, for example, examples/benchmarks ?

It's quite difficult to create benchmarks that test *everything*. But
any time someone posts a good benchmark, it really should go in here.
Hopefully with some documentation describing what it tests.

Luke

Michael Scott

unread,
Jan 25, 2004, 6:31:52 PM1/25/04
to perl6-i...@perl.org
A few notes on the benchmarks can be found/added here

http://www.vendian.org/parrot/wiki/bin/view.cgi/Main/
ParrotDistributionExamples#benchmarking

Mike

Leopold Toetsch

unread,
Jan 25, 2004, 7:14:41 PM1/25/04
to Gordon Henriksen, perl6-i...@perl.org
Gordon Henriksen <mali...@mac.com> wrote:

> Would be nice if there were a convenient way to run the lot of them

> collect the timing information, though.

Yep. That would be really great. That is: have per platform numbers over
time (correlated to patches) about performance of current and a lot of
*TODO* benchmarks.

> =97

a!

> Gordon Henriksen

> --Apple-Mail-2-129918875--

[ Still sucks - SCNR ]

leo

Dan Sugalski

unread,
Jan 29, 2004, 4:50:33 AM1/29/04
to Gordon Henriksen, Matt_...@softhome.net, P6I

Sounds like a good plan. I've thrown an item into the todo list :)

0 new messages