I'd like to thank you too Peter, for this very informative overview of
typed-records which I'm looking forward to start using!

K.

On Wed, Jul 27, 2016 at 6:31 PM, Matt Welland <mattrwell...@gmail.com>
wrote:

> Top posting just to say thanks to Peter, typed-records are a huge benefit
> for me. I much appreciate the detailed response.
>
> On Mon, Jul 25, 2016 at 11:49 PM, Peter Bex <pe...@more-magic.net> wrote:
>
>> On Sun, Jul 24, 2016 at 11:06:32AM -0700, Matt Welland wrote:
>> > For years now I've been using inlined vectors instead of records or
>> coops
>> > for data structures due to performance concerns. Recently I was training
>> > someone on how to maintain my code and I felt quite lame explaining the
>> > vector records. So I started switching to defstruct records. However
>> I've
>> > started to hit some performance issues and one factor seems to be the
>> > records (more work to do to confirm this).
>> >
>> > Below is a simplistic benchmark comparing using inlined vectors, inlined
>> > vectors with a type check, defstruct and coops. Where performance
>> matters
>> > using vectors or type checked vectors seems to help. The benchmark seems
>> > enough to hint to me to switch back to vectors - especially in cases
>> where
>> > I'm slinging around 10's of thousands of records.
>>
>> Hello Matt,
>>
>> The reason for this is pretty simple: record types do not have inlineable
>> accessors.  This means that accessors (and constructors) will require
>> that they are invoked in full CPS context.  If you have a procedure which
>> calls such an accessor, it will always be split up into at least 2 C
>> functions.
>>
>> This is because records have an API defined by the procedures which are
>> created by the define-record(-type)/defstruct macros; the objects
>> themselves do not contain enough information to have a generic accessor,
>> and when you're calling an accessor, the compiler doesn't know that it's
>> a record accessor.  Vectors, on the other hand, have a common interface:
>> they can be referenced by one and the same accessor: vector-ref.
>> This is inlineable in C, as C_i_vector_ref().  In the next version of
>> CHICKEN, we'll even be able to rewrite those directly to C_slot() if
>> the vector is of a known length and the index is within bounds.
>>
>> > My question: can anyone offer insight into a better way to balance
>> > performance with elegance/flexibility of records?
>>
>> Luckily, there's a simple solution.  Felix wrote a wonderful little egg
>> called "typed-records", which provides drop-in replacements for
>> define-record(-type) *and* defstruct which will emit specialisations
>> for records.
>>
>> That is, if an object is known to be a record of the given type, the
>> accessor is rewritten to (##sys#slot <record> <slot>).
>>
>> For instance, with (defstruct foo bar qux), (foo-qux x) is rewritten
>> to (##sys#slot x 2) if x is known to be of type (struct foo).
>> The only disadvantage of this is that if you change your definition
>> of a record, you'll need to recompile all the units that access these
>> records, because they've been inlined as numbered slot references.
>>
>> Changing the sample code in your e-mail by simply replacing "defstruct"
>> in your "use" line with "typed-records" results in noticeable
>> performance improvement:
>>
>> Using vectors
>> 1.148s CPU time, 33162750 mutations, 0/2309 GCs (major/minor)
>> Using vectors (safe mode)
>> 2.308s CPU time, 0.02s GC time (major), 49744125 mutations, 15/20266 GCs
>> (major/minor)
>> Using defstruct
>> 1.66s CPU time, 33162750 mutations, 5/11665 GCs (major/minor)
>> Using coops
>> 20.608s CPU time, 0.628s GC time (major), 33162760 mutations, 960/231731
>> GCs (major/minor)
>>
>> Before making that one-word replacement, it was:
>>
>> Using vectors
>> 1.112s CPU time, 33162750 mutations, 0/2309 GCs (major/minor)
>> Using vectors (safe mode)
>> 2.34s CPU time, 0.02s GC time (major), 49744125 mutations, 15/20266 GCs
>> (major/minor)
>> Using defstruct
>> 4.224s CPU time, 0.012s GC time (major), 33162750 mutations, 36/40736 GCs
>> (major/minor)
>> Using coops
>> 20.572s CPU time, 0.608s GC time (major), 33162760 mutations, 938/231753
>> GCs (major/minor)
>>
>> Not too bad, especially considering that typed-records is _safe_: it
>> will only perform the rewrites when the compiler can prove that the given
>> object is of the required type.
>>
>> If it cannot, you can always add a check to your code like this:
>> (if (not (my-type? x)) (error "wrong type") (begin ...))
>> The use of a predicate will tell the compiler that in the else branch,
>> x can only be of the required type.
>>
>> If you're using separate compilation, you need to remember to ask the
>> compiler to emit the type declarations to a file, and use that file while
>> compiling the users of the API.
>>
>> Cheers,
>> Peter
>>
>> _______________________________________________
>> Chicken-users mailing list
>> Chicken-users@nongnu.org
>> https://lists.nongnu.org/mailman/listinfo/chicken-users
>>
>>
>
> _______________________________________________
> Chicken-users mailing list
> Chicken-users@nongnu.org
> https://lists.nongnu.org/mailman/listinfo/chicken-users
>
>
_______________________________________________
Chicken-users mailing list
Chicken-users@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-users

Reply via email to