Re: Almost, but not quite: C++ STL in LilyPond

2020-05-07 Thread David Kastrup
David Kastrup  writes:

> David Kastrup  writes:
>
>> Han-Wen Nienhuys  writes:
>>
>>> On Tue, May 5, 2020 at 5:49 PM Jonas Hahnfeld  wrote:

 Am Dienstag, den 05.05.2020, 17:09 +0200 schrieb David Kastrup:
 > I am currently digging back and forth regarding implementation of our
 > Smobs (Scheme objects) and garbage collection and STL, and I think I am
 > converging on the realisation that we'll have to end up duplicating
 > those parts of STL that we are using.

 Please forgive my ignorance, but I'm missing a bit of context. Are we
 talking about vector/lists/... of Smobs? Or is the issue that Smobs
 contain STL containers?

 In any case I'm not really fond of duplicating code. Given that it
 seems to "work" right now, IMHO this needs to give some very clear
 advantage over keeping the status quo.
>>>
>>> I'm siding with Jonas here. Unless there is a realliy strong
>>> overriding concern, we should not be reimplementing STL.
>>
>> I was not planning on reimplementing STL.  I was planning on only using
>> those parts of STL as part of Smob that we had a reimplementation for
>> and otherwise either keeping away or providing a substitute.
>
> Well, I may be able to figure out a way to analyse STL nodes after all
> without too much of a negative performance impact.  If a "Pointer"
> initialises from a static variable instead of a constant, I can create
> instances with different initial values and compare their memory images.

I think I actually found a way to implement the original plan of a
temporary internal structure-recording allocator without performance
impact for the continued operation.  So we can can this topic until
there is code.  There might still be a point in preferring approaches
making more use of Guile facilities when working with Guile data types.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-07 Thread David Kastrup
Jonas Hahnfeld  writes:

> Am Donnerstag, den 07.05.2020, 07:57 +0200 schrieb Han-Wen Nienhuys:
>> In my mind, supporting GUILE 2.x means supporting byte compilation
>> because we'd otherwise regress startup speed by a second or so.
>> 
>> That is currently blocked on your proposed alternative to
>> https://codereview.appspot.com/577720043/ . Or did I miss an update on
>> that topic?
>
> https://sourceforge.net/p/testlilyissues/issues/5895/
> https://codereview.appspot.com/547920045
> ?

Thanks.

-- 
David Kastrup
My replies have a tendency to cause friction.  To help mitigating
damage, feel free to forward problematic posts to me adding a subject
like "timeout 1d" (for a suggested timeout of 1 day) or "offensive".



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-06 Thread Jonas Hahnfeld
Am Donnerstag, den 07.05.2020, 07:57 +0200 schrieb Han-Wen Nienhuys:
> In my mind, supporting GUILE 2.x means supporting byte compilation
> because we'd otherwise regress startup speed by a second or so.
> 
> That is currently blocked on your proposed alternative to
> https://codereview.appspot.com/577720043/ . Or did I miss an update on
> that topic?

https://sourceforge.net/p/testlilyissues/issues/5895/
https://codereview.appspot.com/547920045
?


signature.asc
Description: This is a digitally signed message part


Re: Almost, but not quite: C++ STL in LilyPond

2020-05-06 Thread Han-Wen Nienhuys
On Wed, May 6, 2020 at 11:42 AM David Kastrup  wrote:
> > Or, we could globally map new/delete to GC_malloc and GC_free.
>
> That would vastly increase the amount of GC-scanned memory, and GC
> scanning is already slow.

I'd first measure this carefully before spending time on a complex
rewrite. The last time I tried, I hadn't fixed the heap scaling, so we
have no current data on this. I'd wager that most of the memory we
allocate (especially structures that are persistent) are under GUILE
GC control anyways, so it shouldn't make much difference. (Maybe you'd
have to redo Grob_array, though).

> > The GUILE folks may not love finalizers, but is there any evidence
> > that they're really going to get rid of them? Other foreign function
> > integrations willl need them too, because they need them to manage
> > non-memory resources (eg. files).
>
> Sure, but we require them for everything right now and that does not
> match normal use.  We could reduce this to require them for everything
> that does not use STL.  Which brings us back to the original problem.

Can you sketch the problem you want to address with a code example? I
still don't understand your specific proposal.

> At any rate, the principal request here was to consider other options
> before using previously unused STL structures as part of Smobs because I
> see options that might benefit from them.  Whether to use those options
> will be dependent on actual code I put out to preview.  I just don't see
> the point in making it harder to get to that point when it can easily be
> avoided.

I wish we would first look at getting rid of our GUILE 1.8 support.
Once we move to Boehm GC exclusively, we can simpilfy our code by a
lot, and it will be easier to experiment with different approaches to
address remaining problems.

In my mind, supporting GUILE 2.x means supporting byte compilation
because we'd otherwise regress startup speed by a second or so.

That is currently blocked on your proposed alternative to
https://codereview.appspot.com/577720043/ . Or did I miss an update on
that topic?

-- 
Han-Wen Nienhuys - hanw...@gmail.com - http://www.xs4all.nl/~hanwen



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-06 Thread David Kastrup
David Kastrup  writes:

> Han-Wen Nienhuys  writes:
>
>> On Tue, May 5, 2020 at 5:49 PM Jonas Hahnfeld  wrote:
>>>
>>> Am Dienstag, den 05.05.2020, 17:09 +0200 schrieb David Kastrup:
>>> > I am currently digging back and forth regarding implementation of our
>>> > Smobs (Scheme objects) and garbage collection and STL, and I think I am
>>> > converging on the realisation that we'll have to end up duplicating
>>> > those parts of STL that we are using.
>>>
>>> Please forgive my ignorance, but I'm missing a bit of context. Are we
>>> talking about vector/lists/... of Smobs? Or is the issue that Smobs
>>> contain STL containers?
>>>
>>> In any case I'm not really fond of duplicating code. Given that it
>>> seems to "work" right now, IMHO this needs to give some very clear
>>> advantage over keeping the status quo.
>>
>> I'm siding with Jonas here. Unless there is a realliy strong
>> overriding concern, we should not be reimplementing STL.
>
> I was not planning on reimplementing STL.  I was planning on only using
> those parts of STL as part of Smob that we had a reimplementation for
> and otherwise either keeping away or providing a substitute.

Well, I may be able to figure out a way to analyse STL nodes after all
without too much of a negative performance impact.  If a "Pointer"
initialises from a static variable instead of a constant, I can create
instances with different initial values and compare their memory images.

It remains likely that variants actually taking available Scheme
features into account would perform better.  But it's possible that a
first pitch using a native STL version would work out, even if with
regard to best performance it is likely going to be a stopgap measure.

An interesting corollary of the overall design might be that we can drop
Simple_smob, namely objects without implied Scheme identity.  The
equivalent of smobbed_copy () could be implemented without affecting the
class itself as long as we know its "GC traits".

For smobs with self_scm, this isn't possible.  And they cannot
themselves be placed into STL nodes, merely as an SCM reference, since
their having a self_scm requires them to be allocated as one entity.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-06 Thread David Kastrup
Han-Wen Nienhuys  writes:

> On Tue, May 5, 2020 at 5:49 PM Jonas Hahnfeld  wrote:
>>
>> Am Dienstag, den 05.05.2020, 17:09 +0200 schrieb David Kastrup:
>> > I am currently digging back and forth regarding implementation of our
>> > Smobs (Scheme objects) and garbage collection and STL, and I think I am
>> > converging on the realisation that we'll have to end up duplicating
>> > those parts of STL that we are using.
>>
>> Please forgive my ignorance, but I'm missing a bit of context. Are we
>> talking about vector/lists/... of Smobs? Or is the issue that Smobs
>> contain STL containers?
>>
>> In any case I'm not really fond of duplicating code. Given that it
>> seems to "work" right now, IMHO this needs to give some very clear
>> advantage over keeping the status quo.
>
> I'm siding with Jonas here. Unless there is a realliy strong
> overriding concern, we should not be reimplementing STL.

I was not planning on reimplementing STL.  I was planning on only using
those parts of STL as part of Smob that we had a reimplementation for
and otherwise either keeping away or providing a substitute.

> Regarding GC, once we leave behind GUILE 1.x, we could adorn all SCM
> containing structures with a operator new/operator delete, which calls
> scm_gc_alloc()/scm_gc_free(). That would get rid of marking functions.
> For vectors, we could introduce a scm_vector, which is an STL vector
> with a custom allocator.

Uh, that sounds pretty much like what I was proposing.

> Or, we could globally map new/delete to GC_malloc and GC_free.

That would vastly increase the amount of GC-scanned memory, and GC
scanning is already slow.

> We might need finalizers where the smobs refer to outside data
> structures, like freetype and pango fonts, but I think we could even
> get away with never deallocating them at all.

