external c pointers

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

external c pointers

Ian Upright-2

Hi there.. we are getting completely slaughtered when we scale up external interfacing to C/C++ using subclasses of ExternalInterface, and it’s due to our pointers turning into LargePositiveIntegers instead of SmallIntegers..  One possibility may be to allocate a number of handles in lower memory, and this could have indirect pointers to the real address (potentially higher)..  That would be quite involved and a big hack, and due to the additional indirection, would be slightly more costly.  However, the external interfacing for us is up to 40x slower than it is when it’s using SmallInts..  Not too surprisingly, our app can be up to 5-20x slower than when it resides in lower memory– a real killer.

 

What might be the best way to remedy this?  Patch the VM somehow so something like a CPointer can have a 32-bit pointer directly embedded in it?

And then have some kind of lightning fast hash primitive that calculates it based on the value of the (embedded) 32-bit CPointer?

 

I noticed that the hash for Large integers doesn’t seem to be a primitive.. I wonder if this is part of the extremely poor performance.

When it comes to speeding this up, I’m all ears.. as it seems there is no easy solution..?!

 

Ian

 

Reply | Threaded
Open this post in threaded view
|

Re: external c pointers

Alan Knight-2
Not really my area, but I would think the obvious thing to do is to represent the pointer data as UninterpretedBytes or something of that nature. In Security, there is a 32-bit register class, that splits the value up into two small integers, but for passing to C, I would think a byte array representation would be more likely to be directly acceptable to the VM. But it might well require VM changes anyway, I don't know.

I wouldn't think that just calling out to C would require hashing lots of C pointers, so that seems like a separate issue, and that perhaps you're keeping dictionaries of things resulting from callouts. Apart from tweaking the hash function, an obvious possibility is to wrap those pointers in other objects, and to have those objects cache the computed hash value (I assume that the hash of C pointers is unlikely to change).

At 09:30 PM 05/05/2006, Ian Upright wrote:
Hi there.. we are getting completely slaughtered when we scale up external interfacing to C/C++ using subclasses of ExternalInterface, and it’s due to our pointers turning into LargePositiveIntegers instead of SmallIntegers..  One possibility may be to allocate a number of handles in lower memory, and this could have indirect pointers to the real address (potentially higher)..  That would be quite involved and a big hack, and due to the additional indirection, would be slightly more costly.  However, the external interfacing for us is up to 40x slower than it is when it’s using SmallInts..  Not too surprisingly, our app can be up to 5-20x slower than when it resides in lower memory– a real killer.
 
What might be the best way to remedy this?  Patch the VM somehow so something like a CPointer can have a 32-bit pointer directly embedded in it?
And then have some kind of lightning fast hash primitive that calculates it based on the value of the (embedded) 32-bit CPointer?
 
I noticed that the hash for Large integers doesn’t seem to be a primitive.. I wonder if this is part of the extremely poor performance.
When it comes to speeding this up, I’m all ears.. as it seems there is no easy solution..?!
 
Ian
 

--
Alan Knight [|], Cincom Smalltalk Development

"The Static Typing Philosophy: Make it fast. Make it right. Make it run." - Niall Ross
Reply | Threaded
Open this post in threaded view
|

RE: external c pointers

Ian Upright-2
In reply to this post by Ian Upright-2

Hi Travis.. You may be brilliant.  In an earlier version of my code, I was not using SWIG or DLL C Connect, and I was using the Squeak primitives plugin instead.  In so doing, I was using subclasses of ByteArray to pass in the pointers, and did not run into these performance problems.  It wasn’t obvious to me that you could pass in a byte array through the DLL-C Connect mechanisim like you describe. (passing the 4-byte values as appearing like a pointer).   I’ll do some tests and see if you’re right!  If so this could save some serious headache and be relatively easy to implement – and perhaps make it a general part of the SWIG mechanisim itself.  The hash of ByteArray looks frightening.  I wonder if I should either double the size of the ByteArray to cache the hash, or if I should make an external call for that, too.  (probably the latter)

One thing I don’t like about this is the inability to add instance variables to the ByteArray, and I might also need to add an additional byte for a bit-flag to indicate if the pointer owns it’s memory..  Even still, performance in this case is far more important than ugliness.  :-)

 

Thanks muchly, Ian

 


