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