The occasional finaliser is going to match what Guile people are using.
At the current point of time, we have finalisers for everything.  You
have added code that messes with the Boehm GC because its assumptions
are not compatible with that.

> The GUILE folks may not love finalizers, but is there any evidence
> that they're really going to get rid of them? Other foreign function
> integrations willl need them too, because they need them to manage
> non-memory resources (eg. files).

Sure, but we require them for everything right now and that does not
match normal use.  We could reduce this to require them for everything
that does not use STL.  Which brings us back to the original problem.

At any rate, the principal request here was to consider other options
before using previously unused STL structures as part of Smobs because I
see options that might benefit from them.  Whether to use those options
will be dependent on actual code I put out to preview.  I just don't see
the point in making it harder to get to that point when it can easily be
avoided.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-06 Thread Hans Åberg


> On 6 May 2020, at 08:21, Han-Wen Nienhuys  wrote:
> 
> Regarding GC, once we leave behind GUILE 1.x, we could adorn all SCM
> containing structures with a operator new/operator delete, which calls
> scm_gc_alloc()/scm_gc_free(). That would get rid of marking functions.
> For vectors, we could introduce a scm_vector, which is an STL vector
> with a custom allocator. Or, we could globally map new/delete to
> GC_malloc and GC_free.

One can define the standard operator new/delete to call 
GC_malloc_uncollectable/GC_free, and the C++ standard guarantees that all 
allocations will use them (the linker will replace all occurrences).

Then for objects of indeterminate lifetime, I have user defined operator 
new/delete for GC_malloc, and no GC_free, with which I use a reference class 
similar to std::shared_ptr, but calling these operators instead.

> We might need finalizers where the smobs refer to outside data
> structures, like freetype and pango fonts, but I think we could even
> get away with never deallocating them at all.

As for the Boehm GC finalizers, I could not find that they take up significant 
time, but I had to rewrite the C++ code they suggest.





Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Han-Wen Nienhuys
On Tue, May 5, 2020 at 5:49 PM Jonas Hahnfeld  wrote:
>
> Am Dienstag, den 05.05.2020, 17:09 +0200 schrieb David Kastrup:
> > I am currently digging back and forth regarding implementation of our
> > Smobs (Scheme objects) and garbage collection and STL, and I think I am
> > converging on the realisation that we'll have to end up duplicating
> > those parts of STL that we are using.
>
> Please forgive my ignorance, but I'm missing a bit of context. Are we
> talking about vector/lists/... of Smobs? Or is the issue that Smobs
> contain STL containers?
>
> In any case I'm not really fond of duplicating code. Given that it
> seems to "work" right now, IMHO this needs to give some very clear
> advantage over keeping the status quo.

I'm siding with Jonas here. Unless there is a realliy strong
overriding concern, we should not be reimplementing STL.

Regarding GC, once we leave behind GUILE 1.x, we could adorn all SCM
containing structures with a operator new/operator delete, which calls
scm_gc_alloc()/scm_gc_free(). That would get rid of marking functions.
For vectors, we could introduce a scm_vector, which is an STL vector
with a custom allocator. Or, we could globally map new/delete to
GC_malloc and GC_free.

We might need finalizers where the smobs refer to outside data
structures, like freetype and pango fonts, but I think we could even
get away with never deallocating them at all.

The GUILE folks may not love finalizers, but is there any evidence
that they're really going to get rid of them? Other foreign function
integrations willl need them too, because they need them to manage
non-memory resources (eg. files).

-- 
Han-Wen Nienhuys - hanw...@gmail.com - http://www.xs4all.nl/~hanwen



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Carl Sorensen


On 5/5/20, 4:04 PM, "lilypond-devel on behalf of David Kastrup" 
 
wrote:

David Kastrup  writes:



If everything can be represented/mapped in similar manner, the Scheme
garbage collector does not need any interaction with user-written code
for doing its job.

And I really think that's where we want to go in order to get memory
semantics and behavior that are "Guile orthodox" to a degree that we'll
get more attention for problems we are experiencing than what we have
been working with so far.


CS-> In my opinion, this outcome is worth a lot for our project.  We've had 
lots of garbage-collection-related errors as we have moved to new versions of 
Guile.  And the Guile people (reasonably enough from their point of view) say 
they can't help us, because our system is too complex.

CS -> If we can get "Guile orthodox" on our memory semantics, we'll have much 
lower barriers to getting help from the Guile crew, IMO.

Carl




Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Dan Eble
On May 5, 2020, at 18:03, David Kastrup  wrote:
> 
> If everything can be represented/mapped in similar manner, the Scheme
> garbage collector does not need any interaction with user-written code
> for doing its job.