From: Travis Griggs [mailto:[hidden email]]
Sent: Thursday, May 11, 2006 9:47 AM
To: Ian Upright; vwnc
Subject: Re: external c pointers

 

On May 5, 2006, at 18:30, Ian Upright wrote:

Hi there.. we are getting completely slaughtered when we scale up external interfacing to C/C++ using subclasses of ExternalInterface, and it’s due to our pointers turning into LargePositiveIntegers instead of SmallIntegers..  One possibility may be to allocate a number of handles in lower memory, and this could have indirect pointers to the real address (potentially higher)..  That would be quite involved and a big hack, and due to the additional indirection, would be slightly more costly.  However, the external interfacing for us is up to 40x slower than it is when it’s using SmallInts..  Not too surprisingly, our app can be up to 5-20x slower than when it resides in lower memory– a real killer.

 

What might be the best way to remedy this?  Patch the VM somehow so something like a CPointer can have a 32-bit pointer directly embedded in it?

And then have some kind of lightning fast hash primitive that calculates it based on the value of the (embedded) 32-bit CPointer?

 

I noticed that the hash for Large integers doesn’t seem to be a primitive.. I wonder if this is part of the extremely poor performance.

When it comes to speeding this up, I’m all ears.. as it seems there is no easy solution..?!

Ian,

 

So you're saying that you're getting killed by marshalling. Do you actually need to represent the value as an integer? Or is it just a pointer from C, which you're going to pass back as a pointer. IOW, do you ever really need to do integer like math things with it?

 

Here's how I might try to solve it. Make a subclass of UninterpretedBytes (or ByteArray). Define "new" to be "new: 4". You can actually fetch the integer value from it when you need to with unsignedLongAt: 1. Change your <C: definitions> so that when you pass this type it is <unsigned char _oopref * arg>.

 

Don't know how much diff it would make. It would be interesting to know.

 

--

Travis Griggs

Objologist

"Conference calls: the appearance of doing work without the substance thereof" - Bruce Boyer



 




DISCLAIMER: This email is bound by the terms and conditions
described at
http://www.key.net/disclaimer.htm

Reply | Threaded
Open this post in threaded view
|

RE: external c pointers

Ian Upright-2
In reply to this post by Ian Upright-2

Hi Eliot..  We went through some great pain to create a “handle allocator” in C.  Upon startup, it allocates a large number of these handles in lower C virtual memory, that fits within the SmallInteger range.  Then through great pain, every call in C is gone through this handle, which then redirects to the real C object in question, which may reside at a higher up, LargeInteger memory location.  However, this LargeInteger value is never exposed to the Smalltalk VM.  Though more pain, these handles are recycled when they are no longer referenced (via finalization), to ensure that we have enough in lower memory.  The addition of handles and management of them creates an additional layer of indirection that you would think would impact performance negatively.  However, all this refactoring has resulted in tremendous performance gains in our application.  It’s extremely ugly but now performs much better.

 

In the cases we are experiencing, perhaps 25% or more of the pointers are LargeIntegers, so it is not all that infrequent.  We are often running tight loops or large loops making large numbers of extremely short and fast calls.  For example, we have set iterators, implement the collection protocol, and so on, with these kinds of primitives.  Our sets and objects reside in C memory, not in Smalltalk memory, so we are pushing things almost as hard as it gets, in regards to external calls, etc.

 

I’m sure the creation of the LargeIntegers stresses the garbage collector quite a bit..  And as a guess, this additional stress on the garbage collector and creation of extra objects may blow the CPU caches further and cause more impact on performance.  I’m also not sure how efficient the creation of the LargeIntegers is, from an external pointer.  The hash function of the LargeInteger may also negatively impact performance as well.

 

Ian

 

 


From: Eliot Miranda [mailto:[hidden email]]
Sent: Friday, May 12, 2006 5:43 PM
To: [hidden email]
Cc: Alan Knight; [hidden email]
Subject: Re: external c pointers

 

via Alan Knight, At 09:30 PM 05/05/2006, Ian Upright wrote:

Hi there.. we are getting completely slaughtered when we scale up external interfacing to C/C++ using subclasses of ExternalInterface, and it’s due to our pointers turning into LargePositiveIntegers instead of SmallIntegers..  One possibility may be to allocate a number of handles in lower memory, and this could have indirect pointers to the real address (potentially higher)..  That would be quite involved and a big hack, and due to the additional indirection, would be slightly more costly.  However, the external interfacing for us is up to 40x slower than it is when it’s using SmallInts..  Not too surprisingly, our app can be up to 5-20x slower than when it resides in lower memory– a real killer.
 
