[CLJ-1000] Performance drop in PersistentHashMap.valAt(...) in v.1.4 -- Util.hasheq(...) ? Created: 21/May/12 Updated: 01/Mar/13 Resolved: 11/Dec/12 |
|
| Status: | Closed |
| Project: | Clojure |
| Component/s: | None |
| Affects Version/s: | Release 1.4 |
| Fix Version/s: | Release 1.5 |
| Type: | Enhancement | Priority: | Major |
| Reporter: | Oleksandr Shyshko | Assignee: | Timothy Baldridge |
| Resolution: | Completed | Votes: | 1 |
| Labels: | performance | ||
| Environment: |
Java(TM) SE Runtime Environment (build 1.7.0_04-b21) |
||
| Attachments: |
|
| Patch: | Code |
| Approval: | Ok |
| Description |
|
It seems there is a 30-40% performance degradation of PersistentHashMap.valAt(...) in Clojure 1.4. I have created a demo project with more details and some profiling information here: |
| Comments |
| Comment by Christophe Grand [ 27/Nov/12 8:30 AM ] |
|
I added a patch consisting of three commits:
|
| Comment by Timothy Baldridge [ 30/Nov/12 12:10 PM ] |
|
In the process of screening this, I'm not seeing much of a performance difference after applying the patch. before patch: Version: 1.5.0-master-SNAPSHOT after patch: Version: 1.5.0-master-SNAPSHOT clojure 1.4: Version: 1.4.0 clojure 1.3 Version: 1.3.0 I blew away my local clojure repo and re-applied the patch just to make sure, but the results are the same. Does this fix not optimize the case given in the original test project? For reference I'm running this code: (defn -main (println) (def mm 10000) (def str-keys (map str (range mm))) (def kw-keys (map #(keyword (str %)) (range mm))) (def sym-keys (map #(symbol (str %)) (range mm))) (println)) |
| Comment by Christophe Grand [ 30/Nov/12 2:10 PM ] |
|
Sorry, I was too quick to react on the ML (someone said it was related to hasheq caching and since I had the patch almost ready: on a project I noticed too much time spent computing hasheq on vectors). In 1.4, for a "regular" object, it must fails two instanceof tests before calling .hashCode(). |
| Comment by Timothy Baldridge [ 30/Nov/12 2:16 PM ] |
|
Marking as incomplete, should we also delete the patch as it seems like it should be in a different ticket? |
| Comment by Christophe Grand [ 03/Dec/12 10:00 AM ] |
|
In 1.3, #'hash was going through Object.hashCode and thus was simple and fast. Plus collections hashes were cached. The caching-hasheq-v2.diff patchset reintroduces hashes caching for collections/hasheq and reorders the instanceof tests (to test for IHashEq before Number) and makes Keyword and Symbol implement IHashEq to branch fast in Util.hasheq. I recommend adding a collection test to the current benchmark: (defn -main
[& args]
(println)
(println "Version: " (clojure-version))
(def mm 10000)
(def str-keys (map str (range mm)))
(def m (zipmap str-keys (range mm)))
(time (dotimes [i mm] (doseq [k str-keys] (m k))))
(def kw-keys (map #(keyword (str %)) (range mm)))
(def m (zipmap kw-keys (range mm)))
(time (dotimes [i mm] (doseq [k kw-keys] (m k))))
(def sym-keys (map #(symbol (str %)) (range mm)))
(def m (zipmap sym-keys (range mm)))
(time (dotimes [i mm] (doseq [k sym-keys] (m k))))
(def vec-keys (map (comp (juxt keyword symbol identity) str) (range mm)))
(def m (zipmap vec-keys (range mm)))
(time (dotimes [i mm] (doseq [k vec-keys] (m k))))
(println))
|
| Comment by Timothy Baldridge [ 03/Dec/12 10:38 AM ] |
|
For some reason I can't get v2 to build against master. It applies cleanly, but fails to build. |
| Comment by Christophe Grand [ 03/Dec/12 11:30 AM ] |
|
Timothy: I inadvertently deleted a "public" modifier before commiting... fixed in caching-hasheq-v3.diff |
| Comment by Timothy Baldridge [ 10/Dec/12 11:00 AM ] |
|
I now get the following results: Version: 1.4.0 Version: 1.5.0-master-SNAPSHOT (pre-patch) Version: 1.5.0-master-SNAPSHOT (post-patch) Marking as screened |
| Comment by Oleksandr Shyshko [ 10/Dec/12 3:53 PM ] |
|
Please, could you add as a comment the bench result using 1.3 vs 1.5-master-post-patch? |
| Comment by Oleksandr Shyshko [ 11/Dec/12 2:13 PM ] |
|
The performance with 1.5-master is now very close to 1.3 for 3/4 of the benchmark. However, this code is still showing 43% performance drop (3411 ms VS 6030 ms – 1.3 VS 1.5-master): (def str-keys (map str (range mm))) Version: 1.3.0 Version: 1.4.0 Version: 1.5.0-master-SNAPSHOT To reproduce: $ cd .. $ git clone https://github.com/oshyshko/clj-perf.git |