[racket-dev] [plt] Push #27909: master branch updated

From: Neil Toronto (neil.toronto at gmail.com)
Date: Wed Dec 11 19:18:11 EST 2013

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 ⊥

-------------- next part --------------
#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)

Posted on the dev mailing list.