<< Back to previous view

[CLJS-1501] Add :parallel-build support to REPLs Created: 05/Dec/15  Updated: 11/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Task Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The :parallel-build option does not currently work in REPLs due to the implementation of cljs.repl/load-namespace






[CLJS-868] no arity warnings on recursive calls Created: 03/Oct/14  Updated: 11/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File cljs_868_14_Nov_2015.md     Text File cljs_868_14_Nov_2015.patch    

 Description   

If a function recursively invokes itself within its own body the invoke will not be checked for arity mismatch.



 Comments   
Comment by Samuel Miller [ 10/Aug/15 10:06 PM ]

Took some time to look at this issue. Originally thought "Do what loop/recur does" but that does not take into account multi-arity. It seems like maybe the best option is to somehow use the second pass of the analyze(analyze-fn-methods-pass2). The entire information about the function is present and the warning section of the code gets triggered but because of no-warn is ignored. Any other ideas for a solution to this?

Comment by Samuel Miller [ 14/Nov/15 7:47 PM ]

So I am looking for feed back on this patch and I will try to explain the reasoning for each section.

The issue is that a function only knows about it's arity after it has been parsed once.
So we need to check arity issues on the second pass

First off, added two new variables.
-activate-second-pass-warnings:Boolean Basically if you want to have second-pass warnings turned on
-second-pass-cljs-warnings:Set Right now we only have :fn-arity but I figure might as well make it generic.

So first up if the modifications to the analyze-fn-methods-pass2 function.
Instead of using no-warn marco here we have some new functionality.
The goal is to turn everything off except the second-pass warnings

So if activate-second-pass-warnings is false just use no-warn else it will use the new section of code.

The default-warning-handler was also modified. After checking if a warning is on, it checks if the warning is a second-pass warning and
if that warning can now be activated. If activate-second-pass-warnings is false AND a warning is still on that implies it is a second pass warning
in the second pass so we activate it.

Also I tried to keep all modifications in cljs.analyzer.

Originally I had the cljs-warnings :fn-arity to false and it would only be turned on in the second pass.
However the repl section just sets everything to true (and turns off select parts like ns errors).
So I decided to not touch those sections and instead keep how other files interface with the analyzer the same.

Comment by Samuel Miller [ 16/Nov/15 10:58 PM ]

Just realized that I have the patch marked as .md instead of .patch





[CLJS-719] this-as behaves incorrectly in "scoping function" Created: 07/Dec/13  Updated: 11/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Major
Reporter: Kevin Marolt Assignee: David Nolen
Resolution: Unresolved Votes: 1
Labels: None


 Description   

When a this-as expression gets put in a "scoping function", e.g. in a let-binding, the value bound via this-as refers to the scoping function, and not to the outer scope.

Example:

(def foo
  (js-obj
    "bar" "baz"
    "getBarRight" (fn [] (this-as self (.-bar self)))
    "getBarWrong" (fn []
                    (let [bar (this-as self (.-bar self))]
                      bar))))
     
(.log js/console (.getBarRight foo)) ;; => "baz"
(.log js/console (.getBarWrong foo)) ;; => undefined

Whereas foo.getBarRight expands to something like

function() {
  var self = this; // this refers to foo
  return self.bar; // returns "bar"
}

foo.getBarWrong on the other hand expands to

function() {
  var bar = function() {
    var self = this; // this refers to enclosing function
    return self.bar; // returns undefined
  }();
  return bar; // returns undefined
}





[CLJS-968] Metadata on function literal inside of a let produces invalid Javascript Created: 07/Jan/15  Updated: 28/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Major
Reporter: Bobby Eickhoff Assignee: David Nolen
Resolution: Unresolved Votes: 2
Labels: bug
Environment:

Originally found with [org.clojure/clojurescript "0.0-2496"]
Still reproducible with the latest cljsc (b5e9a5116259fc9f201bee4b9c6564f35306f9a5)



 Description   

Here is a minimal test case that produces the invalid Javascript:

(defn f []
  (let [a 0]
    ^{"meta" "data"}
    (fn [] true)))

The compiled Javascript includes the invalid token sequence "return return". (Per Chrome: Uncaught SyntaxError: Unexpected token return)

The problem does not occur if the metadata applies to a map literal instead of a function literal.
The problem only occurs when the function and metadata are inside of a let.



 Comments   
Comment by Bobby Eickhoff [ 07/Jan/15 9:45 PM ]

I forgot to try with-meta. Using with-meta does not produce this syntax error, so it's only a problem with the reader macro for metadata.

Comment by David Nolen [ 08/Jan/15 7:41 AM ]

Any quick thoughts about this one Nicola? Quite possibly a compiler issue on the CLJS side.

Comment by Nicola Mometto [ 08/Jan/15 8:07 AM ]

David, I understand why this happens but I don't know enough about how cljs's js emission to propose a fix.
The issue is that with this commit: https://github.com/clojure/clojurescript/commit/d54defd32d6c5ffcf6b0698072184fe8ccecc93a the following scenario is possible:

{:op :meta
 :env {:context :return}
 :expr {:op :fn
        :env {:context :expr}
        :methods [{:op :fn-method 
                   :env {:context :return} ..}]
        ..}
 ..}

i.e. analyze-wrap-meta changes the context of the :fn node to :expr but keeps the context of the :fn-methods to :return.

This causes both
https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/compiler.clj#L575-L576
and
https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/compiler.clj#L488 (https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/compiler.clj#L233)

to be true and emit a "return".

Comment by David Nolen [ 06/May/15 7:15 PM ]

Hrm, it appears analyze-wrap-meta may need to defer to a helper to change the :context of the given AST node.

Comment by Herwig Hochleitner [ 11/Dec/15 10:52 AM ]

I just randomly ran into this, when upgrading an old project. There is also a duplicate already: http://dev.clojure.org/jira/browse/CLJS-1482

Comment by Jonathan Chu [ 28/Jan/16 6:19 PM ]

This issue occurs for me even without a let.

(fn []
  ^{"meta" "data"}
  (fn [] true))

gives me

#object[SyntaxError SyntaxError: Unexpected token return]




[CLJS-1300] REPLs do no write out updated deps.js when compiling files Created: 05/Jun/15  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File cljs-1300.patch    
Patch: Code

 Description   

For example a user may edit a file including a new dependency. This will work at the REPL but if a browser refresh is made the emitted goog.require will fail due to the initial deps.js file being stale.



 Comments   
Comment by ewen grosjean [ 05/Dec/15 4:15 PM ]

load-file is broken into 4 sub-functions:
repl-compile-cljs: compile the cljs file beeing loaded
repl-cljs-on-disk: ensures all dependencies are on disk
refresh-cljs-deps: refreshes the cljs_deps.js file
repl-eval-compiled: eval the compiled file

Comment by David Nolen [ 05/Dec/15 9:02 PM ]

Thanks will review.

Comment by Mike Fikes [ 31/Jan/16 3:25 PM ]

cljs-1300.patch no longer applies on master





[CLJS-1497] `find` on an associative collection does not return collection key Created: 30/Nov/15  Updated: 11/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Instead find returns the passed in key. This means metadata on the key will appear to be lost. Related to CLJS-1496.






[CLJS-527] Support dynamic runtime extension of protocols to types Created: 20/Jun/13  Updated: 11/Dec/13

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Chas Emerick Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File CLJS-527.diff    
Patch: Code and Test

 Description   

Here is a transliteration of a semi-common pattern used with Clojure protocols to dynamically extend protocols to concrete types implementing other protocols (or interfaces, on the JVM):

(defprotocol P (m [this]))

(extend-protocol P
  object
  (m [this]
    (if (seq? this)
      (do
        (extend-type (type this) P
          (m [this] (count this)))
        (m this))
      (throw (ex-info "Cannot extend m to type" {:type (type this)})))))

(I think dnolen was the first to talk about this outside of irc.) Unfortunately, this does not work in ClojureScript; extend-type currently requires that the type be specified as a symbol:

clojure.lang.ExceptionInfo: clojure.lang.PersistentList cannot be cast to clojure.lang.Named at line 4  {:tag :cljs/analysis-error, :file nil, :line 4, :column 5}

I can (hackily?) make this work by simply not attempting to resolve tsym here. However, that leaves lists in as values for :tag metadata (which might be used by the analyzer and/or other tools that depend upon it?), which I presume is not OK.

If someone can provide guidance on a sane path from here, I'll do what I can to produce a plausible patch.



 Comments   
Comment by Chas Emerick [ 21/Jun/13 12:08 PM ]

Looks like jvm.tools.analyzer emits a :tag of nil for some corresponding Clojure code; this can be seen by running this:

(require '[clojure.tools.analyzer :refer (ast)])
#_= nil
(defprotocol P (m [this]))
#_= P
(ast (fn [x]
       (extend-type (type x)
         P
         (m [this] (count this)))))
#_= ...

(The output is verbose enough that I'm not bothering to paste it here.) So, that's easy enough to do, and makes the original example work in ClojureScript.

However, simply suspending the lookup of what is currently assumed to be a symbol naming the type being extended isn't enough. With only that, dynamic usage of extend-type will affect js native prototypes, e.g.:

ClojureScript:cljs.user> (defprotocol P (m [this]))
nil
ClojureScript:cljs.user> (defn naive-dynamic-extend [x]
  (extend-type (type x)
    P
    (m [this] "hi")))
...
ClojureScript:cljs.user> (naive-dynamic-extend true)
...
ClojureScript:cljs.user> js/Boolean.prototype.cljs$user$P$m$arity$1
#<
function (this$) {
    return "hi";
}
>

So the bits in extend-type that handle base types (boolean, string, function, array, etc) need to be brought over to runtime. Looking into this now.

Comment by Chas Emerick [ 24/Jun/13 8:22 AM ]

Patch attached. All previously-allowed usage of extend-type continues to emit exactly the same code. Extensions without a statically-named type include both possible code paths:

1. When the type is a JavaScript native, the extension is made on the prototype's fns using the same base type names as are used for static extensions to e.g. string, object, etc
2. When the type is some other prototype, the extension is made on it directly.

This yields code like:

ClojureScript:cljs.user> (defprotocol P (m [this]))
nil
ClojureScript:cljs.user> #(extend-type (type %) P (m [this] "hi"))
#<
function (p1__4810_SHARP_) {
    var G__4813 = cljs.core.type.call(null, p1__4810_SHARP_);
    var temp__4090__auto__ = (cljs.core.base_type[G__4813]);
    if (cljs.core.truth_(temp__4090__auto__)) {
        var G__4814 = temp__4090__auto__;
        (cljs.user.P[G__4814] = true);
        return (cljs.user.m[G__4814] = ((function (G__4814, temp__4090__auto__, G__4813) {
            return (function (this$) {
                return "hi";
            });
        })(G__4814, temp__4090__auto__, G__4813)));
    } else {
        G__4813.prototype.cljs$user$P$ = true;
        return G__4813.prototype.cljs$user$P$m$arity$1 = ((function (temp__4090__auto__, G__4813) {
            return (function (this$) {
                return "hi";
            });
        })(temp__4090__auto__, G__4813));
    }
}
>

The duplication of the prototype method implementation bodies is unfortunate, a side effect of keeping the extend-type macro and supporting emit-* fns relatively simple. (Note that advanced compilation doesn't lift and merge those fns.) I'm inclined to say that it's a reasonable tradeoff, at least for now, as it only affects the dynamic type extension case; a reasonable TODO later, perhaps.

Comment by Brandon Bloom [ 03/Jul/13 3:44 PM ]

At Chas' request, I took a look at the patch. Tests pass locally & my few small toy projects run fine. I haven't benchmarked.

My only real concern is pretty minor: I'm terrified of JavaScript's semantics around typeof, toString, etc. The existing code paths leverage goog.typeOf, which has some pretty hairy internals. Meanwhile, Chas is just implicitly toString-ing on some type objects with an array set. The code of goog.typeOf also discusses oddities of Object.prototype.toString in firefox, but presumably that won't matter via the implicit conversion present in the array set. So if this works in all the major browsers, the patch LGTM.

Comment by Chas Emerick [ 03/Jul/13 6:29 PM ]

Just a point of documentation w.r.t. the stringifying of js-native prototypes: given the initial example above, if (type x) (or, whatever expression the user is providing that will return a "type" to extend) returns a js-native prototype, we need some way to map that at runtime to the strings that ClojureScript uses for those types when performing protocol dispatch. Using a js object containing as literal a representation of that mapping as possible seemed like a reasonable option. Providing a fn that cond's through the various options would be equivalent AFAICT.

A separate larger issue is, what is a type in ClojureScript? As far as protocols are concerned, the type of types is approximately the union of all non-native js prototypes, and symbols identifying those natives. However, type (and, really, any user of ClojureScript writing expressions provided to extend-type) doesn't know about the latter or the carve-out w.r.t. prototypes, thus some implicit runtime conversion is needed. Alternatively, one could say that any expression provided to extend-type must respect that contract, but then (a) users would need to explicitly handle js native types, and (b) Clojure/ClojureScript portability would be further complicated in this department.

Comment by David Nolen [ 03/Jul/13 8:02 PM ]

Reviewing the patch, thanks all.

Comment by David Nolen [ 03/Jul/13 8:09 PM ]

Ok what is the base-type js-obj for? Why aren't we using goog.typeOf?

Comment by Chas Emerick [ 03/Jul/13 9:06 PM ]

We can't use goog.typeOf because extend-type works with a type (i.e. the return of (type x)), not a value the type of which should be extended to the given protocol(s). (goog.typeOf will always return "function" for prototypes, js-native or not.)

The ClojureScript cljs.core/base-type js-obj is simply a runtime-accessible analogue of the (Clojure) cljs.core/base-type map, except it maps js-native prototypes to the goog.typeOf strings that are used for protocol dispatch.

Comment by David Nolen [ 16/Jul/13 6:40 AM ]

Ok I looked at the patch some more, I don't really like the string coercion aspect around base-type. Let's switch this to an array-map.

Comment by Chas Emerick [ 16/Jul/13 6:48 AM ]

Sure, I can do that. FWIW, that will rope in PAM and whatever other persistent data structure and printing bits it depends upon by default…is that considered acceptable?

Comment by David Nolen [ 16/Jul/13 10:31 AM ]

Hrm, that's actually a good point. Perhaps better to do a array + scan. I thought about this patch some more and it really needs more work. One thing this doesn't handle is objects from foreign contexts. ClojureScript can currently handle this by combining default cases with goog.typeOf.

I think extend-type should probably work with strings and/or symbols that represent the base types so that objects from other contexts can also be handled. I think automating this will be unweildy but at least it gives users the flexibility to handle these cases themselves.

Comment by Chas Emerick [ 16/Jul/13 7:14 PM ]

What do you mean by "foreign contexts"? I did a bit of searching on the term, and didn't turn up anything promising in connection with either ClojureScript or JavaScript. I assume you're not referring to e.g. types loaded via :foreign-libs, but who knows…

Re "strings and/or symbols", are you suggesting that dynamic usage of extend-type should not perform any translation of js-native prototypes to their string names, i.e. an expression being evaluated to determine the type to extend would need to return "string" (or 'string) rather than js/String?

Comment by David Nolen [ 16/Jul/13 9:00 PM ]

JavaScript objects from other JS execution contexts, IFrames are the most common source of these. This is why goog.typeOf implementation is so complex, it handles these cases.

I'm saying that extend-type should do run time extension to JS natives if the user specifies the extension at runtime via a string or symbol for the native cases because an Array from another JS Execution context is not equal to the Array in the current one.

Comment by Brandon Bloom [ 23/Jul/13 2:04 PM ]

It seems silly to argue about all the edge cases here, considering how many edge cases pertaining to "types" are already broken in ClojureScript.

For example, currently (= (type :foo) (type "foo"))

This is because cljs.core/type simply calls accesses the constructor field, and keywords are strings at runtime. Meanwhile, the (type (type x)) is always a function, since there is no Type type.

There are three problems:

1) Type equality

2) Getting an object's type

3) Runtime protocol extension

This patch delegates #2 to cljs.core/type and properly addresses #3.

#1 is a bit trickier, since there are three valid approaches I can think of:

A) Nominal equality - Enhance cljs.core/type to return sensible symbols, by implementing the crux of the goog/typeOf behavior plus some extra behavior for extracting type names out of function string representations.

B) Constructor equality - Simply compare .constructor; This is basically what happens now, but has 2 problems: B1) Doesn't provide for types at compile time B2) might not work correctly with IFrame execution environments

C) Hybrid/Heuristic - (defprotocol IType ...) and implement some Type objects with equality sensible operators; lazily stuff those type objects into a reflection map of some sort.

Personally, I think that B (the current state of the world) is hopelessly broken. Despite my initial reservations regarding the toString coercion, I think this patch does a reasonable job of eschewing B for a stop-gap A (with compile time interop). Given this analysis, I think the string coercion for natives actually does a better job than one could do with a PAM of constructors: ie the coercion covers the remote execution state. Unless this is provably broken for some key scenarios with IFrames, I think the patch is good as is, but we need to think about a follow on patch for fixing up runtime types in general.

Comment by Brandon Bloom [ 23/Jul/13 2:23 PM ]

I should also point out: Unlike JavaScript, Java has a unified nominal type system. Name equality is type equality (ignoring custom class loaders). However, JavaScript with Google Closure has a stratified type system: The dynamic type system utilizes object identity for equality. The GClosure static type system is (mostly) nominal with some fudge factor for the mismatch with the runtime type system (mostly around inheritance/mixins/array-like/etc). I think that ClojureScript should strive for a runtime reification of the Google Closure type system, since that would be most compatible with the Clojure/JVM type system.

Comment by David Nolen [ 23/Jul/13 3:06 PM ]

We are not going to follow goog.typeOf.

Comment by Brandon Bloom [ 23/Jul/13 3:22 PM ]

Follow it where?

Comment by David Nolen [ 23/Jul/13 3:29 PM ]

We're not going to use it nor follow its example for determining types unless we are trying to detect natives.

Comment by Brandon Bloom [ 23/Jul/13 3:34 PM ]

Getting back on topic: Getting some type-like-thing from an object is not this patch.

This patch is about extend-type, which I think it implements reasonably well given our current failings at runtime type reification.

Chas has this working with user defined types as well as with natives. Are there any particular scenarios that are provably broken? Either in general or on a particular browser/runtime?

Comment by David Nolen [ 23/Jul/13 3:38 PM ]

Chas's patch can't catch natives from IFrame contexts, I'd rather this patch move forward with at least the ability for a user to handle that situation themselves which I said above.

Comment by Brandon Bloom [ 23/Jul/13 3:59 PM ]

I think this does handle natives from iframe contexts, since extend-type takes a "type" not an object. Getting the type from an object does not need to happen here. The patch coerces types to a string via toString, which is precisely how goog.typeOf works internally on natives. Search for Object.prototype.toString.call in http://docs.closure-library.googlecode.com/git/closure_goog_base.js.source.html

Are you speculating that the patch doesn't work, or have you tried it?

If the former, Chas: Can you provide a test project that demonstrates extension of the cross product of these two sets:

1) local type
2) request remote object, coerce to type locally
3) request remote type object

A) native objects
B) deftype-ed objects

Comment by Chas Emerick [ 23/Jul/13 7:42 PM ]

Whatever the semantics and dark corners of JavaScript "types" — or, what they should be, at least w.r.t. ClojureScript — extend-type has very little latitude to operate.

The runtime-dynamic variant of the code it generates will be expecting something typeish coming out of whatever expression the user provides to it.
AFAICT, the only sane possibilities for "typeish" in this context are strings naming javascript natives (e.g. "string", or perhaps 'string if we want to be generous), or a constructor fn (cljs.core/PV, or js/String, or anything else returned by type). The current patch only accepts the latter, done to preserve as much as possible the existing patterns of extend-type usage in Clojure, and hopefully avoid foisting conversion of js/String to "string" at runtime onto users. String coercion is used to normalize the former into the latter; since the code determining the typeish value is entirely in the hands of the user (we don't have access to an object that exemplifies the type to which the user is extending, so we can't wedge in anything particularly clever), I believe it (or something similar) is all we can do.

From here, the only other option I see would be to expand the patch to eliminate this coercion, accepting strings or symbols naming js natives ("string", "boolean", and so on), and allow extensions to js natives at runtime without restriction. This may be a feature for some (perhaps if someone wants to extend a protocol to a js native only withing a particular iframe context?); on the other hand, we should probably document heavily that runtime usage of extend-type should take care to perform the sort of coercion the current patch does (and maybe provide some kind of helper function?), insofar as extension to natives directly is considered harmful in general (e.g. http://dev.clojure.org/jira/browse/CLJS-528, which was viewed favorably in irc some weeks ago?).

I'm happy to produce further tests (up to the suite that Brandon suggested above) if that would be helpful.

Comment by Michał Marczyk [ 26/Jul/13 7:04 PM ]

Just wanted to note that I've run into a situation where runtime extension of protocols to types would AFAICT be the next best thing to "extending protocol to protocol". Here's a link to the relevant ticket in fipp's issue tracker: https://github.com/brandonbloom/fipp/issues/6 (relevant part starts in the 8th comment).





[CLJS-1278] Asserts still fail while :require-ing .js file (either in :libs or in :source-paths) (same as CLJS-1196) Created: 20/May/15  Updated: 14/Jul/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3269
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Michal Till Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File cljs_1278.patch    

 Description   

Following on CLJS-1196, I can't get it to work.

In version 0.0-3264 lein-cljsbuild crashed on weird eception `Caused by: java.lang.IllegalArgumentException: No implementation of method: :make-reader of protocol: #'clojure.java.io/IOFactory found for class: nil"` but the current version 0.0-3269 gives the same failed assertion as previously.

I've put up a sample project to illustrate the issue.

Steps to reproduce:

`git clone https://github.com/tillda/stackone`
`cd stackone`
`git checkout 537e5c69b844bc53c159e85cafc24310543cc918`
`lein clean && lein cljsbuild once temp`

Expected behaviour: cljs compiled successfully with src/vendor/client/closure.js and env/stackone/helpersjs.js being included.

Actual behaviour:

```
Compiling "resources/public/lein-cljsbuild-temp/dev-mode-deps.js" failed.
Exception in thread "main" java.lang.AssertionError: Assert failed: (or (file? x) (url? x) (string? x)), compiling/private/var/folders/ym/l2qxd7l97kzfzftrdpqsclm40000gn/T/form-init3642888309490821030.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7249)
at clojure.lang.Compiler.loadFile(Compiler.java:7175)
at clojure.main$load_script.invoke(main.clj:275)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invoke(main.clj:308)
at clojure.main$null_opt.invoke(main.clj:343)
at clojure.main$main.doInvoke(main.clj:421)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.AssertionError: Assert failed: (or (file? x) (url? x) (string? x))
at cljs.util$ext.invoke(util.cljc:115)
at cljs.closure$source_on_disk.invoke(closure.clj:1206)
at cljs.closure$output_unoptimized$fn__3708.invoke(closure.clj:1235)
at clojure.core$map$fn__4551.invoke(core.clj:2622)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4126.invoke(core.clj:135)
at clojure.core$filter$fn__4578.invoke(core.clj:2677)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4126.invoke(core.clj:135)
at clojure.core$map$fn__4551.invoke(core.clj:2614)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.Cons.next(Cons.java:39)
at clojure.lang.RT.next(RT.java:674)
at clojure.core$next__4110.invoke(core.clj:64)
at clojure.core$str$fn__4186.invoke(core.clj:528)
at clojure.core$str.doInvoke(core.clj:526)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invoke(core.clj:628)
at cljs.closure$deps_file.invoke(closure.clj:1040)
at cljs.closure$output_deps_file.invoke(closure.clj:1060)
at cljs.closure$output_unoptimized.doInvoke(closure.clj:1243)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invoke(core.clj:630)
at cljs.closure$build.invoke(closure.clj:1514)
at cljs.closure$build.invoke(closure.clj:1426)
at cljsbuild.compiler$compile_cljs$fn__3884.invoke(compiler.clj:81)
at cljsbuild.compiler$compile_cljs.invoke(compiler.clj:80)
at cljsbuild.compiler$run_compiler.invoke(compiler.clj:187)
at user$eval4018$iter_40544058$fn4059$fn_4077.invoke(form-init3642888309490821030.clj:1)
at user$eval4018$iter_40544058$fn_4059.invoke(form-init3642888309490821030.clj:1)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4126.invoke(core.clj:135)
at clojure.core$dorun.invoke(core.clj:3007)
at clojure.core$doall.invoke(core.clj:3023)
at user$eval4018.invoke(form-init3642888309490821030.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6792)
at clojure.lang.Compiler.eval(Compiler.java:6782)
at clojure.lang.Compiler.load(Compiler.java:7237)
... 11 more
Subprocess failed
```



 Comments   
Comment by David Nolen [ 20/May/15 10:21 AM ]

This issue is in danger of being closed. Please supply minimal steps to reproduce that do not involve anything other than the ClojureScript compiler. We no longer have time to wade through the indirection introduced by cljsbuild or any other downstream tooling. Thanks.

Comment by Michal Till [ 20/May/15 11:14 AM ]

@David Nolen: I have created a failing minimal testcase based on the Quick Start document. Here it is: https://github.com/tillda/cljs-testcase/

Comment by David Nolen [ 20/May/15 11:27 AM ]

Michal the failing example is not correct. You are not supplying any :libs option.

Comment by Michal Till [ 20/May/15 11:45 AM ]

Ah! Thank you very much! This additional issue was therefore my error. Now it seems to work even in my "big" example.

However it would be cool if there was a meaningful error message stating that a file path can't be resolved. If one is not an expert in the cljs compiler this is almost impossible to figure out. After all the error message in the CLJS-1196 issue and in this wrongfully reported one are exactly the same.

You may close this issue.

Comment by David Nolen [ 20/May/15 11:55 AM ]

We'll leave it open for the improving the error message.

Comment by Sebastian Bensusan [ 22/May/15 7:16 AM ]

Added the check in cljs.closure/source-on-disk where there is info for the error message.

For the supplied case, the error message is:

java.lang.IllegalArgumentException: The file file:/home/carlos/Playground/cljs-testcase/src/hello_world/closure.js 
lacks an associated source file. If it is a JavaScript library please add it to :libs}}

