Enhanced Primitive Support

This document describes work in progress in the prim, num, equal and equiv branches. This work is not promised to become part of Clojure, as is or otherwise, now or in the future. The purpose of this document is to explain the work and encourage discussion.

Update redux: equiv, the revenge of num

The latest revision is in the equiv branch. It takes the approach of num, with no auto-promotion, and bigint contagion, but adds several things to better support contagion:

Re-Update, alternate universe

In an attempt at fairness in evaluation, this version reverses the defaults. If you want arbitrary precision you must use the ‘prime’ versions of the ops.

The current plan is implemented in the equal branch. It includes changes from what is below, which should be looked at for facts other than in the areas stated here. I’ll reorganize soon.


;;;;;;;;;;;;;;;;;;;;;;;;;
;;equal branch, as of:
;;http://github.com/richhickey/clojure/commit/310534b8e7e7f28c75bb122b4bf1bee320cdae67

(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 3565.579 msecs"

;; hint arg and return
(defn ^:static fib ^long [^long n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 395.365 msecs"

;Use promoting op' ops for arbitrary precision
(defn fib [n]
  (if (<= n 1)
    1
    (+' (fib (dec' n)) (fib (-' n 2)))))

(time (fib 38))
"Elapsed time: 3705.123 msecs"

;; double arg/return
(defn ^:static fib ^double [^double n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 860.326 msecs"

;; double arg/return, double operands
(defn ^:static fib ^double [^double n]
  (if (<= n 1.0)
    1.0
    (+ (fib (dec n)) (fib (- n 2.0)))))

(time (fib 38))
"Elapsed time: 428.116 msecs"

Update

The current plan is implemented in the equal branch. It includes changes from what is below, which should be looked at for facts other than in the areas stated here. I’ll reorganize soon.


;;; equal branch, as of
;;http://github.com/richhickey/clojure/commit/7652f7e935684d3c7851fbcad8ddce97e510a5a6

(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 3530.085 msecs"

;; hint arg and return
(defn ^:static fib ^long [^long n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 1238.83 msecs"

;hint arg and return, non-promoting op' ops
(defn ^:static fib ^long [^long n]
  (if (<= n 1)
    1
    (+' (fib (dec' n)) (fib (-' n 2)))))

(time (fib 38))
"Elapsed time: 407.476 msecs"

;; double arg/return
(defn ^:static fib ^double [^double n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 804.862 msecs"

;; double arg/return, double operands, note does not use op'
(defn ^:static fib ^double [^double n]
  (if (<= n 1.0)
    1.0
    (+ (fib (dec n)) (fib (- n 2.0)))))

(time (fib 38))
"Elapsed time: 480.442 msecs"

Objective – Support primitives as fn arguments and return values

Issues

:static

Features to support :static and primitive args/returns are introduced in the prim branch

Static linking


;;;;;;;;;;;;;;;;;pre-prim master
(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 3798.641 msecs"

;with type hints
(defn fib [n]
  (let [n (long n)]
    (if (<= n (long 1))
      (long 1)
      (+ (fib (dec n)) (fib (- n (long 2)))))))

(time (fib 38))
"Elapsed time: 2548.311 msecs"

;;;;;;;;;;;;;;;;; prim branch
(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 3423.861 msecs"

; add primitive arg/return type
(defn ^:static fib ^long [^long n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 3701.73 msecs" ;no benefit, still boxed math

; primitive arg/return type plus hints for primitive math
(defn ^:static fib ^long [^long n]
  (if (<= n (long 1))
    (long 1)
    (+ (fib (dec n)) (fib (- n (long 2))))))

(time (fib 38))
"Elapsed time: 437.96 msecs"

Objective – Unify primitive and boxed integer semantics

Features to support unified integer semantics are introduced in the num branch


;;;;;;;;;;;;;;;;; num branch
(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 3592.971 msecs"

; add primitive arg/return type
(defn ^:static fib ^long [^long n]
  (if (<= n 1)
    1
    (+ (fib (dec n)) (fib (- n 2)))))

(time (fib 38))
"Elapsed time: 392.605 msecs"

Objective – Get rid of equivalence semantics for numeric =

Features to support type-sensitive numeric equality are introduced in the equal branch


(doc =)
-------------------------
clojure.core/=
([x] [x y] [x y & more])
  Equality. Returns true if x equals y, false if not. Same as Java
  x.equals(y) except it also works for nil. Boxed numbers must have
  same type. Clojure's immutable data structures define equals() (and
  thus =) as a value, not an identity, comparison.

Summary