On 12/11/2013 02:49 PM, Neil Toronto wrote:
On 12/11/2013 01:55 PM, Stephen Bloch wrote:
On Dec 11, 2013, at 2:36 PM, Neil Toronto wrote:

numeric primitives implemented in Typed Racket are faster than the
same primitives implemented in C.

Whoa!  How did that happen?

Whoa! That's not what I meant! O_o

I said "we might be getting close" to that. I haven't tried porting a
numeric C primitive to TR yet, but I have a hunch that it'll still be
slower. I'll try one now and report what I find.

Neil ⊥

I can't figure out why `flsinh' is faster to call from untyped Racket than `sinh'. All my tests with a Typed Racket `magnitude' show calls from untyped code are significantly slower, except in the one case that it computes Euclidean distance. That case is only twice as slow.

I've attached the benchmark program. The `magnitude*' function is more or less a direct translation of `magnitude' from "number.c" into Typed Racket. Here's a summary of the results I get on my computer, in milliseconds, for 5 million calls from untyped Racket, by data type.


Function         Flonum  Rational  Fixnum  Integer  Float-Complex
-------------------------------------------------------------------
magnitude*         385      419      378     414         686
magnitude           59       44       40      40         390


The only one that's close in relative terms is Float-Complex. The others just call `abs'. The decompiled code doesn't show any inlining of `magnitude', so this comparison should be good.

I'll bet checking the return value contract (which is unnecessary) is the main slowdown. It has to check for number of values.

For comparison, here are the timings for running the benchmarks in TR with #:no-optimize:


Function         Flonum  Rational  Fixnum  Integer  Float-Complex
-------------------------------------------------------------------
magnitude*          45       70*      37     102*       318
magnitude           61       45       39      91*       394

                                              * = unexpectedly high


Here's what I understand from comparing the numbers:

* Except for non-fixnum integers, calling `magnitude' in TR is just as fast as in untyped Racket. I have no idea why it would be slower on big integers. That's just weird.

* Calling `abs' in Racket is faster than calling `scheme_abs' in C, except on rationals and big integers.

* Operating on flonums in Typed Racket, using generic numeric functions, is faster than doing the same in C.

Overall, it looks like the TR code is within the same order of magnitude (pun not intended) as the C code. I would love to try this benchmark with either 1) a `magnitude*' with an `AnyValues' return type; or 2) a contract boundary that doesn't check TR's return types for first-order functions.

(I managed to make a `magnitude*' with type Number -> AnyValues, but TR couldn't make a contract for it.)

Neil ⊥

#lang racket

(module typed-defs typed/racket
  (require math/base)
  
  (provide magnitude*)
  
  (: magnitude* (Number -> Any))
  (define (magnitude* z)
    (cond [(real? z)  (abs z)]
          [else
           (define r (abs (real-part z)))
           (define i (abs (imag-part z)))
           (cond [(eq? r 0)  i]
                 [else
                  (let-values ([(r i)  (if (i . < . r) (values i r) (values r 
i))])
                    (cond [(zero? r)  (exact->inexact i)]
                          [(= i +inf.0)  (if (eqv? r +nan.0) +nan.0 +inf.0)]
                          [else
                           (define q (/ r i))
                           (* i (sqrt (+ 1 (* q q))))]))])]))
  )

;(module test typed/racket #:no-optimize
(module test racket
  
  (require math/base
           typed/racket/base
           (submod ".." typed-defs))
  
  (define x (random))
  (define y (/ (random 10000) (+ 1 (random 10000))))
  (define i (random-integer (- (expt 2 20)) (expt 2 20)))
  (define n (let: loop : Integer ()
              (define n (random-integer (- (expt 2 128)) (expt 2 128)))
              (if (fixnum? n) (loop) n)))
  (define z (make-rectangular (random) (random)))
  
  (define-syntax-rule (test-one-arg-fun f x)
    (begin
      (printf "(~a ~a)~n" 'f 'x)
      (for ([_  (in-range 5)])
        (time (for ([_  (in-range 5000000)])
                (f x))))
      (newline)))
  
  (test-one-arg-fun magnitude* x)
  (test-one-arg-fun magnitude x)
  (test-one-arg-fun magnitude* y)
  (test-one-arg-fun magnitude y)
  (test-one-arg-fun magnitude* i)
  (test-one-arg-fun magnitude i)
  (test-one-arg-fun magnitude* n)
  (test-one-arg-fun magnitude n)
  (test-one-arg-fun magnitude* z)
  (test-one-arg-fun magnitude z)
  )

(require 'test)
_________________________
  Racket Developers list:
  http://lists.racket-lang.org/dev

Reply via email to