If a different wording or location of the check is needed, I'll submit a new patch with corrections.

Notes:

  • Changed:(:provides js) to (-provides js) in order to be consistent with IJavaScript.
  • cljs.clojure/source-on-disk takes a js argument that should satisfy with IJavaScript and ISourceMap if :source-map is enabled but the implementation is hardcoded to maps because :source-map and :source-url are used instead of ISourceMap methods -source-map and -source-url. I propose to extend PersistentMap and PersistentArrayMap to ISourceMap to make source-on-disk compliant with both protocols.




[CLJS-713] optimized case Created: 04/Dec/13  Updated: 22/Dec/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File 0001-CLJS-713-Allow-test-expressions-for-case-to-be-chars.patch     Text File 0001-CLJS-713-first-cut-at-compiling-case-to-switch.patch    

 Description   

With the advent of asm.js many engines will like compile switch statements over integers into jump tables. We should provide a real `case*` ast node that compiles to JS `switch` when possible - i.e. numbers, strings, keywords etc.



 Comments   
Comment by Michał Marczyk [ 18/Feb/14 5:56 PM ]

First cut impl also available here:

https://github.com/michalmarczyk/clojurescript/tree/713-compile-case-to-switch

With this patch applied, case expressions are compiled to switch + some extra bits when all tests are numbers or strings, otherwise old logic is used.

For example, {{(fn [] (let [x 1] (case x 1 :foo (2 3) :bar :quux)))}} gets compiled to

function () {
    var x = 1;
    var G__6469 = x;
    var caseval__6470;
    switch (G__6469) {
      case 1:
        caseval__6470 = new cljs.core.Keyword(null, "foo", "foo", 1014005816);
        break;
      case 2:
      case 3:
        caseval__6470 = new cljs.core.Keyword(null, "bar", "bar", 1014001541);
        break;
      default:
        caseval__6470 = new cljs.core.Keyword(null, "quux", "quux", 1017386809);
    }
    return caseval__6470;
}

The existing test suite passes, but I suppose it wouldn't hurt to add some tests with case in all possible contexts.

Comment by Michał Marczyk [ 18/Feb/14 6:05 PM ]

As a next step, I was planning to arrange things so that numbers/strings are fished out from among the tests and compiled to switch always, with any leftover tests put in an old-style nested-ifs-based case under default:. Does this sound good?

It seems to me that to deal with symbols and keywords in a similar manner we'd have to do one of two things:

1. check symbol? and keyword? at the top, then compile separate switches (the one for keywords would extract the name from the given keyword and use strings in the switch);

2. use hashes for dispatch.

Which one sounds better? Or is there a third way?

Comment by Michał Marczyk [ 18/Feb/14 6:11 PM ]

Of course we'd need to compute hashes statically to go with 2. I'd kind of like it if it were impossible (randomized seed / universal hashing), but currently it isn't.

Comment by Francis Avila [ 19/Feb/14 12:22 AM ]