What might be the best way to remedy this?  Patch the VM somehow so something like a CPointer can have a 32-bit pointer directly embedded in it?
And then have some kind of lightning fast hash primitive that calculates it based on the value of the (embedded) 32-bit CPointer?
 
I noticed that the hash for Large integers doesn’t seem to be a primitive.. I wonder if this is part of the extremely poor performance.
When it comes to speeding this up, I’m all ears.. as it seems there is no easy solution..?!
 
Ian
 

 

Ian,

     I can understand that large interger allocation could hit one hard if the C code were returning addresses that missed the SmallInteger range on the vast majority of calls and if the application did nothing but collect these results.  But I find it hard to imagine a real application that would be able to suffer this cost.  Further, the cost of dereferencing the LargePositiveIntegers into 32-bit values passed as pointers should be a minor part of the entiore call-out overhead.

Hence, how are you measuring this performance overhead?  Are you convinced ther cost is in pointer representation in LargeIntegers?

What is an example inner loop from the application?

P.S.  Alan, Ian, did your original post go to vwnc?  I didn't see it there.  If it did, I again have reason to spew bile at Cincom's "email" server...

Reply | Threaded
Open this post in threaded view
|

RE: external c pointers

Terry Raymond

Ian

 

Have you tried increasing the size of the Eden memory space?

Sometimes, properly sizing Eden can make a significant difference

in overall program performance.

 

Terry

===========================================================
Terry Raymond       Smalltalk Professional Debug Package
Crafted Smalltalk
80 Lazywood Ln.
Tiverton, RI  02878
(401) 624-4517      [hidden email]
<http://www.craftedsmalltalk.com>
===========================================================


From: Ian Upright [mailto:[hidden email]]
Sent: Tuesday, May 16, 2006 6:24 PM
To: [hidden email]
Cc: [hidden email]
Subject: RE: external c pointers

 

Hi Eliot..  We went through some great pain to create a “handle allocator” in C.  Upon startup, it allocates a large number of these handles in lower C virtual memory, that fits within the SmallInteger range.  Then through great pain, every call in C is gone through this handle, which then redirects to the real C object in question, which may reside at a higher up, LargeInteger memory location.  However, this LargeInteger value is never exposed to the Smalltalk VM.  Though more pain, these handles are recycled when they are no longer referenced (via finalization), to ensure that we have enough in lower memory.  The addition of handles and management of them creates an additional layer of indirection that you would think would impact performance negatively.  However, all this refactoring has resulted in tremendous performance gains in our application.  It’s extremely ugly but now performs much better.

 

In the cases we are experiencing, perhaps 25% or more of the pointers are LargeIntegers, so it is not all that infrequent.  We are often running tight loops or large loops making large numbers of extremely short and fast calls.  For example, we have set iterators, implement the collection protocol, and so on, with these kinds of primitives.  Our sets and objects reside in C memory, not in Smalltalk memory, so we are pushing things almost as hard as it gets, in regards to external calls, etc.

 

I’m sure the creation of the LargeIntegers stresses the garbage collector quite a bit..  And as a guess, this additional stress on the garbage collector and creation of extra objects may blow the CPU caches further and cause more impact on performance.  I’m also not sure how efficient the creation of the LargeIntegers is, from an external pointer.  The hash function of the LargeInteger may also negatively impact performance as well.

 

Ian

 

 


From: Eliot Miranda [mailto:[hidden email]]
Sent: Friday, May 12, 2006 5:43 PM
To: [hidden email]
Cc: Alan Knight; [hidden email]
Subject: Re: external c pointers

 

via Alan Knight, At 09:30 PM 05/05/2006, Ian Upright wrote:

Hi there.. we are getting completely slaughtered when we scale up external interfacing to C/C++ using subclasses of ExternalInterface, and it’s due to our pointers turning into LargePositiveIntegers instead of SmallIntegers..  One possibility may be to allocate a number of handles in lower memory, and this could have indirect pointers to the real address (potentially higher)..  That would be quite involved and a big hack, and due to the additional indirection, would be slightly more costly.  However, the external interfacing for us is up to 40x slower than it is when it’s using SmallInts..  Not too surprisingly, our app can be up to 5-20x slower than when it resides in lower memory– a real killer.
 
