Just for reference:

Here's a test of improving performance by throwing the task out in C as a quick hack:


Results:

64bit:
for integer (fixnum) (doesn't apply)
for flonum           14,287,125           - good
for rational         (doesn't apply)
for bignum integer 1 (doesn't apply)
for bignum integer 2 (doesn't apply)

32bit:
for integer (fixnum) (doesn't apply)
for flonum           13,993,639           - good
for rational         (doesn't apply)
for bignum integer 1 (doesn't apply)
for bignum integer 2 (doesn't apply)

Interesting that they were so close, as this is a 32bit install.

Also interesting that this runs at ~39% the speed of (exact->inexact [fixnum]).


So a general definition could be made out of this by

(define (->integer n)
  (if (flonum? n)
    (c-flonum-to-int n)
    (inexact->exact (floor n))))


Though appears like a hack and it's a minus that it requires C compilation.

If you have any more general definition at hand feel free to share.






(define c-flonum-to-int (c-lambda (double) int64 "___result = (___S64) ___arg1;"))

(define c-flonum-to-int32 (c-lambda (double) int32 "___result = (___S32) ___arg1;"))

(define (c-flonum-to-int-test1) (test (lambda () (c-flonum-to-int 5   ))))
(define (c-flonum-to-int-test2) (test (lambda () (c-flonum-to-int 5.  ))))
(define (c-flonum-to-int-test3) (test (lambda () (c-flonum-to-int 10/7))))
(define (c-flonum-to-int-test4) (test (lambda () (c-flonum-to-int 1e25))))
(define (c-flonum-to-int-test5) (test (lambda () (c-flonum-to-int 10000000000000000000000000))))

(define (c-flonum-to-int-test32-1) (test (lambda () (c-flonum-to-int32 5   ))))
(define (c-flonum-to-int-test32-2) (test (lambda () (c-flonum-to-int32 5.  ))))
(define (c-flonum-to-int-test32-3) (test (lambda () (c-flonum-to-int32 10/7))))
(define (c-flonum-to-int-test32-4) (test (lambda () (c-flonum-to-int32 1e25))))
(define (c-flonum-to-int-test32-5) (test (lambda () (c-flonum-to-int32 10000000000000000000000000))))


2013/4/24 Mikael <mikael.rcv@gmail.com>
Ah, realized that Gambit makes a difference internally between 1e25 and 10000000000000000000000000 . So added a test also for the latter.


Got 6,794,405 per second for |->integer| - fair enough!

And got 384,459 for |->flonum| - could be faster.


> (->integer-test5)
6794405.8
> (->flonum-test5)
384459.2


Def:
(define (->integer-test5) (test (->integer 10000000000000000000000000)))

(define (->flonum-test5) (test (->flonum 10000000000000000000000000)))



2013/4/24 Mikael <mikael.rcv@gmail.com>
Dear list,

I was keeping this question in the back of my mind for several years so taking the occasion to reflect it now:

Here follows the result of a simple benchmark of (exact->inexact n) and (exact->inexact (floor n)) where n is a flonum, fixnum or bignum integer, or rational.

The code was executed in compiled form, with both safe and interrupts-enabled set and without any consideration to C compiler flags beyond -O1 , to reflect the environment of a typical application (not for instance a specialized number crunching environment).

I remember a conversation on this topic like ~4y ago but don't remember any conclusion from it.


Looking at these numbers, I think they're great and all you can ask for, with two exceptions, being to-integer conversion of a flonum or integer bignum.

To get a better idea I experimented with heap size and live percent setting with those two, and got about the same results.


If you are aware of any definition of |->integer| that would perform much better, please feel free to share!




Result:

On a laptop CPU core, here's how many of the respective operation Gambit performs per second 

->integer of an integer (fixnum) 13,804,449
->integer of a flonum               260,932 (perhaps performs much better with another definition?)
->integer of a rational          10,130,272
->integer of an integer (bignum)    598,228 (perhaps performs much better with another definition?)


->flonum of an integer (fixnum)  36,550,882
->flonum of a flonum             47,756,845
->flonum of a rational              334,976
->flonum of an integer (bignum)  51,075,409



Test code:

(declare (block) (standard-bindings) (extended-bindings))

(define (noop) (void))

(define (->integer n)
  (lambda ()
    (inexact->exact (floor n))))

(define (->flonum n)
  (lambda ()
    (exact->inexact n)))

(define (test t #!optional (seconds 5))
  (let* ((at 0)
         (th (thread-start! (make-thread (lambda () (let loop ()
                                                      (t)
                                                      (set! at (fx+ at 1))
                                                      (loop)))))))
    (thread-sleep! seconds)
    (let ((r at))
      (thread-terminate! th)
      (exact->inexact (/ r seconds)))))


(define (noop-test) (test noop))

(define (->integer-test1) (test (->integer 5   )))
(define (->integer-test2) (test (->integer 5.  )))
(define (->integer-test3) (test (->integer 10/7)))
(define (->integer-test4) (test (->integer 1e25)))

(define (->flonum-test1) (test (->flonum 5   )))
(define (->flonum-test2) (test (->flonum 5.  )))
(define (->flonum-test3) (test (->flonum 10/7)))
(define (->flonum-test4) (test (->flonum 1e25)))


Test output:

> (time (noop-test))
(time (noop-test))
    5040 ms real time
    4932 ms cpu time (4896 user, 36 system)
    no collections
    2112 bytes allocated
    6 minor faults
    no major faults
57985446.4
> (time (->integer-test1))
(time (->integer-test1))
    5019 ms real time
    4884 ms cpu time (4872 user, 12 system)
    no collections
    1232 bytes allocated
    no minor faults
    no major faults
13804449.
> (time (->integer-test2))
(time (->integer-test2))
    5000 ms real time
    4792 ms cpu time (4656 user, 136 system)
    81 collections accounting for 187 ms real time (120 user, 52 system)
    1607420656 bytes allocated
    5110 minor faults
    no major faults
260932.6
> (time (->integer-test3))
(time (->integer-test3))
    5015 ms real time
    4888 ms cpu time (4872 user, 16 system)
    no collections
    2240 bytes allocated
    no minor faults
    no major faults
10130272.8
> (time (->integer-test4))
(time (->integer-test4))
    5003 ms real time
    4680 ms cpu time (4536 user, 144 system)
    158 collections accounting for 401 ms real time (304 user, 60 system)
    3110877424 bytes allocated
    no minor faults
    no major faults
598228.8
> (time (->flonum-test1))
(time (->flonum-test1))
    5011 ms real time
    4740 ms cpu time (4460 user, 280 system)
    297 collections accounting for 736 ms real time (524 user, 124 system)
    5848140864 bytes allocated
    no minor faults
    no major faults
36550882.2
> (time (->flonum-test2))
(time (->flonum-test2))
    5001 ms real time
    4840 ms cpu time (4800 user, 40 system)
    no collections
    2088 bytes allocated
    no minor faults
    no major faults
47756845.8
> (time (->flonum-test3))
(time (->flonum-test3))
    5005 ms real time
    4972 ms cpu time (4920 user, 52 system)
    65 collections accounting for 147 ms real time (120 user, 16 system)
    1286350504 bytes allocated
    no minor faults
    no major faults
334976.6
> (time (->flonum-test4))
(time (->flonum-test4))
    5002 ms real time
    4876 ms cpu time (4848 user, 28 system)
    no collections
    2080 bytes allocated
    no minor faults
    no major faults
51075409.8



2013/4/24 Bradley Lucier <lucier@math.purdue.edu>
On 04/24/2013 01:33 PM, Mikael wrote:
Hi Brad!

2013/4/24 Bradley Lucier <lucier@math.purdue.edu>
On 04/24/2013 12:37 PM, Zhen Shen wrote:

[...]
 
> Now, doing (declare (flonum)) at the top level, does this stop gambit
> from boxing flonums across function calls?

No.  Gambit keeps flonums unboxed inside a basic block, whenever there's
a jump (or the possibility of a jump), Gambit boxes up all the
still-needed flonums.

What about fixnums, would they remain unboxed in a loop?

Yes.  Fixnums are always "immediate" (not boxed) values.



Also btw, are there any tricks that can be applied to make it keep flonums and fixnums unboxed in loops, like, (declare (not interrupts-enabled)) or (not safe)?

Use (declare (not safe)) and flonum-specific operations to keep flonums unboxed in a basic block.  There's no way to keep them unboxed across jumps.  (With generic operations, flonums are boxed even in a basic block.)

Or, you can use an f64vector as an explicit "box" for your flonum and write monstrous code like this.

(define (Array-sum a)
  (f64vector-ref (Array-reduce (lambda (result y)
                                 (f64vector-set! result 0 (fl+ (f64vector-ref result 0) y))
                                 result)
                               (f64vector 0.)
                               a)
                   0))

Brad