Как, если вообще, я могу улучшить свою числовую производительность в Clojure? - PullRequest
0 голосов
/ 02 октября 2018

Вот короткая программа Scala, которая вычисляет плохое приближение числа Эйлера, генерируя несколько случайных чисел:

package example

import scala.util.Random

object ApproxE extends App {

   def calc = {
     var S = 0.0
     for {i <- 1 to 100000000} {
       var x = 0.0
       var t = 0
       while (x <= 1.0) {
         x += Random.nextDouble
         t += 1
       }
       S += t
     }
     S / 100000000
  }

  val st = System.currentTimeMillis()
  val e = calc
  val ed = System.currentTimeMillis()

  println(e)
  println(ed - st)

}

Работая на моем ноутбуке, она выполняет вычисления примерно за 7 секунд.

Ниже приведены две эквивалентные функции Clojure.On возвращается через 48 секунд, а другой через 230 секунд.

Можно ли в чистом Clojure написать эквивалентную программу с производительностью, сравнимой с достижимой в Java или Scala?

48 с:

(defn calc []
    (loop [i (int 0)
           S (double 0.0)]
        (if (= i 100000000)
            (/ S 100000000)
            (let [rs (repeatedly rand)
                  ps (reductions + rs)
                  <=1 #(<= % 1)]
              (->> ps
                (take-while <=1)
                (count)
                (inc)
                (+ S)
                (recur (inc i)))))))

230 с:

(defn calc2 []
    (with-local-vars [S 0.0]
        (dotimes [i 100000000]
            (with-local-vars [x (double 0)
                              t (int 0)]
                (while (<= @x 1)
                    (var-set x (+ @x (rand)))
                    (var-set t (inc @t)))
                (var-set S (+ @S @t))))
        (/ @S 100000000)))

1 Ответ

0 голосов
/ 02 октября 2018

И ленивые структуры данных, и локальные переменные вносят накладные расходы из-за выделения (хотя вы все равно можете сделать вариант с переменными немного быстрее, переместив выделения для x и t из цикла), разрешение имен(в случае переменных) и вызова метода.

При переводе кода Scala между словами используются локальные привязки (с использованием let и loop), которые эквивалентны локальным переменным Java:

(defn calc-loops []
  (loop [i (int 0)
         S (double 0.0)]
    (if (= i 100000000)
        (/ S 100000000)
        (recur
         (inc i)
         (loop [t 0 x 0.0]
           (if (<= x 1.0)
             (recur (inc t) (+ x (rand)))
             (+ S t)))))))

(time (calc))       ;; => "Elapsed time: 56255.692677 msecs"
(time (calc-loops)) ;; => "Elapsed time: 8800.746127 msecs"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...