<< Back to previous view

[CLJS-436] defn missing arg vector gives error about max Created: 06/Dec/12  Updated: 29/Jul/13

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

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

Attachments: Text File 0001-CLJS-436-validate-arguments-to-fn-forms.patch    

 Comments   
Comment by David Nolen [ 06/Dec/12 10:09 AM ]

The issue is actually a bit subtle for example:

(defn foo "name")

is valid Clojure - however this doesn't work in ClojureScript. In this case ClojureScript generates the following:

function () {
    switch (arguments.length) {
    }
    throw (new Error("Invalid arity: " + arguments.length));
}
Comment by David Nolen [ 06/Dec/12 10:19 AM ]

After talking to Rich the above really should not compile.

Comment by Michał Marczyk [ 08/Dec/12 6:09 PM ]

Here's a patch with the fn* validations I could think of. It seems to cause the compilation time to increase by a measurable amount, which I find quite surprising (even though it's to be expected that there will be quite of few fn* forms in CLJS sources). I'd love to know if it's just on my box (let's hope so). If validations really have that sort of effect, I'm thinking about refactoring the analyser so that they can be turned on (which should also be the default) and off (for particularly fast compilations).

Comment by Michał Marczyk [ 08/Dec/12 6:11 PM ]

Ah, wait, there's a minor typo in the commit message, I'll fix it in a second...

Comment by Michał Marczyk [ 08/Dec/12 6:13 PM ]

...um, no there isn't. Sorry for the confusion.





[CLJS-299] PersistentVector push-tail, do-assoc, pop-tail should not contain recursive calls Created: 04/Jun/12  Updated: 29/Jul/13

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

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

Attachments: Text File 0001-CLJS-299-eliminate-recursive-calls-in-PV-s-push-tail.patch     Text File 0001-CLJS-299-eliminate-recursive-calls-in-PV-s-push-tail.patch    

 Description   

This prevents V8 inlining. Lazlo's earlier experiments seemed to suggest ~30% gain.



 Comments   
Comment by Michał Marczyk [ 15/Aug/12 6:17 AM ]

Here's a patch which eliminates recursion in all the fns mentioned in the ticket name.

I haven't done much in the way of measuring the impact on perf; a naive benchmark of (assoc huge-vector 123 :foo) seems to indicate a modest perf gain (a few percent shaved off the runtime).

Note that whether pop-tail as implemented in this patch is an improvement over the previous version is not that clear, since it needs to precalculate the level at which the main loop should terminate. Careful measurements will be necessary. (I wouldn't be surprised if the difference turned out to be unmeasurable, in which case I guess we needn't bother with the change.)

push-tail and do-assoc have no such problems – all the work being done needs to be done (and is now done in a non-recursive fashion).

Hm... Actually that probably means this patch should be split in two (pop-tail & rest). I'll get around to that soon.

More importantly, some benchmarks for large data structures should be added (assoc / conj of 32 items (at any rate, a large enough number of items for the tail to be pushed in) / repeated pops (again, enough to pop the tail)). ("Large" here means "large enough for the shift of the vector to grow at least to 10", meaning size > 1024 + 32; shift 15 would be better, requiring size > 32768 + 32.)

Comment by David Nolen [ 17/Aug/12 12:36 AM ]

Awesome! Lets split the patch in two.

Comment by Michał Marczyk [ 17/Aug/12 4:23 AM ]

Ok. Here's a new patch which eliminates recursion in push-tail and do-assoc. I guess the large data structure benchmarks should probably get in first – see CLJS-357 (some benchmarks already attached – could use some scrutiny in case they're not exercising what they should be; initial runs indicate a very modest speedup).

I'll give pop-tail a little more thought and write a new patch for it sometime soon too. (Perhaps worth pointing out is that the zlvl precalculation in the current version of the pop-tail patch doesn't need to traverse any nodes – it's all bit twiddling – so it should be pretty cheap... I guess we'll see what the benchmarks say – I haven't actually ran them on this code yet.)

Comment by David Nolen [ 29/Aug/12 8:44 PM ]

So how did the benchmarks turn out for these?

Comment by David Nolen [ 31/Aug/12 9:04 AM ]

I applied and ran these benchmarks using the lasted V8. These don't seem to make any difference. Perhaps V8 no longer has issues with the recursive cases? I didn't test beyond running the benchmark script. Might be worth getting something up on JSPerf.





[CLJS-246] Use protocol mask test in protocol fns Created: 09/May/12  Updated: 29/Jul/13

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

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

Attachments: Text File 0001-CLJS-246-have-protocol-methods-check-bitmasks-for-fa.patch    

 Description   

This is a performance win on many browsers.

http://jsperf.com/direct-vs-chain/8



 Comments   
Comment by Michał Marczyk [ 10/May/12 1:11 PM ]

See CLJS-247 for comments relevant to this patch.

Comment by David Nolen [ 10/May/12 3:30 PM ]

Not seeing much of a perf benefit from this, though Michal reports differently. More investigation is needed.

Comment by David Nolen [ 17/Jun/12 12:14 PM ]

patch no longer applies. I wonder if I see bad behavior because I was testing with node or it was prior to the fixes around avoiding deoptimization.

Comment by Michał Marczyk [ 17/Jun/12 9:28 PM ]

I'll bring it up to date with the recent changes, thanks for the prod!





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

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

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

Attachments: 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).





Generated at Fri Apr 18 03:44:10 CDT 2014 using JIRA 4.4#649-r158309.