source: project/release/4/numbers/trunk/NOTES @ 31493

Last change on this file since 31493 was 31493, checked in by sjamaan, 6 years ago

numbers: Add some important notes about the 'design' of the new numeric system, so I won't forget

File size: 4.9 KB
1Some quick notes while everything's still fresh in my mind.  These
2might also be useful when integrating this in core.
4=== Integration of the numeric tower into the type system
6The basic idea in this code is that there are two distinct types of
7numbers: "basic" and "extended".  The basic numbers are the
8fundamental ones that have always been known by CHICKEN core, with the
9new addition of bignums.  The extended numbers exist *ONLY* in Scheme,
10which means that to C they're just structure/record objects.  This
11rule is broken in only a handful of places (eqv, assq) for performance
14In the CHICKEN 4 numbers egg, this is faked out because we can't truly
15extend the core number types, so bignums are structures as well.  But
16for integration into core, this is changed to a true type.
18In intermediate versions of the "numbers" egg, we had to pass a
19failure continuation, which meant creating an extra closure object
20upon every call to numeric operations.  But now, in order to avoid any
21performance impact, the Scheme procedures are invoked as an
22"exception", much like the way the error handler is invoked through
23barf() in cases of error.  This allows us to only pass the arguments
24to a numeric operation, pretending the implementation is native C.
26=== Performance impact
28I've tried very hard to keep the performance of basic numeric
29operations exactly the same as in core.  In particular, the various
30checks for number types are done in exactly the same order as
31everywhere in core:
33- Is it a fixnum?
34- Is it an immediate?  If so, barf.
35- Does the header have a flonum tag? (before, this was combined with the immediate check)
36- Does the header have a bignum tag? (normally, we'd have an "else barf()" at this point)
37- Look up the numeric operation's matching Scheme procedure for extended numeric types, and call it (or barf, if it's not defined for these)
39This means that "generic" numeric code should incur ZERO performance
40penalty for functions that are non-allocating and inlineable....
42Unfortunately, that's where the good part ends.  Any operation that
43results in a fresh number is no longer inlineable, because in case of
44bignums they will need to allocate an unknown quantity of memory,
45which may require a GC.  The upshot is that every "allocating inline"
46procedure will now need to be called in primitive CPS context.  This
47is fundamental limitation that we can't do much about.
49In addition, the comparison functions (=, <, >, <=, >=) are no longer
50inline.  This is due to the fact that in order to correctly compare
51flonums, they need to be converted to a bignum and then compared.  We
52*could* decide to rip this out, but that would result in unexpected
53things, like: (< 19000000000000000.0 19000000000000001) => #f or
54(= 19000000000000000.0 190000000000000001) => #t
55These are currently the case, too.  This is due to precision loss from
56the fix->flo conversion (which means we drop from 62 bits to 54 bits).
57Because we _are_ comparing inexact numbers (which could already have
58lost information before comparing them), we could decide to ignore
59these edge cases and keep them like they are.  For the "=" function
60that would mean it can remain inlined and non-allocating.  For < and >,
61however, this doesn't help: in the case of ratnums we must multiply the
62numerator of x with the denominator of y and vice versa, and compare
63the results.  This means we're stuck with an allocating, non-inlineable
64function.  Because of this, I decided to keep the comparison functions
65all non-inlineable.
67Finally, the C implementation of the comparison functions as well as
68+, -, * and / are no longer vararg functions.  Instead, the variadic
69part is handled in Scheme, and the C implementation only compares two
70numbers at a time.  This shouldn't be too much of a performance impact
71considering they already have to be in CPS context anyway.  Plus,
72calls with two operations can easily be rewritten to a direct call,
73which leads us to...
75==== Specializations
77There's some light at the end of the tunnel: In critical
78number-crunching code, you'll usually be working with either integers
79or flonums (or you'd already be using the old numbers egg and
80everything would be shit-slow anyway).  These two situations are
81catered to specifically by specialized versions.  This is where the
82specialization/scrutiny stuff really shines: if we know something is a
83whole integer, we can use unsafe operations that only need to check a
84single bit to distinguish whether a number is fixnum or bignum, just
85like in the old situation we could this to distinguish between fixnum
86and flonum.
88This case should be easy to infer.  There's one caveat: do not use the
89"/" division operator, because this may result in a ratnum.  Instead,
90in fast code where you know you're dealing with integers, it's best to
91use "quotient", instead.  And of course, if you use the trigonometric
92operations you may get a flonum, which will also result in the generic
93number functions being used.
Note: See TracBrowser for help on using the repository browser.