Re: Almost, but not quite: C++ STL in LilyPond
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
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
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
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
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
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
> 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
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
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
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
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
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
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
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
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
> 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
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
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
> 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
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
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
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