At least on v8, there are surprisingly few cases where a switch statement will be optimized to a jump table. Basically the type of the switched-over value must always (across calls) match the type of every case, and there must be fewer than 128 cases, and integer cases must be 31-bit ints (v8's smi type). So mixing string and number cases in the same switch guarantees the statement will never be compiled. In many cases an equivalent if-else will end up being significantly faster on v8 just because the optimizing jit recognizes them better. There's an oldish bug filed against v8 switch performance. Looking at the many jsperfs of switch statements, it doesn't seem that v8 has improved. Relevant jsperf

Firefox is much better at optimizing switch statements (maybe because of their asm.js/emscripten work) but I don't know what conditions trigger (de)optimization.

I suspect the best approach is probably going to be your option one: if-else dispatch on type if any case is not a number, and then a switch statement covering the values for each of the keyword/string/symbol types present (no nested switch statements, and outlining the nested switches might be necessary). Even with a good hash, to guarantee v8 optimizing-compilation you would need to truncate the hashes into an smi (signed-left-shift once?) inside the case*.

Comment by David Nolen [ 19/Feb/14 12:50 AM ]

There's no need for invention here. We should follow the strategy that Clojure adopts - compile time hash calculation.

Comment by Francis Avila [ 19/Feb/14 3:09 PM ]

The problem, as Michal alluded to, is that the hash functions in cljs's runtime environment are not available at compile-time (unlike in Clojure). This might be a good opportunity to clean up that situation or even use identical hash values across Clojure and Clojurescript (i.e. CLJS-754), but that's a much bigger project. Especially considering it will probably not bring much of a speedup over an if-else-if implementation except in very narrow circumstances.

Comment by David Nolen [ 19/Feb/14 4:38 PM ]

Francis Avila I would make no such assumptions about performance without benchmarks. One of the critical uses for case is over keywords. Keyword hashes are computed at compile time, so that's one function call and a jump on some JavaScript engines. This is particularly useful for the performance of records where you want to lookup a field via keyword before checking the extension map.

This ticket should probably wait for CLJS-754 before proceeding.

Comment by Francis Avila [ 22/Feb/14 4:44 AM ]

Record field lookup is a good narrow use case to test. I put together a jsperf to compare if-else (current) vs switch with string cases vs switch with int cases (i.e., hash-compares, assuming perfect hashing).

Comment by David Nolen [ 10/May/14 3:59 PM ]

I've merged the case* analyzer and emitter bits by hand into master.

Comment by David Nolen [ 10/May/14 4:42 PM ]

I'v merged the rest of the patch into master. If any further optimizations are done it will be around dispatching on hash code a la Clojure.

Comment by Francis Avila [ 11/May/14 12:53 AM ]

Your keyword-test optimization has a bug: https://github.com/clojure/clojurescript/commit/9872788b3caa86f639633ff14dc0db49f16d3e2a

Test case:

(let [x "a"] (case x :a 1 "a"))
;=> 1
;;; Should be "a"

Github comment suggests two possible fixes.

Comment by David Nolen [ 11/May/14 10:50 AM ]

Thanks fix in master.

Comment by Christoffer Sawicki [ 23/Jun/14 3:41 PM ]

case over "chars" is currently not being optimized to switch (in other words: (case c (\a) :a :other) uses if instead of switch).

Given that ClojureScript chars are just strings of length 1, could this perhaps simply be fixed by tweaking https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/core.clj#L1187 ?

Comment by Christoffer Sawicki [ 23/Jun/14 4:11 PM ]

OK, I couldn't resist trying and it seems to be that easy. Would be great if somebody more knowledgeable could look at it and say if it has any side-effects. (See patch with name 0001-CLJS-713-Allow-test-expressions-for-case-to-be-chars.patch.)

Comment by David Nolen [ 23/Jun/14 4:15 PM ]

The patch looks good I would have applied it if I hadn't already gone and done it master myself just now

Comment by Christoffer Sawicki [ 23/Jun/14 4:22 PM ]

Hehe. Thanks! Don't forget to update the "case* tests must be numbers or strings" message on line 496 too.

Comment by David Nolen [ 23/Jun/14 4:48 PM ]

The existing docstring is inaccurate - case supports all compile time literals.

Comment by David Nolen [ 22/Dec/15 4:59 PM ]

There are quite a few optimization in master now.





[CLJS-404] Automate Browser REPL testing Created: 23/Oct/12  Updated: 05/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Minor
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None


 Description   

It's worth investigating Selenium, PhantomJS, etc. as solutions to sanity check the Browser REPL when we run the other tests.



 Comments   
Comment by Robert Krahn [ 22/Dec/14 1:22 PM ]

An attempt: https://github.com/clojure/clojurescript/pull/42

Comment by David Nolen [ 24/Dec/14 8:57 AM ]

This looks like an interesting patch, thanks!

Comment by Robert Krahn [ 26/Dec/14 10:57 AM ]

I'll post a patch here, first I'll investigate the load-file issue, though.





[CLJS-374] satisfies? produces strange code when the protocol is not in the fast-path list Created: 06/Sep/12  Updated: 05/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None





[CLJS-1415] Handling JSDoc param name [x] optional syntax Created: 10/Aug/15  Updated: 11/Jan/16

Status: In Progress
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: type-check





[CLJS-994] print a warning when :externs file paths can't be found. Created: 30/Jan/15  Updated: 11/Jan/16

Status: Reopened
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: Crispin Wellington Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: cljs, enhancement, errormsgs, patch,
Environment:

Linux 64bit

java version "1.7.0_65"
OpenJDK Runtime Environment (IcedTea 2.5.3) (7u71-2.5.3-0ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)


Attachments: Text File clojurescript-extern-missing-warning.patch    
Patch: Code

 Description   

clojurescript silently ignores missing externs files possibly leading a developer to chase their tail.

Presently it can be very confusing using advanced compilation if you have made a mistake in the path name of one of your :externs files. This patch makes the compiler print a warning on stderr so you can quickly determine the cause of the broken advanced compilation output.

As a side effect, when doing a basic lein-cljsbuild a warning is always printed:

```
WARNING: js resource path closure-js/externs does not exist
```

This is because lein-cljsbuild quietly adds this extra path to your :externs listing without you knowing.



 Comments   
Comment by David Nolen [ 31/Jan/15 1:59 PM ]

You need to bind *out* to *err*, or just print to it directly a la cljs.util/debug-prn.

Comment by Crispin Wellington [ 31/Jan/15 7:30 PM ]

I did bind out to err. Check the patch.

Comment by David Nolen [ 01/Feb/15 12:30 PM ]

Crispin, oops sorry you are correct. Thanks.

Comment by David Nolen [ 13/Mar/15 7:33 AM ]

fixed https://github.com/clojure/clojurescript/commit/5f66a78bf469a9875e51aa39c29d3e66ce890eb4

Comment by David Nolen [ 14/Mar/15 5:55 AM ]

The solution does not work for cljsbuild. It's unclear why there so much machinery in place over the approach taken for deps.clj.

Comment by David Nolen [ 15/Mar/15 10:37 AM ]

Stalled on this cljsbuild issue https://github.com/emezeske/lein-cljsbuild/issues/383

Comment by Crispin Wellington [ 23/Mar/15 2:50 AM ]

This lein-cljsbuild issue is what made me make it just a warning initially, and not a hard error like raising IllegalArgumentException does. Though I agree it should be a hard error. If we start with a warning, it enables the immediate problem for the developer to be resolved, and leaves a wart that the cljs-build project can then see that need fixing on their end. Then when that end is fixed it could be made a hard error. If cljsbuild is fixed fairly soon then all is well, but if it takes a long time, a warning might be a good first step.





[CLJS-1556] Invalid code emit for obj literal Created: 31/Jan/16  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1556.patch    
Patch: Code and Test

 Description   

For some legal ClojureScript expressions involving #js object literals, invalid JavaScript is emitted. Specifically this has to do with object literals appearing at the beginning of statements where the opening brace can be interpreted as the beginning of a JavaScript block.

One way to reproduce this is to evaluate

(do #js {:a 1} 
    #js {:b 2})

in the Node REPL. In this case it is the first object literal that causes the problem; the second one emitted follows a return statement and is OK.

Rationale for marking it as minor: This appears to only really occur in places where the object literal won't actually be used.






[CLJS-1518] Case macro expansion evaluates expression twice Created: 21/Dec/15  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145, 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Darrick Wiebe Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None
Environment:

The issue is present in version 1.7.189.


Attachments: Text File CLJS-1518.patch    
Patch: Code and Test

 Description   

The value being checked is evaluated twice if all of the test values are keywords.

(macroexpand-1 '(case (expensive) :a 1 2))
(cljs.core/let [G__123555 (if (cljs.core/keyword? (expensive)) (.-fqn (expensive)) nil)]
  (case* G__123555 [["a"]] [1] 2))


 Comments   
Comment by Mike Fikes [ 31/Jan/16 11:38 PM ]

Patch takes advantage of the existing gensym as a temp place to stash the evaluated value before test / FQN conversion.

Adds a unit test specifically checking for single evaluation in this case.

Comment by Mike Fikes [ 31/Jan/16 11:40 PM ]

With the patch, Darrick's macroexpansion example becomes:

(cljs.core/let [G__7663 (expensive) 
                G__7663 (if (cljs.core/keyword? G__7663) (.-fqn G__7663) nil)] 
  (case* G__7663 [["a"]] [1] 2))




[CLJS-1515] Self-host: Allow :file key in cljs.js/*load-fn* callback Created: 17/Dec/15  Updated: 14/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: bootstrap

Attachments: Text File CLJS-1515-1.patch     Text File CLJS-1515-2.patch     Text File CLJS-1515-3.patch     Text File CLJS-1515-4.patch     Text File CLJS-1515-5.patch     Text File CLJS-1515-6.patch    
Patch: Code and Test

 Description   

Bootstrapped ClojureScript is abstracted away from direct I/O by use of a *load-fn* callback. A result is that when a namespace is loaded, the :file attribute associated with def s in [:cljs.analyzer/namespaces 'foo.ns :defs] in the AST is nil, because cljs.analyzer/*cljs-file* cannot be set to a meaningful value.

This ticket asks for an extension to *load-fn*, allowing a :file key to be optionally included by cljs.js clients, and for cljs.analyzer/*cljs-file* to be bound to that value in appropriate places in cljs.js so that the :file info appears in the AST.

One rationale for this :file attribute is that it makes it easier for clients of cljs.js to look up the file for a def, say, for use when implementing a source REPL special, for example.



 Comments   
Comment by Andrea Richiardi [ 17/Dec/15 4:31 PM ]

Initial patch, adding a :file key to load-fn and a :file-env key inside opts and then assigning it to cljs.analyzer/cljs-file in eval-str. This approach can be discussed and we can create an ad-hoc function for binding. It felt right there.
Moreover, cljs.analyzer/cljs-file gets overridden every time with the payload coming from load-fn.
All this was very quickly done in order to have a feedback from who's more expert than me about the consequences. This is also my very first ClojureScript patch

Comment by Mike Fikes [ 17/Dec/15 5:33 PM ]

I tried this patch. It is working fine for me when loading namespaces, but if I use cljs.js/analyze-str where the string is an ns form referring other namespaces loaded via *load-fn*, along with a def, things are off. (I have that ns referring macros from a clj file and a symbol from a cljs file, and the clj file gets associated with the top-level def and the macro, and the def in the referred file ends up with nil.

As a minor aside, the patch has a spurious whitespace change at the end.

Comment by Mike Fikes [ 17/Dec/15 5:56 PM ]

With respect to the last comment: The patch employs the pattern of conveying the :file passed in the cb via a :file-env opt to the consuming fn. It is consumed in eval-str* but not in analyze-str*. If the same logic is added to analyze-str* then the problem mentioned in the last comment goes away.

Comment by David Miller [ 17/Dec/15 6:48 PM ]

I'm hopeful someone will assign this to a responsible party. I am not that person.

Comment by Andrea Richiardi [ 17/Dec/15 7:21 PM ]

sorry David (Miller) and thanks Mike, I will rework it, adding some tests as well

Comment by Andrea Richiardi [ 17/Dec/15 7:23 PM ]

By the way this makes me think that maybe a better choice is to consider this a side effect and directly modify cljs.analyzer/*cljs-file* returning from *load-fn*, who knows how many other spots I am not covering...

Comment by Mike Fikes [ 18/Dec/15 5:36 AM ]

Two more comments:

1) Broadening the scope of the binding doesn't appear to work properly for me. But things do work if the bindings are done as in the patch now (next to where the other bindings are done).

2) Perhaps :file should be only set if the :lang being called back with is :clj. Maybe this could at least be documented. (It is not clear to me if it is useful for :js, as the patch is setting ana/*cljs-file*.)

Comment by Andrea Richiardi [ 18/Dec/15 10:27 AM ]

About 2), is any AST generated for .js files at all? If yes maybe then we should add it too...I need to explore that code path as well.

Comment by Andrea Richiardi [ 18/Dec/15 3:33 PM ]

So basically with ana/*cljs-file* binding the :file in :meta is not changed at all (I fixed following Mike's advice) but :file is, are we ok with this? In replumb (from planck) we check both so no problem, nonetheless it would be great to know why..

:defs {foo {:protocol-inline nil, :meta {:file bootstrap-test.core, :line 3, :column 7, :end-line 3, :end-column 10, :arglists (quote ([a b]))}, :name bootstrap-test.core/foo, :variadic false, :file /.../clojurescript/src/test/self/bootstrap_test/core.cljs, :end-column 10, :method-params ([a b]), :protocol-impl nil, :arglists-meta (nil nil), :column 1, :line 3, :end-line 3, :max-fixed-arity 2, :fn-var true, :arglists (quote ([a b]))}}, :require-macros nil, :doc nil

Comment by Andrea Richiardi [ 18/Dec/15 3:44 PM ]

It looks like the information in :meta comes directly from the multimethod parse which I dont' think we can change easily. So either we override :file in :meta or we leave as it is with a note in the documentation for :file in *load-fn*

Comment by Andrea Richiardi [ 18/Dec/15 4:10 PM ]

About :js files at least to me it looks like the only trace of importing, say, goog.sting in the AST is in the :imports of the parent namespace. No :file key anywhere, but please correct me if I am wrong as the AST is difficult to untangle

Comment by Andrea Richiardi [ 18/Dec/15 5:29 PM ]

Patch and test

Comment by Mike Fikes [ 18/Dec/15 7:43 PM ]

Comments on {{CLJS-1515-2.patch}} (mostly just opinion):

  1. (Opinion): Introduces new public API, especially with respect to AST exposure. Perhaps fn could instead be added to the test namespace.
  2. (Opinion): I wouldn't try anything complicated to try to patch up the :file that is in the :meta map. (Maybe we'll ultimately figure out why setting cljs.analyzer/*cljs-file* is insufficient for that bit.)
  3. (Opinion): For the :file docstring, I'd avoid mentioning AST. (Even though that was the true motivation for this ticket.) I'd only indicate that it represents the location where :source was obtained. (Which I guess would leave open it being perfectly fine for clients to provide it in the case that :lang is :js.)
  4. script/test-self-host passes for me.
  5. Inadvertent whitespace changes in append-source-map.
Comment by Andrea Richiardi [ 18/Dec/15 7:49 PM ]

1. Sorry Mike I don't understand when you say fn...what do you mean? Can you expand?
2. Yes and it would change a lot of code, that's why I didn't even try
3. Ok can change that, but where should be mentioned that we are modifying :file but not inside :meta?
4. Great!
5. You know I really tried hard not to have that, I will try again to disable all the auto indent my emacs has.

Comment by Mike Fikes [ 18/Dec/15 8:30 PM ]

1. The three new public functions in cljs.js: (var-ast, ns-ast, file->lang) could perhaps be moved to be utility functions in the self-host test namespace.
3. Dunno about the :meta question. But on the :lang :js question, perhaps the patch should only bind :cljs.analyzer/*cljs-file* if :lang :clj?

Comment by Andrea Richiardi [ 18/Dec/15 8:38 PM ]

1. I know it looks like they are used in test only, but I put them there as public because both replumb and planck use them and I was kind of "proposing" this kind of AST utils to be part of the official API (so that the poor dev does not have to go through cljs.analyzer in order to query the AST. I understand if no though.
3. This I don't really know, and seek guidance. I have not noticed any significant change in the AST for .js file, maybe *cljs-file* is never queried in that code path. I could not even find a way to test it. But I could, of course, be very wrong.

Comment by Andrea Richiardi [ 21/Dec/15 2:13 PM ]

This puts the utils functions in the test namespace for now, maybe thinking about exposing some API in the future.

Comment by Andrea Richiardi [ 21/Dec/15 8:19 PM ]

About :js:

  • it looks like the analyze-str code path simply recurs to fetch the next dep. So I guess that branch does not touch the AST.
  • for the require code path it looks like it -> is -> similar.

Therefore I don't see the point in adding :file for :js and I will not bind *cljs-file* if this is the case, as you suggested.

Comment by Andrea Richiardi [ 21/Dec/15 9:48 PM ]

Patch #4 changes the conveying key to :cljs-file, after Mike's good suggestion, and moves the assoc to the (condp ... :clj) branch only. I also added a test to check that *cljs-file* does not match the file path when in the :js branch.

Comment by Andrea Richiardi [ 21/Dec/15 11:56 PM ]

Another note, the *cljs-file* test works because the binding form does not actually restore the old value when it exits...In Clojure it would not probably work.

^ This is plain wrong, I was not considering the "when" my tests are executed, please disregard.

Comment by Mike Fikes [ 23/Dec/15 5:13 PM ]

CLJS-1515-4.patch LGTM.

Details: I tested against current ClojureScript master, using downstream Planck to load regular and macro namespaces and the :file portion of the AST gets properly updated. This also occurs if I instead use cljs.js/analyze-str passing in an ns form that causes code to be loaded. Additionally unit tests (regular and bootstrap) pass for me. I think this patch is functionally good to go.

Comment by David Nolen [ 26/Dec/15 6:54 AM ]

Copying goog.string into the source tree is not desirable. Please fix the tests to remove this. If you must, copy it to a temporary a location from the Google Closure Library JAR and remove it after the test has completed, thanks.

Comment by Andrea Richiardi [ 26/Dec/15 2:20 PM ]

Patch 5 avoid copying string.js and re-uses self_host/test.js.

Comment by Andrea Richiardi [ 26/Dec/15 2:22 PM ]

Done what you asked

Comment by Mike Fikes [ 05/Feb/16 8:05 PM ]

CLJS-1515-5.patch no longer applies

Comment by Andrea Richiardi [ 14/Feb/16 9:17 PM ]

Reapplied and re-tested. Works

Testing with Node

Testing self-host.test

Ran 8 tests containing 47 assertions.
0 failures, 0 errors.




[CLJS-1164] quot and rem are inefficient Created: 24/Mar/15  Updated: 20/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Francis Avila Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: math

Attachments: Text File CLJS-1164-1.patch     Text File cljs-1164.patch    
Patch: Code and Test

 Description   

The implementation of the quot and rem functions are needlessly complicated. Currently they are:

(defn quot [n d] (fix (/ (- n (js-mod n d)) d)))
(defn rem [n d] (- n (* d (quot n d))))

However all numbers in js are doubles already, so all this is unnecessary:

(defn quot [n d] (fix (/ n d)))
(defn rem [n d] (js-mod n d)))

Notice that "rem" is simply js-mod, and I'm not sure why no one noticed this before. I keep js-mod for now since a lot of code uses it, and if cljs ever grows a number tower the distinction may be important.

Patch attached, which also:

  • Creates a macro version of quot and rem.
  • Updates documentation for quot, rem, js-mod and mod for clarity.
  • Implement fix (private function to round to zero) with ES6 Math.trunc() if available.

Existing quot and rem tests pass, although there could be some better tests of edge cases (negative decimal num or div, NaN and +-Infinity args).



 Comments   
Comment by Francis Avila [ 24/Mar/15 12:27 PM ]

Better tests found rounding errors in my updated rem, which should stay as-is. (Not simply js-mod after all! Seems to round args first? Not obvious from the spec.) Changed quot however is correct and introduces less error than the current one. Will update patch and tests when I get a chance.

Comment by Francis Avila [ 29/Mar/15 12:39 AM ]

Working patch with tests attached. Tests expanded to cover floating-point cases. rem is now fundamentally the same as master (was more accurate than js-mod!!), but returns results consistent with js-mod for non-finite args or zero divisor.

Comment by Mike Fikes [ 31/Jan/16 3:23 PM ]

cljs-1164.patch no longer applies on master

Comment by Andrea Richiardi [ 14/Feb/16 9:02 PM ]

Patch now applies. I only tested with Nashorn:

V8_HOME not set, skipping V8 tests
SPIDERMONKEY_HOME not set, skipping SpiderMonkey tests
JSC_HOME not set, skipping JavaScriptCore tests
Testing with Nashorn

...

Ran 185 tests containing 17195 assertions.
0 failures, 0 errors.
Tested with 1 out of 4 possible js targets
Comment by Andrea Richiardi [ 14/Feb/16 9:02 PM ]

Patch cleaned up

Comment by Mike Fikes [ 20/Feb/16 10:11 PM ]

Successfully ran Andrea's update to Francis's patch through V8, SpiderMonkey, JavaScriptCore, and Nashorn unit tests.

I also manually ran some of the unit tests in bootstrapped ClojureScript built with the patch.

LGTM.

Comment by Mike Fikes [ 20/Feb/16 10:23 PM ]

Since this is a low-level numerics update, also ran the unit tests through ChackraCore (successfully).





[CLJS-1494] turn cljs.core/*assert* into a goog-define Created: 25/Nov/15  Updated: 22/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Thomas Heller Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File goog-define-assert.patch    
Patch: Code

 Description   

This patch turns the cljs.core/*assert* boolean into a goog.define and also checks *assert* at runtime (instead of only at compile-time).

The closure define option allows the closure compiler to eliminate asserts in :advanced, while :none builds can keep the asserts. This is one of the few remaining issues that prevent :advanced builds to re-use :none compiled (cached) files.

:elide-asserts is unaffected to keep this as simple as possible, but could be built on top of the goog.define instead of actually affecting the compiled output.



 Comments   
Comment by Mike Fikes [ 20/Feb/16 8:02 AM ]

Patch no longer applies, probably owing to CLJS-970.

Comment by Thomas Heller [ 22/Feb/16 5:08 AM ]

There was one more issue I discovered with my approach. My goal was to enable the Closure Compiler to eliminate the asserts when using :advanced compilation. This works perfectly fine with using a goog.define for *assert* but the compiler will complain if you try to adjust the define later since goog.define vars are not allowed to be adjusted at runtime.

(binding [*assert* false]
  (something-that-asserts))

This works in CLJ but not in CLJS since *assert* is only checked at compile time. If compiled with :elide-asserts true you can't bind assert to true either since the code no longer exists.

So some compromise must be made either way, the best solution IMHO would be to have a goog.define which lets the compiler decide whether to eliminate the asserts or not, independent from the *assert* and then moving the assert check itself into js instead of the compiler.

Happy to write the patch if interested.





[CLJS-1572] REPL doesn't give error for expressions with too many right parentheses. Created: 15/Feb/16  Updated: 22/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: J David Eisenberg Assignee: David Nolen
Resolution: Unresolved Votes: 1
Labels: repl
Environment:

Fedora 23, java version "1.8.0_40", javac 1.8.0_40, clojure 1.7.0


Attachments: Text File CLJS-1572.patch    
Patch: Code

 Description   

I was expecting an error message from this; using [org.clojure/clojurescript "1.7.228"]; the Clojure REPL does produce an error.

To quit, type: :cljs/quit
cljs.user=> (+ 3 5)))))
8


 Comments   
Comment by Mike Fikes [ 16/Feb/16 12:49 PM ]

A suggestion on a strategy to fix this: Make the ClojureScript REPL sequentially process all of the forms it can read on a line, just like the Clojure REPL does:

user=> 3 (+ 3 5) 7
3
8
7

If this is done, then the fix for this ticket will fall out “for free” and the ClojureScript REPL will error when it hits a form that appears to start with ).

Comment by Mike Fikes [ 21/Feb/16 4:01 PM ]

The REPL code is very close to working the way mentioned in the previous comment. It currently does not only because this line

https://github.com/clojure/clojurescript/blob/c59e957f6230c07e7a228070dd8eb393d5b8ce40/src/main/clojure/cljs/repl.cljc#L100

invokes code that causes a new PushbackReader to wrap things (discarding things):

https://github.com/clojure/clojurescript/blob/c59e957f6230c07e7a228070dd8eb393d5b8ce40/src/main/clojure/cljs/repl.cljc#L773-L775

If you either let the PushbackReader once and let that reader fn close over it, or otherwise comment out things so that a new PushbackReader is not created for each loop / recur, you will see that the code behaves as suggested in the previous comment, having the desired effect.

The only thing I see that would need to be additionally sorted out with such a patch is being a little more clever about when need-prompt evaluates to true, etc. (otherwise polishing thing so there are no missed corner cases).

Comment by Mike Fikes [ 21/Feb/16 11:02 PM ]

Attached a patch that, in essence makes the ClojureScript REPL behave like the Clojure REPL with respect to multiple items on a line and with respect to detecting malformed input. The patch is fairly straightforward, but could use some testing. I've tried things like

cljs.user=> 3_    ; where _ here is a space

cljs.user=> 3 4 5

cljs.user=> 3)

cljs.user=> 3))

cljs.user=> 3 [4
5]

cljs.user=> (let [x 1]
(+ 1 "a"))         ;; testing to make sure line numbers are right

All the above is looking good to me.

Here is the commit comment:

Take extra care to preserve the state of in so that anything beyond
the first form remains for reading. This fundamentally makes the
ClojureScript REPL behave like the Clojure REPL. In particular, it
allows entering multiple forms on a single line (which will be evaluated
serially). It also means that if malformed input lies beyond the initial
form, it will be read and will cause an exception (just like in the
Clojure REPL).

The bulk of the complexity in this commit has to do with the case where
a new line-numbering reader is established, so that errors in forms
can be associated with line numbers, starting with line 1 being the
first line of the form. This requires a little extra handling because
the source-logging-push-back-reader introduces an extra 1-character
buffer which must be transferred back to the original (pre-bound) in,
otherwise things like an unmatched extra paren right after a well-formed
form won't be detected (as the paren would be in the 1-char buffer and
discarded.)

Also, a Java PushbackReader needs to be eliminated, as it causes things
to fail to behave like the Clojure REPL.

Comment by Mike Fikes [ 21/Feb/16 11:14 PM ]

Note that one extremely useful thing this patch enables is pasting of multiple forms into a ClojureScript REPL!

This fails if pasted using the current cljs.jar, but works with the patch applied:

(def a 1)

(def b 2)

(def c (+ a b))

c




[CLJS-1561] WARN if recur passes non-inferred type Created: 06/Feb/16  Updated: 23/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1561.patch    
Patch: Code

 Description   

Take this code as an example:

(defn f [^boolean b]
  (loop [x b]
    (if x
      (recur 0)
      :done)))

The type of x is inferred to be Boolean, but there is a recur form that can be statically deduced to be passing a non-Boolean.

This ticket asks that a WARN be issued for this case, and perhaps others (where maybe x itself is directly type hinted).



 Comments   
Comment by Mike Fikes [ 06/Feb/16 2:59 PM ]

Attached a patch which warns on for the case of boolean and number, since those two types have special handling.

Some example usage:

cljs.user=> (defn f [^boolean b]
       #_=>   (loop [x b]
       #_=>     (if x
       #_=>       (recur 0)
       #_=>       :done)))
WARNING: recur target parameter x has inferred type boolean, but being passed type number at line 4 
#'cljs.user/f
cljs.user=> (loop [x 1 y true z :hi]
       #_=>   (when false (recur 'a "hi" nil)))
WARNING: recur target parameter x has inferred type number, but being passed type cljs.core/Symbol at line 2 
WARNING: recur target parameter y has inferred type boolean, but being passed type string at line 2 
nil
cljs.user=> (loop [x 1 y true]
       #_=>  (when false (recur nil nil)))
WARNING: recur target parameter x has inferred type number, but being passed type clj-nil at line 2 
WARNING: recur target parameter y has inferred type boolean, but being passed type clj-nil at line 2 
nil
cljs.user=> (loop [x 1]
       #_=>   (let [y (inc x)]
       #_=>     (when false (recur (inc y)))))
nil
cljs.user=> (loop [b true]
       #_=>   (when false (recur (inc 1))))
WARNING: recur target parameter b has inferred type boolean, but being passed type number at line 2 
cljs.user=> (loop [x 1] 
       #_=>   (inc x) 
       #_=>     (when false (recur :hi)))
WARNING: recur target parameter x has inferred type number, but being passed type cljs.core/Keyword at line 3 
nil
cljs.user=> (loop [x :hello] 
       #_=>   (inc x) 
       #_=>     (when false (recur :hi)))
WARNING: cljs.core$macros/+, all arguments must be numbers, got [cljs.core/Keyword number] instead. at line 2 
nil




[CLJS-1591] Compilation time go up significantly when nesting multimethods Created: 25/Feb/16  Updated: 14/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Marian Schubert Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: cljs, compiler

Attachments: Text File CLJS-1591.patch    

 Description   

Code like this takes 140 seconds to compile on my machine. Regular functions don't seem to trigger this behaviour.

(ns slow.core)

(defmulti my-multimethod (fn [x] :whatever))

(defn this-is-sloow-to-compile []
  (my-multimethod
   (my-multimethod
    (my-multimethod
     (my-multimethod
      (my-multimethod
       (my-multimethod
        (my-multimethod
         (my-multimethod
          (my-multimethod
           (my-multimethod
            (my-multimethod
             (my-multimethod
              (my-multimethod
               (my-multimethod
                (my-multimethod
                 (my-multimethod
                  (my-multimethod
                   (my-multimethod
                    (my-multimethod
                     (my-multimethod {})))))))))))))))))))))

$ rm -rf target/ && ./scripts/release 
Building ...
Analyzing jar:file:/Users/maio/.m2/repository/org/clojure/clojurescript/1.7.228/clojurescript-1.7.228.jar!/cljs/core.cljs
Compiling src/slow/core.cljs        <-- here it spends most of the time
Applying optimizations :advanced to 11 sources
... done. Elapsed 141.85386191 seconds

Whole project is here https://github.com/maio/slow-cljs-build



 Comments   
Comment by Mike Fikes [ 04/Mar/16 4:32 PM ]

Hrm. This fix is evidently in the Closure compiler used by 1.7.228: https://github.com/google/closure-compiler/issues/1049

Comment by Thomas Heller [ 06/Mar/16 6:25 AM ]

@mfikes the slowdown is not related to the Closure Compiler since it happens when compiling cljs->js not when optimizing.

The reason for the slowdown is due to the arguments of a multimethod call being analyzed twice (or more in case of deep nesting).

See [1] for the problematic code.

multimethods are not fn-var? so the or does not short circuit and (all-values? argexprs) is reached. This forces the argexprs lazy-seq (thereby analyzing the args). Since the args are not all-values? the else-branch of the if is taken, which then later causes the args to be analyzed again. My math is weak but I'm not mistaken this is O(n!), explaining the dramatic slowdown.

Every var that is not fn-var? is affected by this:

(defn test [& args] :whatever)
(def my-multimethod test)
;; or
(def my-multimethod
  (reify
    IFn
    (-invoke [a] :whatever)))

One solution would be to fix all-values? that instead of running through analyze it could just check whether all args are fixed literals (ie. not list? but all of number? string? symbol? keyword? etc.).

I'm not really sure why the else-branch in [1] exists at all but I assume it is to work around some JS quirks. I will hold off on writing a patch until I figure out why the extra let introduced in the else-branch is needed.

[1] https://github.com/clojure/clojurescript/blob/f58dcdf4dc37ef52d4eb1e2b7c994282bf6351f5/src/main/clojure/cljs/analyzer.cljc#L2257-L2263

PS: forgot to add that this does not happen with :static-fns false since it also prevents the else from being reached.

Comment by Thomas Heller [ 06/Mar/16 6:55 AM ]

The else was introduced in CLJS-855 and is sort of required for invokes without arity information and :static-fns true.

Changing all-values? to just check literals instead of analyzing should be a valid solution.

Comment by Thomas Heller [ 14/Mar/16 8:31 AM ]

The patch removes the extra analyze and instead just checks the few cases that can actually be used without assignment first.

This removes the slowdown while keeping all the functionality.





[CLJS-1607] Advanced compilation bug with `specify!` in JS prototypes Created: 23/Mar/16  Updated: 23/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None
Environment:

affects 1.8.34



 Description   

compiling this code with advanced optimizations

(ns bug.core)

(defprotocol IBug
  (bug [this other] "A sample protocol"))

(defn MyBug [])
(specify! (.-prototype MyBug)
  IBug
  (bug [this other]
    "bug")
  Object
  (foo [this]
    (bug this 3))) ;; line 13

causes the following warning:

WARNING: Use of undeclared Var bug.core/x14072 at line 13


 Comments   
Comment by António Nuno Monteiro [ 23/Mar/16 1:42 PM ]

narrowed it down to this line (https://github.com/clojure/clojurescript/blob/f0ac4c92006ac618516c11e9ca3527904d35d4af/src/main/clojure/cljs/compiler.cljc#L936) being called in `:advanced` because it passes the check of cljs-static-fns in that case





[CLJS-1601] Optimize cljs.core dump Created: 12/Mar/16  Updated: 01/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Nikita Beloglazov Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: bootstrap

Attachments: Text File CLJS-1601.patch     Text File CLJS-1601.patch    

 Description   

When building cljs source that uses cljs.js namespace the final js file is quite huge: 6.4M. As described in wiki: https://github.com/clojure/clojurescript/wiki/Optional-Self-hosting it mostly consists of analysis cache of the cljs.core namespace. As a workaround, the wiki article suggests dumping cache to a separate file and load it at runtime instead of bundling in js binary. I think it is possible to have something in between that doesn't require additional efforts from a user and also optimizes the size of the js file. The idea that instead of dumping cache as raw clojure data-structure it is serialized to string. This way compiler won't compile cache into js (which adds a lot of code) and leave it a string. At runtime, this string will be parsed back to clojure using tools.reader.

Here is the proposal: https://gist.github.com/nbeloglazov/0bf163fb62fa4b61d446

Checking locally it reduces the size of js file from 6.4M to 2.7M which I think quite good. The downside is that now js has to do more work on runtime (parse huge string) when today it simply read js code and evaluates it. But I don't think if it's a big concern. If it is desired to keep all behavior a new option can be added for :dump-core compiler setting, something like :dump-core :string that enables string serialization of the cache.

Does it sound reasonable?



 Comments   
Comment by Nikita Beloglazov [ 27/Mar/16 8:54 PM ]

Attaching suggested fix. Analysis cache is serialized to string and read back to clojure datastructure when cljs.js is initialized.

Comment by David Nolen [ 28/Mar/16 6:39 AM ]

Please change the patch so this optional as you've suggested.

Comment by David Nolen [ 28/Mar/16 6:40 AM ]

Also have you submitted your Clojure CA yet?

Comment by Nikita Beloglazov [ 28/Mar/16 1:35 PM ]

Will do. Yes, I've submitted CA. I used my official name, Mikita Belahlazau there.

Comment by Nikita Beloglazov [ 29/Mar/16 12:16 AM ]

Updated patch that adds option to serialize core analysis cache as string. Possible values of :dump-core are :raw, :string, :none. Old true/false values supported for backward compatibility.

As for default, current patch uses :raw, but I think it makes more sense to use :string. Saving extra few mb of final js is quite good. I think most devs won't go deep into figuring out why js is big and just leave it as it is. Additional one-time parsing performance hit :string introduces acceptable: when :string is used, page loads in 1s while with :raw the time is ~800ms.





[CLJS-1598] Honor printing of function values via IPrintWithWriter Created: 03/Mar/16  Updated: 08/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1598.patch    
Patch: Code

 Description   

If a user wishes to define how function values are printed, allow that to be controlled via IPrintWithWriter with code like

(extend-type function
  IPrintWithWriter
  (-pr-writer [obj writer opts]
    ,,,))


 Comments   
Comment by Mike Fikes [ 03/Mar/16 10:28 AM ]

Can be tested manually:

$ script/nashornrepljs 
To quit, type: :cljs/quit
cljs.user=> inc
#object[cljs$core$inc "function cljs$core$inc(x){
return (x + (1));
}"]
cljs.user=> (extend-type function
  IPrintWithWriter
  (-pr-writer [obj writer _]
    (let [name (.-name obj)
          name (if (empty? name)
                 "Function"
                 name)]
      (write-all writer "#object[" name "]"))))
#object[Function]
cljs.user=> inc
#object[cljs$core$inc]
Comment by David Nolen [ 11/Mar/16 1:04 PM ]

The problem is this makes printing slower. For people using EDN as interchange format this may be a problem. Would need to see some numbers.

Comment by Antonin Hildebrand [ 08/Apr/16 2:11 PM ]

I'm not sure what is the difference between implements? and satisfies?. But by reading the code I would assume that it should be printed by this line:
https://github.com/clojure/clojurescript/blob/9a2be8bc665385be1ef866e2fd76b476c417d2bf/src/main/cljs/cljs/core.cljs#L9056-L9057

Don't we want to change implements? to satisfies? there? Not sure about (perf) implications.





[CLJS-1627] jsdoc parsing fails to recognize union types, breaking resolution Created: 18/Apr/16  Updated: 23/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Patrick Killean Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: patch

Attachments: Text File CLJS-1627.patch    
Patch: Code and Test

 Description   

The Closure Spec For Union Types states that parentheses are necessary for union type expressions. Trying this ...

(defn foo
  "@param {(IBar|IMap)} x"
  [x] 
  ...)

Raises a Closure Error :

...ERROR - Bad type annotation. expected closing }
* @param {user.(IBar|user.IMap)}

This is because comp/resolve-types treats the parentheses as a part of the type tokens and incorrect var resolution occurs as a result. In addition, the compiler emits multiple resolved types separated by "|" characters but does not enclose them in parentheses to create a valid union type.



 Comments   
Comment by Patrick Killean [ 18/Apr/16 4:36 PM ]

This patch includes:

  • comp/resolve-types now removes parentheses when present and emits them when >1 type is detected. This makes parenthesis use optional and existing code remains unbroken (with the added benefit that it may work now)
  • changes to comp/resolve-type
    1. checks for js globals like document or window which are recognized by closure
    2. allows dot.delimited.forms to pass through so we can use types defined in externs and avoid unnecessary resolution
    3. uses ana/resolve-existing-var with a "unresolved jsdoc type" warning
    4. checks if a resolved var is a protocol and warns otherwise. This is more informative than Closure's standard unrecognized type error
  • a test for comp/resolve-types
Comment by David Nolen [ 21/Apr/16 12:45 PM ]

Thanks will try to look more closely at this tomorrow.

Comment by David Nolen [ 23/Apr/16 2:03 PM ]

The patch is getting there, please remove the `js-doc-type` meta stuff. Just extend the signature of resolve-existing-var to take an additional parameter - the confirm-var-exists handler.





[CLJS-1635] Var type implements IEquiv but not IHash Created: 26/Apr/16  Updated: 27/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Chris Vermilion Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: deftype
Environment:

Tested on OS X 10.11, Chrome.


Attachments: Text File CLJS-1635.patch    

 Description   

The Var type implements IEquiv based on the var's symbol, but not IHash. That means that two vars with the same symbol compare equal but don't hash equal, which will cause strange results if you put them in a hash-{map,set}:

cljs.user=> (def foo "bar")
#'cljs.user/foo
cljs.user=> (= #'foo #'foo)
true
cljs.user=> (= (hash #'foo) (hash #'foo))
false

Patch forthcoming.



 Comments   
Comment by Chris Vermilion [ 26/Apr/16 10:41 PM ]

Patch note: The patch fixes the issue but I haven't added a test. It didn't seem like the hash behavior of basic types was tested in general, but moreover while I think this behavior is desirable I'm not sure it should be guaranteed. Happy to write a test if that would be useful.

Comment by Chris Vermilion [ 26/Apr/16 10:48 PM ]

Aside for the curious on how this came up at all: the Schema library uses Vars to specify recursive schemas, and does internal caching by with a map keyed by schemas themselves. If you defined the same recursive schema multiple times, the results would be unpredictable, since two equivalent recursive schemas would compare equal but wouldn't necessarily be interchangeable as map keys.





[CLJS-1636] Mark some symbols in core macros ns as private Created: 27/Apr/16  Updated: 27/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1636.patch    
Patch: Code

 Description   

There are some symbols in the core macros namespace that are not meant for external consumption. Some of these are marked private and some aren't. This ticket asks that the others be marked private as well.

An example of one symbol marked private is defcurried.
An example of one symbol not marked private is caching-hash.



 Comments   
Comment by Mike Fikes [ 27/Apr/16 8:21 AM ]

In CLJS-1636.patch, I checked and it appears nothing in the compiler codebase is explicitly using these symbols outside of the cljs.core namespace. But, it is still worth scanning through these to check if they make sense. For example js-debugger and js-comment are a couple that might actually be meant for public use, but it is difficult to tell.

Comment by Mike Fikes [ 27/Apr/16 2:43 PM ]

Note, that in #cljs-dev slack, there appears to be interest in caching-hash being public.

(I don't mind revising the patch to suit whatever is needed. At the same time, I'm certainly not in a position to take decisions on what is public API or not.)

Comment by Mike Fikes [ 27/Apr/16 2:43 PM ]

Note, that in #cljs-dev slack, there appears to be interest in caching-hash being public.

(I don't mind revising the patch to suit whatever is needed. At the same time, I'm certainly not in a position to take decisions on what is public API or not.)





[CLJS-1344] port Clojure tuples commit Created: 17/Jul/15  Updated: 11/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Task Priority: Trivial
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File 0001-CLJS-1344-port-Clojure-tuples-commit-of-16-July-2015.patch     Text File 0002-CLJS-1344-port-Clojure-tuples-commit-of-16-July-2015.patch     Text File 0003-CLJS-1344-port-Clojure-tuples-commit-of-16-July-2015.patch    

 Description   

See https://github.com/clojure/clojure/commit/36d665793b43f62cfd22354aced4c6892088abd6



 Comments   
Comment by Michał Marczyk [ 18/Jul/15 11:38 AM ]

Patch based on current master.

Comment by Michał Marczyk [ 18/Jul/15 11:50 AM ]

In absence of abstract bases macros seemed like the most straightforward way to keep things DRY. Anything involving transients or metadata still uses PV, as in Clojure.

Comment by Michał Marczyk [ 18/Jul/15 12:04 PM ]

The 0002 patch is the same, except it does NOT change PV's -equiv to check satisfies? IVector rather than instance? PersistentVector. (The 0001 patch does make this change.)

Haven't made up my mind as to whether it's better to switch or not, so I thought I'd prepare both versions.

Comment by Michał Marczyk [ 18/Jul/15 12:25 PM ]

Some benchmark results, for now obtained using SpiderMonkey (I've just realized that I don't have a working V8 setup on this box – which is not the one I normally use for CLJS dev – I'll have to look into fixing that).

On the subject of -equiv, script benchmark says 0002 patch is very slightly faster than 0001 patch, which itself is noticeably faster than master: 633 ms vs 660 ms vs 781 ms in the vector equality benchmark.

The reason both patches are faster than master is undoubtedly their choice to call -count rather than count on the "other thing"; that is 100% justified in the 0002 patch (with instance?) and slightly less justified in the 0001 patch (who could implement IVector without ICounted though?).

Full script/benchmark results:

master
======

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 585 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 137 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 735 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 297 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 23 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 21 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 32 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 524 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1879 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 789 msecs
[coll "foobar"], (first coll), 1000000 runs, 1762 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 150 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 1297 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 587 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 1486 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 105 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 214 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 114 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 111 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 88 msecs
[], (list), 1000000 runs, 13 msecs
[], (list 1 2 3), 1000000 runs, 1691 msecs

;;; vector ops
[], [], 1000000 runs, 9 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 715 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0xd0fabc8 "cljs.tagged_literals.JSValue@d0fabc8"])), 1000000 runs, 972 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 638 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 161 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 323 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 361 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 238 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 211 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 1284 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 1071 msecs
[coll []], (-conj coll 1), 1000000 runs, 1067 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 1133 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 835 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 472 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 557 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 91 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 104 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 690 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 435 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 425 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 251 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 160 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 213 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 184 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 207 msecs
[], (-next v), 1000000 runs, 768 msecs
[], (-rest v), 1000000 runs, 326 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 679 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 781 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 672 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 965 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 403 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 20 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 267 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 675 msecs
[], (list 1 2 3 4 5), 1000000 runs, 639 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 2422 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1725 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 1620 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 3240 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 2525 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 2980 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 453 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 549 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 265 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1695 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 276 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 252 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 2831 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 373 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 276 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 515 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 320 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 355 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 322 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 297 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 416 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 360 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 331 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 894 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 732 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 1027 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 699 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 589 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 330 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 523 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 307 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 530 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 295 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 574 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 291 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 280 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 9 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 251 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 253 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 322 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 336 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 373 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 626 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 266 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 244 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 267 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 340 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 128 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 111 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 361 msecs
[coll pmap], (:f0 coll), 1000000 runs, 385 msecs
[coll pmap], (get coll :f0), 1000000 runs, 370 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 324 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 364 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 3598 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 657 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 565 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 421 msecs

transient map, conj! 100000 items
"Elapsed time: 539 msecs"


;;; set ops
[], #{}, 1000000 runs, 217 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 587 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 477 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 304 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 267 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 289 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 21 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 85 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 1060 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 77 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 369 msecs
;;; second run
[r r], (last r), 1 runs, 94 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 233 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 473 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 1233 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 688 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 53 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 52 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 177 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1298 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 57 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 569 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 67 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 82 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 134 msecs

0001 patch
==========

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 585 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 137 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 735 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 297 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 23 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 21 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 32 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 524 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1879 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 789 msecs
[coll "foobar"], (first coll), 1000000 runs, 1762 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 150 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 1297 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 587 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 1486 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 105 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 214 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 114 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 111 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 88 msecs
[], (list), 1000000 runs, 13 msecs
[], (list 1 2 3), 1000000 runs, 1691 msecs

;;; vector ops
[], [], 1000000 runs, 9 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 715 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0xd0fabc8 "cljs.tagged_literals.JSValue@d0fabc8"])), 1000000 runs, 972 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 638 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 161 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 323 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 361 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 238 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 211 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 1284 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 1071 msecs
[coll []], (-conj coll 1), 1000000 runs, 1067 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 1133 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 835 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 472 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 557 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 91 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 104 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 690 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 435 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 425 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 251 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 160 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 213 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 184 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 207 msecs
[], (-next v), 1000000 runs, 768 msecs
[], (-rest v), 1000000 runs, 326 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 679 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 781 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 672 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 965 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 403 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 20 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 267 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 675 msecs
[], (list 1 2 3 4 5), 1000000 runs, 639 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 2422 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1725 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 1620 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 3240 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 2525 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 2980 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 453 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 549 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 265 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1695 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 276 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 252 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 2831 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 373 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 276 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 515 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 320 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 355 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 322 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 297 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 416 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 360 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 331 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 894 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 732 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 1027 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 699 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 589 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 330 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 523 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 307 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 530 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 295 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 574 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 291 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 280 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 9 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 251 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 253 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 322 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 336 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 373 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 626 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 266 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 244 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 267 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 340 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 128 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 111 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 361 msecs
[coll pmap], (:f0 coll), 1000000 runs, 385 msecs
[coll pmap], (get coll :f0), 1000000 runs, 370 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 324 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 364 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 3598 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 657 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 565 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 421 msecs

transient map, conj! 100000 items
"Elapsed time: 539 msecs"


;;; set ops
[], #{}, 1000000 runs, 217 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 587 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 477 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 304 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 267 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 289 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 21 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 85 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 1060 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 77 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 369 msecs
;;; second run
[r r], (last r), 1 runs, 94 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 233 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 473 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 1233 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 688 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 53 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 52 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 177 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1298 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 57 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 569 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 67 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 82 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 134 msecs

0002 patch
==========

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 645 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 95 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 557 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 2 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 460 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 18 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 32 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 36 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 613 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1658 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 834 msecs
[coll "foobar"], (first coll), 1000000 runs, 1934 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 219 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 1371 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 444 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 351 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 125 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 138 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 97 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 109 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 80 msecs
[], (list), 1000000 runs, 10 msecs
[], (list 1 2 3), 1000000 runs, 1387 msecs

;;; vector ops
[], [], 1000000 runs, 10 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 316 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x57b33c29 "cljs.tagged_literals.JSValue@57b33c29"])), 1000000 runs, 732 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 281 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 484 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 112 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 162 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 194 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 125 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 756 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 606 msecs
[coll []], (-conj coll 1), 1000000 runs, 515 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 648 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 422 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 566 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 89 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 154 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 89 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 631 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 450 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 547 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 589 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 204 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 177 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 143 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 141 msecs
[], (-next v), 1000000 runs, 529 msecs
[], (-rest v), 1000000 runs, 236 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 924 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 633 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 610 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 1138 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 545 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 121 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 281 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 597 msecs
[], (list 1 2 3 4 5), 1000000 runs, 560 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 2573 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1927 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 6163 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 3149 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 1883 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 659 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 611 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 556 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 368 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1707 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 280 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 266 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 2862 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 356 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 391 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 439 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 363 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 321 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 405 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 328 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 444 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 330 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 353 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 1427 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 589 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 1087 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 674 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 719 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 287 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 841 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 327 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 625 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 294 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 630 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 314 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 312 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 10 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 269 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 268 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 309 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 440 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 404 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 756 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 290 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 326 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 279 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 338 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 138 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 177 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 385 msecs
[coll pmap], (:f0 coll), 1000000 runs, 411 msecs
[coll pmap], (get coll :f0), 1000000 runs, 439 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 336 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 457 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 4330 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 831 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 490 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 474 msecs

transient map, conj! 100000 items
"Elapsed time: 565 msecs"


;;; set ops
[], #{}, 1000000 runs, 225 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 711 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 608 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 353 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 322 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 335 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 22 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 99 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 1538 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 37 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 348 msecs
;;; second run
[r r], (last r), 1 runs, 71 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 433 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 287 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 1191 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 831 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 52 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 81 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 207 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1375 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 73 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 429 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 51 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 73 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 133 msecs
Comment by David Nolen [ 19/Jul/15 11:06 AM ]

Thanks will review.

Comment by David Nolen [ 20/Jul/15 2:33 PM ]

This ticket should probably be updated with the latest equiv changes in Clojure master no?

Comment by Michał Marczyk [ 20/Jul/15 2:49 PM ]

Right, will do (plus a rebase on top of current master while I'm at it).

Comment by Michał Marczyk [ 20/Jul/15 6:43 PM ]

New 0003 patch superseding the previous ones attached.

See some new benchmark results below. There are some apparent substantial speedups where I would expect them, there's the somewhat expected slowdown for transient with small vectors.

Freak result: most large vector ops stay around their original spots as expected, except for reduce conj [] over a long range, which becomes weirdly slow. This I find hard to explain, particularly since ranges are used in other benchmarks as well, and those behave sensibly.

I tried compiling the benchmark suite with :optimizations :simple to see if the freak result was something obvious maybe. Oddly enough, all/most timings are significantly better under :simple. Am I missing something obvious here…?

1. master:

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 506 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 70 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 554 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 313 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 15 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 19 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 33 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 401 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1202 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 563 msecs
[coll "foobar"], (first coll), 1000000 runs, 1307 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 161 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 949 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 379 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 1025 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 59 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 88 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 67 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 80 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 62 msecs
[], (list), 1000000 runs, 10 msecs
[], (list 1 2 3), 1000000 runs, 1132 msecs

;;; vector ops
[], [], 1000000 runs, 10 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 495 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x56b66a26 "cljs.tagged_literals.JSValue@56b66a26"])), 1000000 runs, 547 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 435 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 120 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 197 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 95 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 199 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 209 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 893 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 842 msecs
[coll []], (-conj coll 1), 1000000 runs, 765 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 854 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 631 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 413 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 668 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 163 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 89 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 497 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 319 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 316 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 172 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 240 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 128 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 80 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 105 msecs
[], (-next v), 1000000 runs, 460 msecs
[], (-rest v), 1000000 runs, 166 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 746 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 557 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 437 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 820 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 308 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 20 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 250 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 471 msecs
[], (list 1 2 3 4 5), 1000000 runs, 410 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 1898 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1506 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 954 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 2495 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 1864 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 2799 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 2367 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 465 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 268 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1228 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 251 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 270 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 3502 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 330 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 294 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 528 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 282 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 333 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 318 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 286 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 409 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 341 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 353 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 878 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 589 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 972 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 582 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 850 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 269 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 793 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 269 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 596 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 311 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 586 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 321 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 280 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 10 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 254 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 250 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 288 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 322 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 305 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 537 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 257 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 250 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 238 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 338 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 123 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 114 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 342 msecs
[coll pmap], (:f0 coll), 1000000 runs, 368 msecs
[coll pmap], (get coll :f0), 1000000 runs, 356 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 274 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 290 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 3028 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 641 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 412 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 411 msecs

transient map, conj! 100000 items
"Elapsed time: 505 msecs"


;;; set ops
[], #{}, 1000000 runs, 215 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 460 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 516 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 293 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 269 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 290 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 18 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 77 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 957 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 42 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 310 msecs
;;; second run
[r r], (last r), 1 runs, 71 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 234 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 416 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 981 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 699 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 50 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 52 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 184 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1278 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 46 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 333 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 216 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 71 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 142 msecs

2. 0003 patch:

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 480 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 71 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 476 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 621 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 28 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 30 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 54 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 542 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1209 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 564 msecs
[coll "foobar"], (first coll), 1000000 runs, 1257 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 140 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 913 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 424 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 170 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 58 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 89 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 69 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 80 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 61 msecs
[], (list), 1000000 runs, 10 msecs
[], (list 1 2 3), 1000000 runs, 1142 msecs

;;; vector ops
[], [], 1000000 runs, 10 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 272 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x3ff26c9 "cljs.tagged_literals.JSValue@3ff26c9"])), 1000000 runs, 585 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 240 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 273 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 101 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 102 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 102 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 38 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 429 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 392 msecs
[coll []], (-conj coll 1), 1000000 runs, 368 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 395 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 364 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 383 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 75 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 142 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 79 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 395 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 408 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 383 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 406 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 269 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 131 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 80 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 104 msecs
[], (-next v), 1000000 runs, 461 msecs
[], (-rest v), 1000000 runs, 171 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 592 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 562 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 467 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 830 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 426 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 15 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 265 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 580 msecs
[], (list 1 2 3 4 5), 1000000 runs, 386 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 1885 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1362 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 4564 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 2536 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 1940 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 1948 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 452 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 484 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 264 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1540 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 294 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 251 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 3150 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 378 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 278 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 507 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 288 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 339 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 324 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 301 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 533 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 355 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 309 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 757 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 514 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 844 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 622 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 765 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 271 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 521 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 276 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 515 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 314 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 534 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 324 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 327 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 10 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 290 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 254 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 301 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 347 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 365 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 496 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 242 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 254 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 262 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 319 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 128 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 124 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 368 msecs
[coll pmap], (:f0 coll), 1000000 runs, 446 msecs
[coll pmap], (get coll :f0), 1000000 runs, 511 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 328 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 319 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 4954 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 963 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 425 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 421 msecs

transient map, conj! 100000 items
"Elapsed time: 531 msecs"


;;; set ops
[], #{}, 1000000 runs, 230 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 679 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 605 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 295 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 273 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 321 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 22 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 73 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 934 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 33 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 310 msecs
;;; second run
[r r], (last r), 1 runs, 60 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 230 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 400 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 865 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 627 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 49 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 55 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 197 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1296 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 48 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 502 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 50 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 434 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 132 msecs
Comment by David Nolen [ 20/Jul/15 10:35 PM ]

Michal it might be a GC thing? Not sure. These tests need to be run on more engines, kinda wish we had something a bit more visual by now

Comment by Michał Marczyk [ 20/Jul/15 11:32 PM ]

Indeed… I got the V8 situation sorted in the meantime and benchmarked master vs 0003 with a fresh build; I thought the results were pretty encouraging, particularly the 2x speedup for "small vector conj".

1. master:

Benchmarking with V8
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 63 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 24 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 21 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 14 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 14 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 6 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 12 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 21 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 26 msecs
[coll "foobar"], (seq coll), 1000000 runs, 31 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 39 msecs
[coll "foobar"], (first coll), 1000000 runs, 59 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 37 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 139 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 26 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 61 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 20 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 16 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 13 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 10 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 11 msecs
[], (list), 1000000 runs, 5 msecs
[], (list 1 2 3), 1000000 runs, 67 msecs

;;; vector ops
[], [], 1000000 runs, 4 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 71 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x158e6fc2 "cljs.tagged_literals.JSValue@158e6fc2"])), 1000000 runs, 56 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 83 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 28 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 23 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 25 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 22 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 27 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 64 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 60 msecs
[coll []], (-conj coll 1), 1000000 runs, 53 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 54 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 53 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 26 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 25 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 19 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 16 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 24 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 22 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 26 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 56 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 27 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 64 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 23 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 14 msecs
[], (-next v), 1000000 runs, 32 msecs
[], (-rest v), 1000000 runs, 33 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 41 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 36 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 327 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 974 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 74 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 16 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 28 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 61 msecs
[], (list 1 2 3 4 5), 1000000 runs, 146 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 121 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 809 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 141 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 201 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 483 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 290 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 295 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 271 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 27 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 711 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 23 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 24 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 1284 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 66 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 43 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 72 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 52 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 56 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 79 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 57 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 87 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 61 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 70 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 142 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 91 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 171 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 99 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 106 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 34 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 84 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 32 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 82 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 40 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 119 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 47 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 70 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 9 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 36 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 29 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 60 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 69 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 47 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 257 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 13 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 24 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 13 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 80 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 75 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 91 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 83 msecs
[coll pmap], (:f0 coll), 1000000 runs, 64 msecs
[coll pmap], (get coll :f0), 1000000 runs, 56 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 45 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 78 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 224 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 405 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 83 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 155 msecs

transient map, conj! 100000 items
"Elapsed time: 48 msecs"


;;; set ops
[], #{}, 1000000 runs, 5 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 355 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 243 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 48 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 38 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 54 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 24 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 23 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 654 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 42 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 319 msecs
;;; second run
[r r], (last r), 1 runs, 87 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 246 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 163 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 134 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 22 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 85 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 105 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 359 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1413 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 80 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 134 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 8 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 30 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 95 msecs

2. 0003:

Benchmarking with V8
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 51 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 23 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 19 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 13 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 11 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 6 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 12 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 22 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 27 msecs
[coll "foobar"], (seq coll), 1000000 runs, 39 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 34 msecs
[coll "foobar"], (first coll), 1000000 runs, 44 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 35 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 137 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 23 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 69 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 20 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 16 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 13 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 10 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 10 msecs
[], (list), 1000000 runs, 4 msecs
[], (list 1 2 3), 1000000 runs, 67 msecs

;;; vector ops
[], [], 1000000 runs, 4 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 40 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x3e5ebdfe "cljs.tagged_literals.JSValue@3e5ebdfe"])), 1000000 runs, 55 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 46 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 31 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 16 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 16 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 16 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 24 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 31 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 29 msecs
[coll []], (-conj coll 1), 1000000 runs, 25 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 22 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 26 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 26 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 26 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 28 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 24 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 30 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 26 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 29 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 60 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 32 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 63 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 22 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 14 msecs
[], (-next v), 1000000 runs, 36 msecs
[], (-rest v), 1000000 runs, 33 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 44 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 37 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 281 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 1025 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 75 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 17 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 25 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 69 msecs
[], (list 1 2 3 4 5), 1000000 runs, 206 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 121 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 757 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 924 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 184 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 453 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 320 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 301 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 267 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 27 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 727 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 24 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 25 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 762 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 66 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 50 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 74 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 56 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 62 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 82 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 61 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 90 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 65 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 72 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 142 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 92 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 163 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 102 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 105 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 35 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 86 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 31 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 80 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 42 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 116 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 47 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 67 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 5 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 35 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 30 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 52 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 61 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 44 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 230 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 13 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 22 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 11 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 70 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 65 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 73 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 96 msecs
[coll pmap], (:f0 coll), 1000000 runs, 64 msecs
[coll pmap], (get coll :f0), 1000000 runs, 52 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 47 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 79 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 216 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 405 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 80 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 156 msecs

transient map, conj! 100000 items
"Elapsed time: 53 msecs"


;;; set ops
[], #{}, 1000000 runs, 5 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 515 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 266 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 50 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 38 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 62 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 23 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 30 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 636 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 44 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 322 msecs
;;; second run
[r r], (last r), 1 runs, 90 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 229 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 196 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 133 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 23 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 85 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 108 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 349 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1387 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 79 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 122 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 8 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 154 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 94 msecs
Comment by Michał Marczyk [ 20/Jul/15 11:35 PM ]

(No rebase this time, as 0003 still applies cleanly and all tests pass.)

Comment by David Nolen [ 10/Nov/15 8:41 AM ]

De-prioritized. We need to be able to prove the issues around the proposed JVM approach doesn't also appear under popular JS engines.





[CLJS-1364] cljs.js: Update default *load-fn* args to reflect docstring Created: 23/Jul/15  Updated: 14/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Trivial
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1364-1.patch    

 Description   

The default *load-fn* :

(fn [name cb]
    (throw (js/Error. "No *load-fn* set")))

But the name arg reflects an older impl, with the new arg actually being a map.

To avoid confusion for anyone reading this code, perhaps

(fn [_ _]
    (throw (js/Error. "No *load-fn* set")))

or maybe name the first argument something meaningful?



 Comments   
Comment by Andrea Richiardi [ 18/Dec/15 6:52 PM ]

I decided to give it a try





Generated at Fri Apr 29 05:09:46 CDT 2016 using JIRA 4.4#649-r158309.