What might be the best way to remedy this?  Patch the VM somehow so something like a CPointer can have a 32-bit pointer directly embedded in it?
And then have some kind of lightning fast hash primitive that calculates it based on the value of the (embedded) 32-bit CPointer?
 
I noticed that the hash for Large integers doesn’t seem to be a primitive.. I wonder if this is part of the extremely poor performance.
When it comes to speeding this up, I’m all ears.. as it seems there is no easy solution..?!
 
Ian
 

 

Ian,

     I can understand that large interger allocation could hit one hard if the C code were returning addresses that missed the SmallInteger range on the vast majority of calls and if the application did nothing but collect these results.  But I find it hard to imagine a real application that would be able to suffer this cost.  Further, the cost of dereferencing the LargePositiveIntegers into 32-bit values passed as pointers should be a minor part of the entiore call-out overhead.

Hence, how are you measuring this performance overhead?  Are you convinced ther cost is in pointer representation in LargeIntegers?

What is an example inner loop from the application?

P.S.  Alan, Ian, did your original post go to vwnc?  I didn't see it there.  If it did, I again have reason to spew bile at Cincom's "email" server...

Reply | Threaded
Open this post in threaded view
|

Re: external c pointers

Mark Pirogovsky-3
Terry,

Is there any definition of the "properly sized Eden" - How one can
determine if his "Eden" is sized properly ?

Any calculation, empirical data, recommendations, etc. would be greater
appreciated.

--Mark

Terry Raymond wrote:

> Ian
>
>  
>
> Have you tried increasing the size of the Eden memory space?
>
> Sometimes, properly sizing Eden can make a significant difference
>
> in overall program performance.
>
>  
>
> Terry
>
> ===========================================================
> Terry Raymond       Smalltalk Professional Debug Package
> Crafted Smalltalk
> 80 Lazywood Ln.
> Tiverton, RI  02878
> (401) 624-4517      [hidden email]
> <http://www.craftedsmalltalk.com>
> ===========================================================
>
> ------------------------------------------------------------------------
>
> From: Ian Upright [mailto:[hidden email]]
> Sent: Tuesday, May 16, 2006 6:24 PM
> To: [hidden email]
> Cc: [hidden email]
> Subject: RE: external c pointers
>
>  
>
> Hi Eliot..  We went through some great pain to create a “handle
> allocator” in C.  Upon startup, it allocates a large number of these
> handles in lower C virtual memory, that fits within the SmallInteger
> range.  Then through great pain, every call in C is gone through this
> handle, which then redirects to the real C object in question, which may
> reside at a higher up, LargeInteger memory location.  However, this
> LargeInteger value is never exposed to the Smalltalk VM.  Though more
> pain, these handles are recycled when they are no longer referenced (via
> finalization), to ensure that we have enough in lower memory.  The
> addition of handles and management of them creates an additional layer
> of indirection that you would think would impact performance
> negatively.  However, all this refactoring has resulted in tremendous
> performance gains in our application.  It’s extremely ugly but now
> performs much better.
>
>  
>
> In the cases we are experiencing, perhaps 25% or more of the pointers
> are LargeIntegers, so it is not all that infrequent.  We are often
> running tight loops or large loops making large numbers of extremely
> short and fast calls.  For example, we have set iterators, implement the
> collection protocol, and so on, with these kinds of primitives.  Our
> sets and objects reside in C memory, not in Smalltalk memory, so we are
> pushing things almost as hard as it gets, in regards to external calls, etc.
>
>  
>
> I’m sure the creation of the LargeIntegers stresses the garbage
> collector quite a bit..  And as a guess, this additional stress on the
> garbage collector and creation of extra objects may blow the CPU caches
> further and cause more impact on performance.  I’m also not sure how
> efficient the creation of the LargeIntegers is, from an external
> pointer.  The hash function of the LargeInteger may also negatively
> impact performance as well.
>
>  
>
> Ian
>
>  
>
>  
>
> ------------------------------------------------------------------------
>
> From: Eliot Miranda [mailto:[hidden email]]
> Sent: Friday, May 12, 2006 5:43 PM
> To: [hidden email]
> Cc: Alan Knight; [hidden email]
> Subject: Re: external c pointers
>
>  
>
> via Alan Knight, At 09:30 PM 05/05/2006, Ian Upright wrote:
>
>> Hi there.. we are getting completely slaughtered when we scale up
>> external interfacing to C/C++ using subclasses of ExternalInterface,
>> and it’s due to our pointers turning into LargePositiveIntegers
>> instead of SmallIntegers..  One possibility may be to allocate a
>> number of handles in lower memory, and this could have indirect
>> pointers to the real address (potentially higher)..  That would be
>> quite involved and a big hack, and due to the additional indirection,
>> would be slightly more costly.  However, the external interfacing for
>> us is up to 40x slower than it is when it’s using SmallInts..  Not too
>> surprisingly, our app can be up to 5-20x slower than when it resides
>> in lower memory– a real killer.
>>  
>> What might be the best way to remedy this?  Patch the VM somehow so
>> something like a CPointer can have a 32-bit pointer directly embedded
>> in it?
>> And then have some kind of lightning fast hash primitive that
>> calculates it based on the value of the (embedded) 32-bit CPointer?
>>  
>> I noticed that the hash for Large integers doesn’t seem to be a
>> primitive.. I wonder if this is part of the extremely poor performance.
>> When it comes to speeding this up, I’m all ears.. as it seems there is
>> no easy solution..?!
>>  
>> Ian
>>  
>>
>>  
>>
>> Ian,
>>
>>      I can understand that large interger allocation could hit one
>> hard if the C code were returning addresses that missed the
>> SmallInteger range on the vast majority of calls and if the
>> application did nothing but collect these results.  But I find it hard
>> to imagine a real application that would be able to suffer this cost.  
>> Further, the cost of dereferencing the LargePositiveIntegers into
>> 32-bit values passed as pointers should be a minor part of the entiore
>> call-out overhead.
>>
>> Hence, how are you measuring this performance overhead?  Are you
>> convinced ther cost is in pointer representation in LargeIntegers?
>>
>> What is an example inner loop from the application?
>>
>> P.S.  Alan, Ian, did your original post go to vwnc?  I didn't see it
>> there.  If it did, I again have reason to spew bile at Cincom's
>> "email" server...
>>