I would not like to see the STL go, but if the getting rid of all the explicit 
GC code is the other side of it, it might be worth it.
— 
Dan




Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Dan Eble
On May 5, 2020, at 17:48, David Kastrup  wrote:
> 
> One thing here is that being under control of a wrapper, one can use
> unchecked unsmobs.

This is a good idea.  I considered this briefly, but I wanted to focus first on 
making it natural to deal robustly with potentially improper or heterogeneous 
lists coming from a user, and leave optimizing more restrictive cases for later.
— 
Dan




Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread David Kastrup
David Kastrup  writes:

> Dan Eble  writes:
>
>> On May 5, 2020, at 13:37, David Kastrup  wrote:
>>> 
>>> What I have ready-to-use is something that stores like an SCM value but
>>> behaves like a Smob pointer with regard to -> and * usage.
>>
>> Oh.  I believe I have some of that too.  Excerpt:
>>
>> // specialization for pointers
>> template 
>> class ly_scm_t
>> {
>> private:
>>   using traits = ly_scm_traits;
>>
>> private:
>>   SCM scm_ = SCM_UNDEFINED;
>>
>> public:
>>   ly_scm_t () = default;
>>   ly_scm_t (const ly_scm_t &) = default;
>>   ly_scm_t &operator = (const ly_scm_t &) = default;
>>   ~ly_scm_t () = default;
>>
>>   explicit ly_scm_t (T *v) : scm_ (traits::to_scm (v)) {}
>>
>>   explicit operator bool () const { return traits::has_value (scm_); }
>>   operator T *() const { return traits::to_value (scm_); }
>>   T *operator ->() { return operator T * (); }
>>   // TODO: operator *
>> };
>
> Well, mine is a lot less formal I guess.  Something akin to
>
> template 
> class Scm_ptr
> {
>   SCM scm_;
> public:
>   Scm_ptr () : scm_ (SCM_UNDEFINED) {}
>   Scm_ptr (SCM v) : scm_ (v)
>   {
> assert (SCM_UNBNDP (v) || T::is_smob (v));
>   }
>   Scm_ptr (const T *p) : scm_ (p ? p->self_scm () : SCM_UNDEFINED) {}
>   Scm_ptr & operator = (T* p)
>   {
> scm_ = p ? p->self_scm () : SCM_UNDEFINED;
> return *this;
>   }
>   Scm_ptr & operator = (SCM s)
>   {
> if (SCM_UNBNDP (s) || LY_ASSERT_SMOB (T, s, 1))
>   scm_ = s;
> else
>   scm_ = SCM_UNDEFINED;
> return *this;
>   }
>   T * operator -> () const
>   {
> return dynamic_cast (T::unchecked_unsmob (scm_));
>   }
>   operator T * () const
>   {
> return dynamic_cast (T::unchecked_unsmob (scm_));
>   }
>   operator SCM () const
>   {
> return scm_;
>   }
>   operator bool () const
>   {
> return SCM_BNDP (scm_);
>   }
> };
>
> One thing here is that being under control of a wrapper, one can use
> unchecked unsmobs.  I am not sure about conversion to SCM: this uses a
> conversion operator.  I was tempted to use operator & instead.  I did
> not have any traits in mind yet, but formalising the allocation/garbage
> protection thing would certainly call for something identifiable with
> regard to types that LilyPond knows how to allocate/protect.

To expound somewhat: a vector class (replete with iterators etc)
implemented through SCM would internally consist of one SCM cell holding
a Scheme vector and one size_t number for its current length.  The
capacity would be the actual size of the Scheme vector (with extra slots
filled with SCM_UNBNDP).  So the garbage collector footprint is
basically "pouo" (look up Scheme struct vtables for the meaning).  A
Smob allocator basically collects the strings of its constituents at
their respective offsets when it is first called and initializes a
Scheme vtable with it, and then uses this vtable to allocate the smob as
an SCM value.  Simple_smob can create a "smobbed copy" on demand in a
similar way.

If everything can be represented/mapped in similar manner, the Scheme
garbage collector does not need any interaction with user-written code
for doing its job.

And I really think that's where we want to go in order to get memory
semantics and behavior that are "Guile orthodox" to a degree that we'll
get more attention for problems we are experiencing than what we have
been working with so far.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread David Kastrup
Dan Eble  writes:

