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
Afficher les réponses par date
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
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
On 2013-04-24, at 3:32 PM, Mikael mikael.rcv@gmail.com wrote:
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)))
Here are some other options:
(##flonum.->fixnum 1.5e6)
1500000
(##flonum.->exact-int 1.5e20)
150000000000000000000
(##flonum.<-fixnum 123456)
123456.
(##flonum.<-exact-int 1234567890123456)
1.234567890123456e15
Marc
On 2013-04-24, at 7:55 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
On 2013-04-24, at 3:32 PM, Mikael mikael.rcv@gmail.com wrote:
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)))
Here are some other options:
(##flonum.->fixnum 1.5e6)
1500000
(##flonum.->exact-int 1.5e20)
150000000000000000000
(##flonum.<-fixnum 123456)
(##flonum.<-exact-int 1234567890123456)
1.234567890123456e15
And also these primitives:
(##flonum->fixnum 12.3)
12
(##fixnum->flonum 123)
123.
These are inlined by gsc, so they should run at the same speed as C.
Marc
Hi!
Aha.
Is there any difference between ##flonum->fixnum , ##flonum.->fixnum and ##fl->fx?
(And in the other direction, ##fixnum->flonum , ##flonum.<-fixnum and ##fl<-fx )
Brgds
2013/4/25 Marc Feeley feeley@iro.umontreal.ca
On 2013-04-24, at 7:55 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
On 2013-04-24, at 3:32 PM, Mikael mikael.rcv@gmail.com wrote:
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)))
Here are some other options:
(##flonum.->fixnum 1.5e6)
1500000
(##flonum.->exact-int 1.5e20)
150000000000000000000
(##flonum.<-fixnum 123456)
(##flonum.<-exact-int 1234567890123456)
1.234567890123456e15
And also these primitives:
(##flonum->fixnum 12.3)
12
(##fixnum->flonum 123)
These are inlined by gsc, so they should run at the same speed as C.
Marc
On 2013-04-25, at 8:54 AM, Mikael mikael.rcv@gmail.com wrote:
Hi!
Aha.
Is there any difference between ##flonum->fixnum , ##flonum.->fixnum and ##fl->fx?
(And in the other direction, ##fixnum->flonum , ##flonum.<-fixnum and ##fl<-fx )
They are all equivalent. The ##fixnum.xxx and ##flonum.xxx primitives (with the period) are the old names that haven't been removed yet.
Marc
OK, data is good.
Here is what Gambit expands your code to, with some annotations:
heine:~/Downloads> gsc -c -expansion conversion.scm Expansion:
(define noop (lambda () #!void))
The following implies that gsc could expand (floor n) better as:
(cond ((fixnum? n) n) ((and ('#<procedure #2 ##flonum?> n) ('#<procedure #3 ##flfinite?> n))('#<procedure #4 ##flfloor> n)) (else('#<procedure #5 floor> n))))
and it could expand inexact->exact better (after defining flfixnum?) as
(cond ((fixnum? n) n) ((and (flonum? n) (flinteger? n)(flfixnum? n)) (flonum->fixnum n)) (else (<inexact->exact> n))
(define ->integer (lambda (n) (lambda () (let ((temp.5 (if (and ('#<procedure #2 ##flonum?> n) ('#<procedure #3 ##flfinite?> n)) ('#<procedure #4 ##flfloor> n) ('#<procedure #5 floor> n)))) (if ('#<procedure #6 ##fixnum?> temp.5) temp.5 ('#<procedure #7 inexact->exact> temp.5))))))
(define ->flonum (lambda (n) (lambda () (if ('#<procedure #6 ##fixnum?> n) ('#<procedure #8 ##fl<-fx> n) (if ('#<procedure #2 ##flonum?> n) n ('#<procedure #9 exact->inexact> n))))))
This following machinery seems pretty heavy. I'd suggest
(define (test t #!optional (seconds 1.)) (let loop ((n 1)) (let ((start-time (cpu-time))) (do ((i 0 (fx+ i 1))) ((fx= i n)) (t)) (let ((end-time (cpu-time))) (if (<= seconds (fl- end-time start-time)) (pp (/ n (fl- end-time start-time))) (loop (fx* n 2)))))))
(define test (lambda (t #!optional (seconds 5)) (let ((at ('#<procedure #10 ##box> 0))) (let ((th (thread-start! (make-thread (lambda () (letrec ((loop (lambda (t at) (let ((begin-temp.1 (t))) (let ((begin-temp.0 ('#<procedure #11 ##set-box!> at (let ((temp.7 ('#<procedure #12 ##unbox> at))) (if ('#<procedure #6 ##fixnum?> temp.7) (let ((temp.9 ('#<procedure #13 ##fx+?> temp.7 1))) (if temp.9 temp.9 ('#<procedure #14 fx+> temp.7 1))) ('#<procedure #14 fx+> temp.7 1)))))) (loop t at)))))) (loop t at))))))) (let ((begin-temp.3 (thread-sleep! seconds))) (let ((r ('#<procedure #12 ##unbox> at))) (let ((begin-temp.2 (thread-terminate! th))) (let ((temp.12 (if (and ('#<procedure #2 ##flonum?> seconds) ('#<procedure #2 ##flonum?> r)) ('#<procedure #15 ##fl/> r seconds) ('#<procedure #16 /> r seconds)))) (if ('#<procedure #6 ##fixnum?> temp.12) ('#<procedure #8 ##fl<-fx> temp.12) (if ('#<procedure #2 ##flonum?> temp.12) temp.12 ('#<procedure #9 exact->inexact> temp.12)))))))))))
With that, my rates are (first for void, then the four ->integer, then the four ->flonum):
76082403.02475469
18077766.69130593 2113932.395354323 12632656.242117403 1560283.434666285
34377410.006859235 69323006.15509051 1230645.7282318561 69180045.37858963
And, with (declare (not safe)) I get
105510445.88390666
18807367.927219782 2444080.701261633 13230418.609566122 1569627.036643679
47124065.98183112 92942772.26488659 1327228.230636181 92942772.26488681
2013/4/24 Bradley Lucier lucier@math.purdue.edu
OK, data is good.
Here is what Gambit expands your code to, with some annotations:
heine:~/Downloads> gsc -c -expansion conversion.scm Expansion:
(define noop (lambda () #!void))
The following implies that gsc could expand (floor n) better as:
(cond ((fixnum? n) n) ((and ('#<procedure #2 ##flonum?> n) ('#<procedure #3 ##flfinite?> n)) ('#<procedure #4 ##flfloor> n)) (else ('#<procedure #5 floor> n))))
and it could expand inexact->exact better (after defining flfixnum?) as
(cond ((fixnum? n) n) ((and (flonum? n) (flinteger? n) (flfixnum? n)) (flonum->fixnum n)) (else (<inexact->exact> n))
Wait, is this a feature suggestion for Gambit, did I understand you right by that?
(As in, currently Gambit expands it another way and now you suggest this particular way that you've given here)
(define ->integer (lambda (n) (lambda () (let ((temp.5 (if (and ('#<procedure #2 ##flonum?> n) ('#<procedure #3 ##flfinite?> n)) ('#<procedure #4 ##flfloor> n) ('#<procedure #5 floor> n)))) (if ('#<procedure #6 ##fixnum?> temp.5) temp.5 ('#<procedure #7 inexact->exact> temp.5))))))
(define ->flonum (lambda (n) (lambda () (if ('#<procedure #6 ##fixnum?> n) ('#<procedure #8 ##fl<-fx> n) (if ('#<procedure #2 ##flonum?> n) n ('#<procedure #9 exact->inexact> n))))))
This following machinery seems pretty heavy. I'd suggest
(define (test t #!optional (seconds 1.)) (let loop ((n 1)) (let ((start-time (cpu-time))) (do ((i 0 (fx+ i 1))) ((fx= i n)) (t)) (let ((end-time (cpu-time))) (if (<= seconds (fl- end-time start-time)) (pp (/ n (fl- end-time start-time))) (loop (fx* n 2)))))))
(define test (lambda (t #!optional (seconds 5)) (let ((at ('#<procedure #10 ##box> 0))) (let ((th (thread-start! (make-thread (lambda () (letrec ((loop (lambda (t at) (let ((begin-temp.1 (t))) (let ((begin-temp.0 ('#<procedure #11 ##set-box!> at (let ((temp.7 ('#<procedure #12 ##unbox> at))) (if ('#<procedure #6 ##fixnum?> temp.7) (let ((temp.9 ('#<procedure #13 ##fx+?> temp.7 1))) (if temp.9 temp.9 ('#<procedure #14 fx+> temp.7 1))) ('#<procedure #14 fx+> temp.7 1)))))) (loop t at)))))) (loop t at))))))) (let ((begin-temp.3 (thread-sleep! seconds))) (let ((r ('#<procedure #12 ##unbox> at))) (let ((begin-temp.2 (thread-terminate! th))) (let ((temp.12 (if (and ('#<procedure #2 ##flonum?> seconds) ('#<procedure #2 ##flonum?> r)) ('#<procedure #15 ##fl/> r seconds) ('#<procedure #16 /> r seconds)))) (if ('#<procedure #6 ##fixnum?> temp.12) ('#<procedure #8 ##fl<-fx> temp.12) (if ('#<procedure #2 ##flonum?> temp.12) temp.12 ('#<procedure #9 exact->inexact> temp.12)))))))))))
With that, my rates are (first for void, then the four ->integer, then the four ->flonum):
76082403.02475469
18077766.69130593 2113932.395354323 12632656.242117403 1560283.434666285
34377410.006859235 69323006.15509051 1230645.7282318561 69180045.37858963
And, with (declare (not safe)) I get
105510445.88390666
18807367.927219782 2444080.701261633 13230418.609566122 1569627.036643679
47124065.98183112 92942772.26488659 1327228.230636181 92942772.26488681
Basically, nice numbers!
Numbers that are in the millions are good really.
I'd love to see the flonum to integer speed a bit higher (yellow above), I mean in C that's just double d; int i = (int) d; .
I tried it out in C and got 47,619,047 per second, code below.
Now, Gambit does lots of typechecking and boxing and stuff, though shouldn't like 5-10 million per second be reachable?
That I got 10-50 million per second of the other operations that do basically the same thing as this in Gambit led me to think that there might be some other definition of |->integer| that could get to this result, though not clear right now what that definition would be.
Addressed this because I thought it's like a generally relevant thing.
Brgds
C test:
#include <stdio.h> #include <stdlib.h>
int main() { double d = (double)rand()/100.0; int x; int i; for (i = 0; i < 1000000000; i++) { x = (int) d; } }
g++ cfile.c; time ./a.out
On Apr 24, 2013, at 5:17 PM, Mikael wrote:
I'd love to see the flonum to integer speed a bit higher (yellow above), I mean in C that's just double d; int i = (int) d; .
If you want to do that, you can do (##flonum->fixnum d) (undocumented, internal function):
(##flonum->fixnum 5.5)
5
(##flonum->fixnum -5.5)
-5
But that isn't floor; that doesn't work for large flonums.
If you want C, you can write C in Gambit. Many people don't understand the semantics of the numerical operations in Scheme generally, or in Gambit in particular.
Brad
Hi Brad,
Yeah found ##flonum.->exact-int at the same time as you wrote.
(From tests, for reference, floor is ~15 million per sec always, while inexact->exact takes time for flonums and rationals and for the others is a noop.)
So a performant, general definition goes:
(define ->integer (let ((fixnum-max-as-flonum (##fixnum->flonum ##max-fixnum))) (lambda (n) (declare (not safe)) (cond ((##fixnum? n) n) ((##bignum? n) n) ; Bignums are integer by definition ((##flonum? n) (if (##fl< n fixnum-max-as-flonum) (##flonum.->fixnum n) (##flonum.->exact-int n))) ((##ratnum? n) (##inexact->exact (##floor n))) (else (error "Complex numbers not supported"))))))
(->integer 5)
5
(->integer 5.)
5
(->integer 10/7)
1
(->integer 1e25)
10000000000000000905969664
(->integer 1000000000000000000000000)
1000000000000000000000000
Note that it is a completely safe procedure, even while it has (declare (not safe)) as to make compilation more efficient.
Benchmark: (->integer 5) 41,862,119 (->integer 5.) 26,032,509 (->integer 536870912.) 2,625,793 (this is the max flonum + 1, as a fixnum) (->integer 1e15) 1,097,751 (->integer 1e25) 643,797 (->integer 10/7) 5,134,476 (->integer 1000000000000000000000000) 41,216,439
Well, this is as good as it goes then :)
Great!
Yes you certainly have a point about the semantics of Gambit/Scheme numerical operations; do you recommend any particular document for really getting them?
Thanks, Mikael
2013/4/25 Bradley Lucier lucier@math.purdue.edu
On Apr 24, 2013, at 5:17 PM, Mikael wrote:
I'd love to see the flonum to integer speed a bit higher (yellow above),
I mean in C that's just double d; int i = (int) d; .
If you want to do that, you can do (##flonum->fixnum d) (undocumented, internal function):
(##flonum->fixnum 5.5)
5
(##flonum->fixnum -5.5)
-5
But that isn't floor; that doesn't work for large flonums.
If you want C, you can write C in Gambit. Many people don't understand the semantics of the numerical operations in Scheme generally, or in Gambit in particular.
Brad
On 04/24/2013 10:01 PM, Mikael wrote:
Hi Brad,
((##ratnum? n) (##inexact->exact (##floor n)))
The floor of a ratnum is always exact, so you could save a trampoline call here by not calling inexact->exact.
About the semantics of numerical operations in Scheme---I don't really know what to say. I'd suggest thinking of all operations as the same as in grade-school mathematics with integers of arbitrary size, rational numbers, etc. Then layer on top of that some understanding of how flonums work and how they interact with other types of numbers. It's hard to get the conventions of other languages out of one's head.
Brad