Как я могу записать время вызова функции в clojure

Я новичок в Clojure. Я вызываю функцию Clojure с помощью java и хочу записать время, которое занимает конкретная строка выполнения кода clojure:

Предположим, если моя функция clojure:

     (defn sampleFunction [sampleInput]
         (fun1 (fun2 sampleInput))

Вышеупомянутая функция, которую я вызываю из java, возвращает некоторое строковое значение, и я хочу записать время, необходимое для выполнения fun2.

У меня есть другая функция, скажем, logTime, которая запишет переданный ей параметр в некоторую базу данных:

      (defn logTime [time]
            .....
      )

Мой вопрос: как я могу изменить свою функцию sampleFunction(..) для вызова logTime для записи времени, необходимого для выполнения fun2.

Заранее спасибо.


person ppp456878    schedule 04.07.2020    source источник


Ответы (2)


Я не совсем уверен, как различные части вашего кода сочетаются друг с другом и взаимодействуют с Java, но вот что-то, что может работать с тем, как вы это описали.

Чтобы получить время выполнения фрагмента кода, есть основная функция под названием time. Однако эта функция не возвращает время выполнения, а просто выводит его... Итак, если вы хотите записать это время в базу данных, нам нужно написать макрос для захвата как возвращаемого значения fun2, так и времени, которое потребовалось для выполнения:

(defmacro time-execution
  [& body]
  `(let [s# (new java.io.StringWriter)]
     (binding [*out* s#]
       (hash-map :return (time ~@body)
                 :time   (.replaceAll (str s#) "[^0-9\\.]" "")))))

Что делает этот макрос, так это привязывает стандартный вывод к Java StringWriter, чтобы мы могли использовать его для хранения всего, что печатает функция time. Чтобы вернуть как результат fun2, так и время, которое потребовалось для выполнения, мы упаковываем два значения в хэш-карту (может быть и какая-то другая коллекция — позже мы ее деструктурируем). Обратите внимание, что код, выполнение которого мы синхронизируем, заключен в вызов time, так что мы запускаем побочный эффект печати и фиксируем его в s#. Наконец, .replaceAll просто гарантирует, что мы извлекаем только фактическое числовое значение (в миллисекундах), поскольку time печатает что-то вроде "Elapsed time: 0.014617 msecs".

Включив это в ваш код, нам нужно переписать sampleFunction следующим образом:

(defn sampleFunction [sampleInput]
  (let [{:keys [return time]} (time-execution (fun2 sampleInput))]
    (logTime time)
    (fun1 return)))

Мы просто деструктурируем хэш-карту, чтобы получить доступ как к возвращаемому значению fun2, так и к времени, которое потребовалось для выполнения, затем мы регистрируем время выполнения, используя logTime, и, наконец, мы заканчиваем вызовом fun1 для возвращаемого значения fun2.

person count orlok    schedule 04.07.2020
comment
Спасибо. Это решает проблему. Есть ли способ сделать это без возврата карты, например, выполнение по времени может регистрировать время и должно возвращать только результат? Я пытался что-то вроде ниже, и это не работает. Ты можешь сказать мне, почему? (defmacro time-execution [& body] `(let [s# (new java.io.StringWriter)] (binding [out s#] (let [return (time ~@body)] ( logTime (.replaceAll (str s#) [^0-9\\.] )) return)))) (defn sampleFunction [sampleInput] (fun1 (logTime время))) - person ppp456878; 05.07.2020
comment
С макросом, который вы написали, возможно, вы имели в виду, что sampleFunction будет (defn sampleFunction [sampleInput] (fun1 (time-execution (fun2 sampleInput))))? - person count orlok; 05.07.2020
comment
Ты прав. Я имел в виду то же самое. Но это дает исключение: Вызвано: clojure.lang.ExceptionInfo: вызов clojure.core/let не соответствует спецификации. {:clojure.spec.alpha/problems ........ Будем признательны за любую помощь в исправлении этой ошибки. - person ppp456878; 06.07.2020
comment
Я смог добиться этого, заменив let на def :) - person ppp456878; 06.07.2020

Библиотека tupelo.prof предоставляет множество вариантов, если вы хотите зафиксировать время выполнения одной или нескольких функций и накопить его за несколько вызовов. Пример:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [tupelo.profile :as prof]))

(defn add2 [x y] (+ x y))
(prof/defnp fast [] (reduce add2 0 (range 10000)))
(prof/defnp slow [] (reduce add2 0 (range 10000000)))

(dotest
  (prof/timer-stats-reset)
  (dotimes [i 10000] (fast))
  (dotimes [i 10] (slow))
  (prof/print-profile-stats)
  )

с результатом:

--------------------------------------
   Clojure 1.10.2-alpha1    Java 14
--------------------------------------

Testing tst.demo.core

    ---------------------------------------------------------------------------------------------------
    Profile Stats:
       Samples       TOTAL        MEAN      SIGMA           ID
        10000        0.955     0.000096   0.000045   :tst.demo.core/fast                                                              
           10        0.905     0.090500   0.000965   :tst.demo.core/slow                                                              
    ---------------------------------------------------------------------------------------------------

Если вам нужны подробные данные о времени для одного метода, вам нужна библиотека Criterium. Начните с функции quick-bench.

person Alan Thompson    schedule 04.07.2020