> On May 5, 2020, at 13:37, David Kastrup  wrote:
>> 
>> What I have ready-to-use is something that stores like an SCM value but
>> behaves like a Smob pointer with regard to -> and * usage.
>
> Oh.  I believe I have some of that too.  Excerpt:
>
> // specialization for pointers
> template 
> class ly_scm_t
> {
> private:
>   using traits = ly_scm_traits;
>
> private:
>   SCM scm_ = SCM_UNDEFINED;
>
> public:
>   ly_scm_t () = default;
>   ly_scm_t (const ly_scm_t &) = default;
>   ly_scm_t &operator = (const ly_scm_t &) = default;
>   ~ly_scm_t () = default;
>
>   explicit ly_scm_t (T *v) : scm_ (traits::to_scm (v)) {}
>
>   explicit operator bool () const { return traits::has_value (scm_); }
>   operator T *() const { return traits::to_value (scm_); }
>   T *operator ->() { return operator T * (); }
>   // TODO: operator *
> };

Well, mine is a lot less formal I guess.  Something akin to

template 
class Scm_ptr
{
  SCM scm_;
public:
  Scm_ptr () : scm_ (SCM_UNDEFINED) {}
  Scm_ptr (SCM v) : scm_ (v)
  {
assert (SCM_UNBNDP (v) || T::is_smob (v));
  }
  Scm_ptr (const T *p) : scm_ (p ? p->self_scm () : SCM_UNDEFINED) {}
  Scm_ptr & operator = (T* p)
  {
scm_ = p ? p->self_scm () : SCM_UNDEFINED;
return *this;
  }
  Scm_ptr & operator = (SCM s)
  {
if (SCM_UNBNDP (s) || LY_ASSERT_SMOB (T, s, 1))
  scm_ = s;
else
  scm_ = SCM_UNDEFINED;
return *this;
  }
  T * operator -> () const
  {
return dynamic_cast (T::unchecked_unsmob (scm_));
  }
  operator T * () const
  {
return dynamic_cast (T::unchecked_unsmob (scm_));
  }
  operator SCM () const
  {
return scm_;
  }
  operator bool () const
  {
return SCM_BNDP (scm_);
  }
};

One thing here is that being under control of a wrapper, one can use
unchecked unsmobs.  I am not sure about conversion to SCM: this uses a
conversion operator.  I was tempted to use operator & instead.  I did
not have any traits in mind yet, but formalising the allocation/garbage
protection thing would certainly call for something identifiable with
regard to types that LilyPond knows how to allocate/protect.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Dan Eble
On May 5, 2020, at 13:37, David Kastrup  wrote:
> 
> What I have ready-to-use is something that stores like an SCM value but
> behaves like a Smob pointer with regard to -> and * usage.

Oh.  I believe I have some of that too.  Excerpt:

// specialization for pointers
template 
class ly_scm_t
{
private:
  using traits = ly_scm_traits;

private:
  SCM scm_ = SCM_UNDEFINED;

public:
  ly_scm_t () = default;
  ly_scm_t (const ly_scm_t &) = default;
  ly_scm_t &operator = (const ly_scm_t &) = default;
  ~ly_scm_t () = default;

  explicit ly_scm_t (T *v) : scm_ (traits::to_scm (v)) {}

  explicit operator bool () const { return traits::has_value (scm_); }
  operator T *() const { return traits::to_value (scm_); }
  T *operator ->() { return operator T * (); }
  // TODO: operator *
};
— 
Dan




Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread David Kastrup
Dan Eble  writes:

> On May 5, 2020, at 11:09, David Kastrup  wrote:
>> I'd like to come up with an allocator/container programming model
>> comparatively similar to the STL one so that one could mostly steal the
>> implementations and "just" add the required Scheme awareness while
>> removing of destruction/deallocation tracking.
>
> It might be difficult for me to devote the time to this discussion
> that it deserves, but I want to say that I've been working on C++
> wrappers that allow walking SCM lists (of immediate values or Smobs)
> with standard iterator syntax and semantics.  I suppose that's
> tangential to what you are talking about, but it would be
> disappointing if we did significant overlapping work.

At the current point of time, I am more concerned with offering
container shadows than iterators on existing SCM data structures.  The
typical SCM list would be somewhat akin to a Forward_list construct.

What I have ready-to-use is something that stores like an SCM value but
behaves like a Smob pointer with regard to -> and * usage.  That would
allow to replace a number of Smob members that are now marked with

scm_gc_mark (xxx->self_scm ())

with

