This is the mail archive of the guile@sources.redhat.com mailing list for the Guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Again: Guile + Boehm's gc


Hello together!

I have thought a little more about the idea to use Boehm's collector
within guile and discovered a lot of benefits and also quite some
ideas for speeding things up within guile.  Maybe the following points
are not new to most of you, but I admit that I did not see all of the
points below before.  So, I hope that it brings something new to you.

* No need for the mail/real_main hack any more: Boehm's collector will
  automatically detect the top of the stack for us.  OK, this was the
  point everybody knew so far.

* No need for scm_protect/unprotecting things any more: These steps
  were necessary for cases when there were references to SCM objects
  from the C heap, as for example from C or C++ objects obtained via
  malloc/new.  But, since Boehm (as far as I understand it) also scans
  the heap for references, we would not have to care about protecting
  objects this way.

* No need to register objects in global variables: Boehm's collector
  automatically scans the data sections of the code.  This would
  eliminate a lot of newbie bugs, because this is something that people
  tend to forget.

* No trouble any more about initialization order of cell elements:
  Currently we have two problems here:

  - Our first problem is that guile's gc would coredump if there
    occured objects, where the cell type is already set, but the cell
    content is still rubbish.  For example, a scheme vector object
    where the vector type is set, but the contents are not initialized
    yet.  If the gc comes across such a thing, it will assume that the
    vector contents are valid, and thus would try to access memory
    pointed to by uninitialized rubbish that happens to look like a
    cell pointer.
    
  - The second initialization problem is about the risc to lose
    references.  As described before, objects in guile are initialized
    by setting all but the first (the type) cell element, and only at
    the end setting the cell type.  But, guile coders have always to
    be aware of the fact that the objects referenced by the cell are
    not protected from the not-yet-fully-initialized cell: As long as
    the cell type is not set, the gc will _not_ scan references from
    the rest of the cell.  (Just a side note: This could easily be
    fixed in guile: If during gc a cell of type 'allocated' was
    detected, this cell could be scanned conservatively.)

* No more gc-aware coding rules: If for example we currently want to
  iterate over a vector, we may not do it by first extracting the
  pointer to the vector contents from the SCM object and then in the
  loop use just that extracted pointer: This way, the reference to the
  SCM object may be lost in the loop and, while the iteration scans the
  vector contents, gc deletes the vector including the malloced array
  with the vector contents :-(.  Thus, guile's coding rules demand that
  during the loop we have to always start from the SCM vector object
  with _every_ access to a vector element, i. e. we have to always
  re-read the pointer to the vector contents from the SCM object.
  Similar problems occur for strings, bignums, structs, ...  With
  Boehm's collector, this is not a problem any more, because Boehm would
  treat the vector content array and the SCM object pointing to it
  separately.

* No separation of SCM cell and corresponding malloced area.  For
  example with strings, vectors, bignums etc. guile uses the following
  approach: The object is referenced by a cell, where the first element
  (the type element) contains the type information _and_ some length
  information, and the second cell element holds a pointer to a malloced
  area where the actual contents of the string, vector etc. are stored.
  With Boehm's gc, we could just use _one_ continuous memory region for
  these types: A string with n characters would just become a single
  memory region, where the first couple of bytes hold the type and
  length information, and then follow the characters.  This means:

  - a performance improvement since there is just _one_ malloc to
    be done.  This counts twice, because first guile saves one malloc
    when creating the object, and second the garbage collector has to
    care about one area less.
  - a performance improvement due to better locality: The string
    spine and the string contents are now in the same memory region.
    This improves virtual memory and cache performance.
  - a size improvement, since the position of the
    string/vector/... contents is always known relative to the
    object's base address.
  - a clarity improvement within guile, since for strings we can
    then use a nice struct type as (for example)
    struct string {
        scm_bits_t type_and_length;
        char[1] contents;
    };

* A simplified type system.  A lot of trouble with the current type
  system comes from the fact that we try to pack as much as possible
  into two words.  Taking up the string example: The first cell holds
  the type _and_ length information in order to have the second cell
  available for the pointer to the actual string region.  This makes the
  type checks more complicated, since at every place where type checks
  are performed the type value has to be masked to only get the relevant
  bits.  Since with Boehm's gc we simply use malloced areas of arbitrary
  size, the string type could be defined as:
    struct string {
        scm_bits_t type;
        unsigned long int length;
        char[1] contents;
    };
  It is stated the the combined encoding of string type and length
  helps to avoid one memory access, since the length is available with
  the type.  However, the way that caches work, the length info of the
  string in the example above should be in the cache together with the
  type.  Thus, the penalty (if any) should be small.

* Using fast code instead of gc aware code.  This includes getting rid
  of most calls to SCM_DEFER/ALLOW_INTS, since in most places these are
  inserted only to deal with the cell initialization problem described
  above.  Further it allows to get rid of all initialization code for
  global variables, all scm_protect stuff, all permanent object stuff
  etc.  Further, from that point we are allowed to use optimizations
  like first extract the address of vectors and then iterate over the
  contents _without_ having to keep the address of the vector's spine.

The more I think about it, the more I like the idea to try out using
guile with Boehm's gc.  However, as I have described, it will cause a
lot of work to get _realistic_ benchmarking results, since it is hard
to tell how much overhead there is in guile just _because_ of the
current gc.

Best regards
Dirk


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]