[racket] fl vs. unsafe-fl

From: Dmitry Pavlov (dpavlov at ipa.nw.ru)
Date: Mon Sep 1 11:12:44 EDT 2014

Hello,

I was wondering whether operations with flonums and flvectors work
slower than their unsafe- counterparts in intensive numerical tasks.

Here is an example: res = a+b*c

#lang racket

(require racket/flonum
          racket/unsafe/ops)

(define n 1000)
(define k 100000)

(define a (make-flvector n 1.0))
(define b (make-flvector n 2.0))
(define c (make-flvector n 3.0))
(define res (make-flvector n))

(define (test-perf)
   (for ((i (in-range k)))
     (for ((j (in-range n)))
       (flvector-set!
        res j (fl+ (flvector-ref a j)
                   (fl* (flvector-ref b j)
                        (flvector-ref c j)))))))

(define (test-perf-unsafe-ops)
   (for ((i (in-range k)))
     (for ((j (in-range n)))
       (flvector-set!
        res j (unsafe-fl+ (flvector-ref a j)
                          (unsafe-fl* (flvector-ref b j)
                                      (flvector-ref c j)))))))

(define (test-perf-unsafe-flvec)
   (for ((i (in-range k)))
     (for ((j (in-range n)))
       (unsafe-flvector-set!
        res j (fl+ (unsafe-flvector-ref a j)
                   (fl* (unsafe-flvector-ref b j)
                        (unsafe-flvector-ref c j)))))))

(define (test-perf-unsafe-all)
   (for ((i (in-range k)))
     (for ((j (in-range n)))
       (unsafe-flvector-set!
        res j (unsafe-fl+ (unsafe-flvector-ref a j)
                          (unsafe-fl* (unsafe-flvector-ref b j)
                                      (unsafe-flvector-ref c j)))))))

(time (test-perf))
(time (test-perf-unsafe-ops))
(time (test-perf-unsafe-flvec))
(time (test-perf-unsafe-all))


Result (Racket 6.1.0.2, Linux 64-bit):

cpu time: 1704 real time: 1704 gc time: 0
cpu time: 1692 real time: 1692 gc time: 0
cpu time: 736 real time: 738 gc time: 0
cpu time: 684 real time: 682 gc time: 0


It turns out that replacing arithmetical operations
on flonums with their unsafe- counterparts makes no difference,
unless flvector access is unsafe- too; and even then, making
flvector access unsafe makes a huge improvement, while acceleration
from unsafe ops is about 7%.

I can imagine that the Racket's JIT skips checking the types of
arguments of fl+ and fl* because it figures that since they come
from flvectors, they must be flonums. Am I correct?

If I am correct, then are unsafe-fl arithmetical operations important
at all? Because in numerical programs, nearly every flonum is either
a constant, or a value taken from flvector, or a function argument
that can be traced to the same.


Regards,

Dmitry

Posted on the users mailing list.