scm_gc_mark (xxx) or scm_gc_mark (xxx.scm ()) which is a first step
towards making Smob memory layout be organised where its garbage
collection phase can be handled by a struct vtable rather than an
explicit hook (this basically shifts the responsibility of whether the
whole struct or just individual parts of it participate in the mark
phase back to the Guile implementation).

But STL containers still require a strategy.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Hans Åberg



> On 5 May 2020, at 18:57, David Kastrup  wrote:
> 
> Hans Åberg  writes:
> 
>>> On 5 May 2020, at 17:09, David Kastrup  wrote:
>>> 
>>> One reason is that we want to get rid of finalisers particularly in
>>> connection with the Boehm GC.  Finalisers are called when garbage is
>>> collected, the collection of garbage is typically indicative of the
>>> expected lifetime of our objects (there are a few that might be
>>> unambiguously dead before), and thus it will cause C++ destructors to be
>>> called.  And those trigger, among other things, the deallocation of
>>> memory resources, but we'd rather want the deallocation to be determined
>>> by Scheme mechanisms and not have any resources in need of destruction
>>> other than falling into oblivion.
>> 
>> For the GC objects, I use operator new/delete with an extra argument,
>> the latter with empty body, wrapped in structure similar to
>> std:shared_ptr. Then the memory resources of those objects are only
>> handled by the GC, regardless of finalizers.
> 
> That does not sound like it has anything to do with SCM garbage
> collection.  

Sorry, I sent the message by mistake: I thought I clicked “Delete”, but it was 
“Send”, so if you so like, just forget about it.

> Nor is it phrased in a manner where I would understand what
> you are talking about.  It is not clear what a "GC object" is supposed
> to be, it is not clear what "I use" is supposed to mean with regard to
> where and how it is being used, "operator new/delete with an extra
> argument, the latter with empty body" presumably means that operator
> delete rather than the extra argument have an empty body.  It is not
> clear what it is supposed to buy us if operator delete has an empty body
> when calling operator delete is something triggered by the destructor
> and not having to call the destructor is a prerequisite for the life
> time being determined by garbage collection without finalisation hook.

C++ operator new/delete are called in pairs, so if the GC allocated objects 
have a pair of their own, then one can set a no-op delete.

> It is not clear _what_ is "wrapped in structure similar to
> std:shared_ptr" and how that is supposed to interact with Scheme.

This class use reference counting; instead use the GC allocating operator new. 
Don’t know about the Scheme part, though, but it uses something similar, I 
think.

>>> I've tried to come up with a clever callback scheme where the first
>>> use of a structure causes fake nodes to be created with a "Pointer"
>>> type that actually calls back with its address to its caller that
>>> then constructs a layout of SCM elements within its governed class.
>>> However, I have found no way to make this "parallel construction"
>>> where I could then swap out this specific "Pointer" behavior for
>>> regular SCM.
>> 
>> Similarly, it is necessary to call GC_INIT() on the first use on
>> MacOS, which can happen before ‘main’ is called. So for that, I let
>> operator new/delete call a function pointer which on the first call
>> initializes, and also changes the pointer to the ordinary function
>> pointer on future calls.
> 
> The ordinary function pointer.  I see.

Right. GC_malloc and GC_malloc_uncollectable.





Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Dan Eble
On May 5, 2020, at 11:09, David Kastrup  wrote:
> I'd like to come up with an allocator/container programming model
> comparatively similar to the STL one so that one could mostly steal the
> implementations and "just" add the required Scheme awareness while
> removing of destruction/deallocation tracking.

It might be difficult for me to devote the time to this discussion that it 
deserves, but I want to say that I've been working on C++ wrappers that allow 
walking SCM lists (of immediate values or Smobs) with standard iterator syntax 
and semantics.  I suppose that's tangential to what you are talking about, but 
it would be disappointing if we did significant overlapping work.

Regards,
— 
Dan




Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread David Kastrup
Hans Åberg  writes:

>> On 5 May 2020, at 17:09, David Kastrup  wrote:
>> 
>> One reason is that we want to get rid of finalisers particularly in
>> connection with the Boehm GC.  Finalisers are called when garbage is
>> collected, the collection of garbage is typically indicative of the
>> expected lifetime of our objects (there are a few that might be
>> unambiguously dead before), and thus it will cause C++ destructors to be
>> called.  And those trigger, among other things, the deallocation of
>> memory resources, but we'd rather want the deallocation to be determined
>> by Scheme mechanisms and not have any resources in need of destruction
>> other than falling into oblivion.
>
> For the GC objects, I use operator new/delete with an extra argument,
> the latter with empty body, wrapped in structure similar to
> std:shared_ptr. Then the memory resources of those objects are only
> handled by the GC, regardless of finalizers.