Reply | Threaded
Open this post in threaded view
|

RE: external c pointers

Terry Raymond
Mark

It really depends on your application.

I would try doubling it, up to 7 times, and rerun the
application each time.

If eden is too big you will encounter excessive pause times.
If there were some way to measure the pause time then I
would recommend increasing eden until you measure pause
times that at your acceptable pause time limit.

Terry
 
===========================================================
Terry Raymond       Smalltalk Professional Debug Package
Crafted Smalltalk
80 Lazywood Ln.
Tiverton, RI  02878
(401) 624-4517      [hidden email]
<http://www.craftedsmalltalk.com>
===========================================================

> -----Original Message-----
> From: Mark Pirogovsky [mailto:[hidden email]]
> Sent: Wednesday, May 17, 2006 11:59 AM
> To: Terry Raymond
> Cc: [hidden email]
> Subject: Re: external c pointers
>
> Terry,
>
> Is there any definition of the "properly sized Eden" - How one can
> determine if his "Eden" is sized properly ?
>
> Any calculation, empirical data, recommendations, etc. would be greater
> appreciated.
>
> --Mark
>
> Terry Raymond wrote:
>
> > Ian
> >
> >
> >
> > Have you tried increasing the size of the Eden memory space?
> >
> > Sometimes, properly sizing Eden can make a significant difference
> >
> > in overall program performance.
> >
> >
> >
> > Terry
> >
> > ===========================================================
> > Terry Raymond       Smalltalk Professional Debug Package
> > Crafted Smalltalk
> > 80 Lazywood Ln.
> > Tiverton, RI  02878
> > (401) 624-4517      [hidden email]
> > <http://www.craftedsmalltalk.com>
> > ===========================================================
> >
> > ------------------------------------------------------------------------
> >
> > From: Ian Upright [mailto:[hidden email]]
> > Sent: Tuesday, May 16, 2006 6:24 PM
> > To: [hidden email]
> > Cc: [hidden email]
> > Subject: RE: external c pointers
> >
> >
> >
> > Hi Eliot..  We went through some great pain to create a "handle
> > allocator" in C.  Upon startup, it allocates a large number of these
> > handles in lower C virtual memory, that fits within the SmallInteger
> > range.  Then through great pain, every call in C is gone through this
> > handle, which then redirects to the real C object in question, which may
> > reside at a higher up, LargeInteger memory location.  However, this
> > LargeInteger value is never exposed to the Smalltalk VM.  Though more
> > pain, these handles are recycled when they are no longer referenced (via
> > finalization), to ensure that we have enough in lower memory.  The
> > addition of handles and management of them creates an additional layer
> > of indirection that you would think would impact performance
> > negatively.  However, all this refactoring has resulted in tremendous
> > performance gains in our application.  It's extremely ugly but now
> > performs much better.
> >
> >
> >
> > In the cases we are experiencing, perhaps 25% or more of the pointers
> > are LargeIntegers, so it is not all that infrequent.  We are often
> > running tight loops or large loops making large numbers of extremely
> > short and fast calls.  For example, we have set iterators, implement the
> > collection protocol, and so on, with these kinds of primitives.  Our
> > sets and objects reside in C memory, not in Smalltalk memory, so we are
> > pushing things almost as hard as it gets, in regards to external calls,
> etc.
> >
> >
> >
> > I'm sure the creation of the LargeIntegers stresses the garbage
> > collector quite a bit..  And as a guess, this additional stress on the
> > garbage collector and creation of extra objects may blow the CPU caches
> > further and cause more impact on performance.  I'm also not sure how
> > efficient the creation of the LargeIntegers is, from an external
> > pointer.  The hash function of the LargeInteger may also negatively
> > impact performance as well.
> >
> >
> >
> > Ian
> >
> >
> >
> >
> >
> > ------------------------------------------------------------------------
> >
> > From: Eliot Miranda [mailto:[hidden email]]
> > Sent: Friday, May 12, 2006 5:43 PM
> > To: [hidden email]
> > Cc: Alan Knight; [hidden email]
> > Subject: Re: external c pointers
> >
> >
> >
> > via Alan Knight, At 09:30 PM 05/05/2006, Ian Upright wrote:
> >
> >> Hi there.. we are getting completely slaughtered when we scale up
> >> external interfacing to C/C++ using subclasses of ExternalInterface,
> >> and it's due to our pointers turning into LargePositiveIntegers
> >> instead of SmallIntegers..  One possibility may be to allocate a
> >> number of handles in lower memory, and this could have indirect
> >> pointers to the real address (potentially higher)..  That would be
> >> quite involved and a big hack, and due to the additional indirection,
> >> would be slightly more costly.  However, the external interfacing for
> >> us is up to 40x slower than it is when it's using SmallInts..  Not too
> >> surprisingly, our app can be up to 5-20x slower than when it resides
> >> in lower memory- a real killer.
> >>
> >> What might be the best way to remedy this?  Patch the VM somehow so
> >> something like a CPointer can have a 32-bit pointer directly embedded
> >> in it?
> >> And then have some kind of lightning fast hash primitive that
> >> calculates it based on the value of the (embedded) 32-bit CPointer?
> >>
> >> I noticed that the hash for Large integers doesn't seem to be a
> >> primitive.. I wonder if this is part of the extremely poor performance.
> >> When it comes to speeding this up, I'm all ears.. as it seems there is
> >> no easy solution..?!
> >>
> >> Ian
> >>
> >>
> >>
> >>
> >> Ian,
> >>
> >>      I can understand that large interger allocation could hit one
> >> hard if the C code were returning addresses that missed the
> >> SmallInteger range on the vast majority of calls and if the
> >> application did nothing but collect these results.  But I find it hard
> >> to imagine a real application that would be able to suffer this cost.
> >> Further, the cost of dereferencing the LargePositiveIntegers into
> >> 32-bit values passed as pointers should be a minor part of the entiore
> >> call-out overhead.
> >>
> >> Hence, how are you measuring this performance overhead?  Are you
> >> convinced ther cost is in pointer representation in LargeIntegers?
> >>
> >> What is an example inner loop from the application?
> >>
> >> P.S.  Alan, Ian, did your original post go to vwnc?  I didn't see it
> >> there.  If it did, I again have reason to spew bile at Cincom's
> >> "email" server...
> >>