Clojure

bit-and missing long parameters overload

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Completed
  • Affects Version/s: None
  • Fix Version/s: Release 1.3
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code
  • Approval:
    Ok

Description

[Updated: The new primitive support relies on a primitive bit-and operator. As one doesn't exist, clojure functions like even? are now using reflective calls and are about 150x slower than in Clojure 1.2)

JVM has a land instruction and java has & operator that works with longs.

public static long and(long, long);
Code:
0: lload_0
1: lload_2
2: land
3: lreturn

The attached patch allows Clojure to take advantage of it.

Activity

Hide
Assembla Importer added a comment -
Show
Assembla Importer added a comment - Converted from http://www.assembla.com/spaces/clojure/tickets/380 Attachments: 0001-Added-bit-and-with-long-parameters-and-overload-reso.patch - https://www.assembla.com/spaces/clojure/documents/dciMoWD8ir36WfeJe5cbLA/download/dciMoWD8ir36WfeJe5cbLA
Hide
Assembla Importer added a comment -

digash said: Just found that Rich is already taking care of this case in the num branch:

http://github.com/richhickey/clojure/commit/c5d0985af6c17103eaabe523e442f14c29916266#L3R1510

The test in the patch could still be useful.

Show
Assembla Importer added a comment - digash said: Just found that Rich is already taking care of this case in the num branch: http://github.com/richhickey/clojure/commit/c5d0985af6c17103eaabe523e442f14c29916266#L3R1510 The test in the patch could still be useful.
Hide
Assembla Importer added a comment -

digash said: Is it possible to include this in 1.2? Since the num branch is not going to be included in 1.2.

Show
Assembla Importer added a comment - digash said: Is it possible to include this in 1.2? Since the num branch is not going to be included in 1.2.
Hide
David Powell added a comment -

The primitives work means that bit-and, when used with numeric literals, attempts to call the primitive overloads in the Numbers class.
As these overloads don't exist, the code makes a reflective + boxed call. This is extremely slow compared to Clojure 1.2.

Clojure 1.2.0-master-SNAPSHOT
user=> (time (count (filter even? (range 1e6))))
"Elapsed time: 248.719099 msecs"

Clojure 1.3.0-alpha4
user=> (time (count (filter even? (range 1e6))))
"Elapsed time: 46679.133155 msecs"

See http://groups.google.com/group/clojure-dev/browse_thread/thread/a1f0f03e11d90f59

Show
David Powell added a comment - The primitives work means that bit-and, when used with numeric literals, attempts to call the primitive overloads in the Numbers class. As these overloads don't exist, the code makes a reflective + boxed call. This is extremely slow compared to Clojure 1.2. Clojure 1.2.0-master-SNAPSHOT user=> (time (count (filter even? (range 1e6)))) "Elapsed time: 248.719099 msecs" Clojure 1.3.0-alpha4 user=> (time (count (filter even? (range 1e6)))) "Elapsed time: 46679.133155 msecs" See http://groups.google.com/group/clojure-dev/browse_thread/thread/a1f0f03e11d90f59
Hide
David Powell added a comment -

Attached is a patch which adds missing numeric overloads to:

and
andNot
or
xor
divide

Show
David Powell added a comment - Attached is a patch which adds missing numeric overloads to: and andNot or xor divide
Hide
Stuart Halloway added a comment -

Second patch removes unnecessary overloads of andNot. Performance tested good using code below. At some point it would be good to get documentation on why some fns are inline and others are static.

(def test-data (range 1 1e4))

(def fn-sources
  '[(fn [a b] (bit-and a b))
    (fn [a b] (bit-and (long a) b))
    (fn [a b] (bit-and a (long b)))
    (fn [a b] (bit-and (long a) (long b)))
    (fn [a b] (bit-and-not a b))
    (fn [a b] (bit-and-not (long a) b))
    (fn [a b] (bit-and-not a (long b)))
    (fn [a b] (bit-and-not (long a) (long b)))
    (fn [a b] (bit-or a b))
    (fn [a b] (bit-or (long a) b))
    (fn [a b] (bit-or a (long b)))
    (fn [a b] (bit-or (long a) (long b)))
    (fn [a b] (bit-xor a b))
    (fn [a b] (bit-xor (long a) b))
    (fn [a b] (bit-xor a (long b)))
    (fn [a b] (bit-xor (long a) (long b)))
    (fn [a b] (/ a b))
    (fn [a b] (/ (long a) b))
    (fn [a b] (/ a (long b)))
    (fn [a b] (/ (long a) (long b)))])

(defn time-numeric-ops
  [fn-sources]
  (doseq [source fn-sources]
    (print source ": ")
    (let [f (eval source)]
      (time
       (do
         (doall (map f test-data test-data))
         nil)))))

(time-numeric-ops fn-sources)
Show
Stuart Halloway added a comment - Second patch removes unnecessary overloads of andNot. Performance tested good using code below. At some point it would be good to get documentation on why some fns are inline and others are static.
(def test-data (range 1 1e4))

(def fn-sources
  '[(fn [a b] (bit-and a b))
    (fn [a b] (bit-and (long a) b))
    (fn [a b] (bit-and a (long b)))
    (fn [a b] (bit-and (long a) (long b)))
    (fn [a b] (bit-and-not a b))
    (fn [a b] (bit-and-not (long a) b))
    (fn [a b] (bit-and-not a (long b)))
    (fn [a b] (bit-and-not (long a) (long b)))
    (fn [a b] (bit-or a b))
    (fn [a b] (bit-or (long a) b))
    (fn [a b] (bit-or a (long b)))
    (fn [a b] (bit-or (long a) (long b)))
    (fn [a b] (bit-xor a b))
    (fn [a b] (bit-xor (long a) b))
    (fn [a b] (bit-xor a (long b)))
    (fn [a b] (bit-xor (long a) (long b)))
    (fn [a b] (/ a b))
    (fn [a b] (/ (long a) b))
    (fn [a b] (/ a (long b)))
    (fn [a b] (/ (long a) (long b)))])

(defn time-numeric-ops
  [fn-sources]
  (doseq [source fn-sources]
    (print source ": ")
    (let [f (eval source)]
      (time
       (do
         (doall (map f test-data test-data))
         nil)))))

(time-numeric-ops fn-sources)

People

Vote (1)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: