Fork me on GitHub
#joyride
<
2023-10-30
>
pez16:10:39

I want a macro like time. Tried to copy time from Clojure core and adapt it like so:

(defmacro timer
  "Evaluates expr and prints the time it took.  Returns the value of
 expr."
  [expr]
  `(let [start# (js/performance.now)
         ret# ~expr]
     (prn (str "Elapsed time: " (/ (double (- (js/performance.now) start#)) 1000000.0) " msecs"))
     ret#))
Getting Function.prototype.apply was called on undefined, which is a undefined and not a function when trying to use it. Am I supposed to be able to do something like this?

pez16:10:51

And maybe Joyride should come with time defined?

pez16:10:26

No, I was being stupid. My macro works if I feed it a valid expression. picard-facepalm

pez16:10:12

For some definition of “works”. 😃 I’ll need to adapt it to whatever it is I get from performance.now()

borkdude16:10:58

time is already available in nbb, just copy it from there

borkdude16:10:07

actually, isn't time standard in SCI?

borkdude16:10:14

maybe it should be

pez16:10:45

We lack cljs.core/system-time in Joyride. Time for some issues!

borkdude16:10:33

I'll just add it now

catjam 1
borkdude17:10:57

wow 1111 stars ;)

clojure-spin 1
pez20:10:11

I did some benchmarking on some different ways to convert acorn’s AST (which is in some funny JS instance) to plain data. Might as well share.

(ns bench.object-to-data
  (:require ["acorn-loose" :as acorn]
            [util :as util]
            ["./ast-util.js" :as ast-util]
            ["fs" :as fs]))

(defn ast->data [node]
  (cond
    (array? node) (mapv ast->data (into [] node))
    (and node (instance? js/Object node)) (into {} (for [k (js-keys node)
                                                         :let [v (aget node k)]]
                                                     [(keyword k) (ast->data v)]))
    :else node))

(comment
  ; wc test-files/large.js
  ; 7662   18943  169040 test-files/large.js
  (def text (fs/readFileSync "/Users/pez/.config/joyride/test-files/large.js"))

  (util/time ; Elapsed time: 8.087583065032959 msecs
   (def ast (acorn.parse text #js {:allowAwaitOutsideFunction true})))

  (util/time ; Elapsed time: 5.1631669998168945 msecs
   (def js-ast (ast-util/objectAstToData ast)))

  (util/time ; Elapsed time: 41.782166957855225 msecs
   (def clojure-ast-1 (js->clj js-ast :keywordize-keys true)))

  (util/time ; Elapsed time: 201.6672089099884 msecs
   (def clojure-ast-2 (ast->data ast)))

  (util/time ; Elapsed time: 16.00624990463257 msecs
   (def js-ast-2 (-> ast
                     js/JSON.stringify
                     js/JSON.parse)))

  (util/time ; Elapsed time: 41.43733310699463 msecs
   (def clojure-ast-3 (js->clj js-ast-2 :keywordize-keys true)))

  :rcf)
The ->js-data converter looks like so:
function objectAstToData(node) {
  if (Array.isArray(node)) {
      return node.map(objectAstToData);
  } else if (node && typeof node === 'object') {
      const result = {};
      for (const key of Object.keys(node)) {
          result[key] = objectAstToData(node[key]);
      }
      return result;
  } else {
      return node;
  }
}
So: • 8ms: parsing the file • 5ms: converting the AST JS data with a JS function • 16ms: converting the AST to JS data via JSON • 40ms: js->clj • 200ms: converting the AST to Clojure data with a SCI CLJS function

pez20:10:45

It’s not exactly criterion benchmarking. I just hammered on the evaluation buttons a few times per form and took some sample I thought look representative.