Add three-arities to < <= > >= = == not=

Description

In my practice, using three-arities of less/greater operations is pretty common for e.g. checking a number is in range:

The problem is, it is almost three times as slow compared to (and (< 0 temp) (< temp 100)).

This happens because three-arities are handled by the generic vararg arity branch:

This patch adds special handling for three-arities to these fns: < <= > >= = == not=

The performance gains are quite significant:

Higher arities also become faster, mainly because there's one less iteration now:

This patch also changes vararg artity of not= to use next/recur instead of apply:

Results are good:

I'm also doing what did in CLJ-1912 (calculating (next more) just once), although perf gains from that alone are not that big.

My point here is that optimizing three-arities makes sence because they appear in the real code quite often. Higher arities (4 and more) are much less widespread.

Environment

None

Activity

Show:
Jozef Wagner
May 16, 2017, 5:55 AM

There's a quite serious bug in the supplied patch(es), that causes e.g. (= 3 3 2) to return true. Because of this the benchmarks are flawed too I guess.

Nikita Prokopov
May 16, 2017, 9:13 PM

thanks for spotting this! Attaching an updated path. Benchmark wasn’t flawed too much because perf gain comes not from doing one less/one more comparison but from not having an overhead of calling a fn with unknown arity.

Alex Miller
July 23, 2019, 3:00 PM

If someone has the time to add tests for this, would be happy to prescreen for 1.11.

Nikita Prokopov
July 23, 2019, 4:12 PM

done

Alex Miller
August 21, 2019, 6:47 PM

In `=`, you use nested if's. If the others, you use `and`. These both seem like options in all cases - did you compare performance? I expect the generated bytecode is similar but seems hard to justify making different choices in these two very similar cases.

Seems like this could also be an inlined arity of these functions, not sure of the perf difference there either? Inlining has the downside of increasing bytecode size and affecting JIT targets.

On the interop calls, you can use clojure.lang.Numbers/gt or whatever instead of the older . syntax.

Assignee

Unassigned

Reporter

Nikita Prokopov

Labels

Approval

Triaged

Patch

Code

Priority

Major