Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does wrapping this in a function take 10x longer to run?

Tags:

clojure

Can anyone explain why the time jumps by an order of magnitude simply by wrapping this in a function?

user> (time (loop [n 0 t 0]
              (if (= n 10000000)
                t
                (recur (inc n) (+ t n)))))
"Elapsed time: 29.312145 msecs"
49999995000000

user> (defn tl [r] (loop [n 0 t 0]
                    (if (= n r)
                      t
                      (recur (inc n) (+ t n)))))
#<Var@54dd6004: #object[user$eval3462$tl__3463 0x7d8ba46 "user$eval3462$tl__3463@7d8ba46"]>

user> (time (tl 10000000))
"Elapsed time: 507.333844 msecs"
49999995000000

I'm curious how a simply iteration like this can be made much faster. For example, a similar iterative loop in C++ takes less than 1 ms in Release mode, or about 20 ms in Debug mode on the same system as this Clojure code.

like image 929
johnbakers Avatar asked May 18 '16 11:05

johnbakers


People also ask

Why is my Python script so slow?

In summary: code is slowed down by the compilation and interpretation that occurs during runtime. Compare this to a statically typed, compiled language which runs just the CPU instructions once compilated. It's actually possible to extend Python with compiled modules that are written in C.

Why does Python code run faster in a function?

The reason these built-in functions are fast is that python's built-in functions, such as min, max, all, map, etc., are implemented in the C language. You should use these built-in functions instead of writing manual functions that will help you execute your code faster.

Do functions make code run faster?

Functions CAN make code faster by coding logic once instead of repeating several times and thus reducing code size and resulting in better CPU cache usage. Functions CAN make code slower by copying parameters and hiding info from the optimization. Certain functions can be inlined to undo these disadvantages.

What is the reason of wrapping entire js file into function block?

The purpose of wrapping is to a namespace and control the visibility of member functions. It wraps the code inside a function scope and decreases clashing with other libraries. This is what we call Immediately Invoked Function Expression (IIFE) or Self Executing Anonymous Function.


1 Answers

This happens because in second case passed argument is being boxed. Add type hint to fix this:

user> (defn tl [^long r]
  (loop [n 0 t 0]
    (if (= n r)
      t
      (recur (inc n) (+ t n)))))

user> (time (tl 10000000))
"Elapsed time: 20.268396 msecs"
49999995000000

UPD:

1, 2) In the first case you operate with java primitives, that's why it's so fast. ^Integer won't work here, because it's type hint for boxed type java.lang.Integer (that's why it's capitalized). ^long is type hint exactly for java long primitive. For function parameters you may do only ^long and ^double primitive type hints (others are not supported besides of these you may do type hints for all kinds of primitive arrays, like ^floats, ^bytes etc.).

3) Since passed argument is boxed this forces generic aritmethics for all operations. In other words, every + and inc operation will create new object on heap (in case of primitives, they will remain on stack).

UPD 2:

As an alternative to type hinting you may explicitly convert passed argument to primitive before the loop:

user> (defn tl [r]
  (let [r (long r)]
    (loop [n 0 t 0]
      (if (= n r)
        t
        (recur (inc n) (+ t n))))))

user> (time (tl 10000000))
"Elapsed time: 18.907161 msecs"
49999995000000
like image 173
OlegTheCat Avatar answered Sep 27 '22 21:09

OlegTheCat