That does not sound like it has anything to do with SCM garbage
collection.  Nor is it phrased in a manner where I would understand what
you are talking about.  It is not clear what a "GC object" is supposed
to be, it is not clear what "I use" is supposed to mean with regard to
where and how it is being used, "operator new/delete with an extra
argument, the latter with empty body" presumably means that operator
delete rather than the extra argument have an empty body.  It is not
clear what it is supposed to buy us if operator delete has an empty body
when calling operator delete is something triggered by the destructor
and not having to call the destructor is a prerequisite for the life
time being determined by garbage collection without finalisation hook.

It is not clear _what_ is "wrapped in structure similar to
std:shared_ptr" and how that is supposed to interact with Scheme.

>> I've tried to come up with a clever callback scheme where the first
>> use of a structure causes fake nodes to be created with a "Pointer"
>> type that actually calls back with its address to its caller that
>> then constructs a layout of SCM elements within its governed class.
>> However, I have found no way to make this "parallel construction"
>> where I could then swap out this specific "Pointer" behavior for
>> regular SCM.
>
> Similarly, it is necessary to call GC_INIT() on the first use on
> MacOS, which can happen before ‘main’ is called. So for that, I let
> operator new/delete call a function pointer which on the first call
> initializes, and also changes the pointer to the ordinary function
> pointer on future calls.

The ordinary function pointer.  I see.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Hans Åberg


> On 5 May 2020, at 17:09, David Kastrup  wrote:
> 
> One reason is that we want to get rid of finalisers particularly in
> connection with the Boehm GC.  Finalisers are called when garbage is
> collected, the collection of garbage is typically indicative of the
> expected lifetime of our objects (there are a few that might be
> unambiguously dead before), and thus it will cause C++ destructors to be
> called.  And those trigger, among other things, the deallocation of
> memory resources, but we'd rather want the deallocation to be determined
> by Scheme mechanisms and not have any resources in need of destruction
> other than falling into oblivion.

For the GC objects, I use operator new/delete with an extra argument, the 
latter with empty body, wrapped in structure similar to std:shared_ptr. Then 
the memory resources of those objects are only handled by the GC, regardless of 
finalizers.

> I've tried to come up with a clever callback scheme where the first use
> of a structure causes fake nodes to be created with a "Pointer" type
> that actually calls back with its address to its caller that then
> constructs a layout of SCM elements within its governed class.  However,
> I have found no way to make this "parallel construction" where I could
> then swap out this specific "Pointer" behavior for regular SCM.

Similarly, it is necessary to call GC_INIT() on the first use on MacOS, which 
can happen before ‘main’ is called. So for that, I let operator new/delete call 
a function pointer which on the first call initializes, and also changes the 
pointer to the ordinary function pointer on future calls.




Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread David Kastrup
Jonas Hahnfeld  writes:

> Am Dienstag, den 05.05.2020, 17:09 +0200 schrieb David Kastrup:
>> I am currently digging back and forth regarding implementation of our
>> Smobs (Scheme objects) and garbage collection and STL, and I think I am
>> converging on the realisation that we'll have to end up duplicating
>> those parts of STL that we are using.
>
> Please forgive my ignorance, but I'm missing a bit of context. Are we
> talking about vector/lists/... of Smobs? Or is the issue that Smobs
> contain STL containers?

Vectors/lists of Smobs are inherently not protected from garbage
protection even when in "auto" stack space or other GC-scanned space
(Guile-2 has different options there).  That's a problem.  One important
GC-scanned space that we'd ultimately want to automate scanning of are
Smobs.  STL containers cannot be automatically scanned even when they
are formally located in scanned memory.

> In any case I'm not really fond of duplicating code. Given that it
> seems to "work" right now, IMHO this needs to give some very clear
> advantage over keeping the status quo.

GC performance remains a major troublesome point.

I want to get the point to where we don't need mark hooks, and where we
don't need finalisation hooks.  Either are causing performance problems,
and with Guile2, they also have a history of causing stability problems
as well as "nobody wants to do that, so we are not really interested in
your problems" reactions from Guile developers.

What I was writing about is to reduce the amount of code duplication by
not considering use of STL as free for the taking (which might cause
more and more need for reimplementation of stuff).  I do not see that at
the current point of time regarding both code bases and standards
(reflection is not in current C++ standards though it may appear at some
point of time) we have a reasonable chance to make STL free for the
taking.  The underlying dynamic allocation and destruction model is not
really a perfect match to what Scheme does and expects, and what the
garbage collectors we are dealing with are good at.

But I am not arguing API duplication: there is a point in making the
semantics of SCM-aware reimplementations follow STL Container semantics.
That will allow us at one point in the future when the SCM memory model
is a better match to STL allocators, to go back to STL.

But the different lifetime models will always mean that we are pressing
our luck when using STL under the assumption that running destructors is
optional.

I might still try coming up with some reflection engine that would work
with STL without significant performance impact and some reliability,
but I would not expect it to come cost free.

-- 
David Kastrup



Re: Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread Jonas Hahnfeld
Am Dienstag, den 05.05.2020, 17:09 +0200 schrieb David Kastrup:
> I am currently digging back and forth regarding implementation of our
> Smobs (Scheme objects) and garbage collection and STL, and I think I am
> converging on the realisation that we'll have to end up duplicating
> those parts of STL that we are using.

Please forgive my ignorance, but I'm missing a bit of context. Are we
talking about vector/lists/... of Smobs? Or is the issue that Smobs
contain STL containers?

In any case I'm not really fond of duplicating code. Given that it
seems to "work" right now, IMHO this needs to give some very clear
advantage over keeping the status quo.

Jonas


signature.asc
Description: This is a digitally signed message part


Almost, but not quite: C++ STL in LilyPond

2020-05-05 Thread David Kastrup


Hi,

I am currently digging back and forth regarding implementation of our
Smobs (Scheme objects) and garbage collection and STL, and I think I am
converging on the realisation that we'll have to end up duplicating
those parts of STL that we are using.

One reason is that we want to get rid of finalisers particularly in
connection with the Boehm GC.  Finalisers are called when garbage is
collected, the collection of garbage is typically indicative of the
expected lifetime of our objects (there are a few that might be
unambiguously dead before), and thus it will cause C++ destructors to be
called.  And those trigger, among other things, the deallocation of
memory resources, but we'd rather want the deallocation to be determined
by Scheme mechanisms and not have any resources in need of destruction
other than falling into oblivion.

Another point is the lack of reflection we have for getting a
description of STL nodes (like list nodes) for the sake of knowing which
elements may be allocated in what manner where.  It is conceivable that
Guile-2 will work tolerably just with the hand-waving knowledge "this
node may or may not contain pointers to other nodes somewhere" which
essentially is the information STL allocators can rely on but having to
rely on it does not seem like a good idea with regard to keeping things
well-collected.

I've tried to come up with a clever callback scheme where the first use
of a structure causes fake nodes to be created with a "Pointer" type
that actually calls back with its address to its caller that then
constructs a layout of SCM elements within its governed class.  However,
I have found no way to make this "parallel construction" where I could
then swap out this specific "Pointer" behavior for regular SCM.  Which
means that pointer/SCM juggling within the STL framework would always
involve some check (empty call or boolean variable) of whether we are
still in initialisation phase.  Not good.

The third reason is that STL allocators just have no knowledge about use
patterns and need to be able to allocate single SCM elements and
multiple SCM elements at request and keep track of them.  That is
particularly problematic since SCM allocations are principally
per-element and any multiple allocation needs to run through vectors and
similar.

And of course we'd want to make good use of what we have for generating
hash values etc in Scheme already.

So that means that I am going to reimplement some STL containers with
equal semantics (and hopefully also matching the "Container" definitions
so that at least container adaptors from the STL can make use of them)
and with more introspection into the Scheme-typical manners of
allocation and semantics.

In practical terms this means for other developers to avoid using or
introducing STL structures into Smobs.  Obviously, arrays and vectors
will have to get reimplemented in course of my plan, so there is no
point in avoiding them.  With regard to maps, ordered and unordered sets
and similar stuff, the strategy would be to vastly prefer any solution
that can be implemented in terms of Scheme functions.

To make this less painful, it is suggested that things like Scheme_hash
get reimplemented in a manner closely following the STL API so that we
can actually program in a C++ style (and encourage other contributors to
do so) without thinking too much.

I'd like to come up with an allocator/container programming model
comparatively similar to the STL one so that one could mostly steal the
implementations and "just" add the required Scheme awareness while
removing of destruction/deallocation tracking.

Using the actual code with trickery around it would be, of course, a lot
nicer.  But after a lot of deliberation and researching implementation
strategies, I am skeptical that making and keeping it work would be
robust and dependable (and efficient) enough to make this a really good
idea.

-- 
David Kastrup