<!--
RSS generated by JIRA (4.4#649-r158309) at Sun May 26 04:09:00 CDT 2013

It is possible to restrict the fields that are returned in this document by specifying the 'field' parameter in your request.
For example, to request only the issue key and summary add field=key&field=summary to the URL of your request.
For example:
http://dev.clojure.org/jira/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?jqlQuery=labels+%3D+performance&tempMax=1000&field=key&field=summary
-->
<!-- If you wish to do custom client-side styling of RSS, uncomment this:
<?xml-stylesheet href="http://dev.clojure.org/jira/styles/jiraxml2html.xsl" type="text/xsl"?>
-->
<rss version="0.92">
    <channel>
        <title>Clojure JIRA</title>
        <link>http://dev.clojure.org/jira/secure/IssueNavigator.jspa?reset=true&amp;jqlQuery=labels+%3D+performance</link>
        <description>An XML representation of a search request</description>
                <language>en-us</language>
                        <issue start="0" end="13" total="13"/>
                <build-info>
            <version>4.4</version>
            <build-number>649</build-number>
            <build-date>25-07-2011</build-date>
        </build-info>
<item>
            <title>[UNIFY-2] Remove reflection warnings</title>
                <link>http://dev.clojure.org/jira/browse/UNIFY-2</link>
                <project id="10073" key="UNIFY">core.unify</project>
                        <description>&lt;div class=&quot;preformatted panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;preformattedContent panelContent&quot;&gt;
&lt;pre&gt;Reflection warning, clojure/core/unify.clj:30 - reference to field getClass can&apos;t be resolved.
Reflection warning, clojure/core/unify.clj:30 - reference to field isArray can&apos;t be resolved.
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These reflective calls occur frequently enough that they should be resolved.&lt;/p&gt;</description>
                <environment></environment>
            <key id="15097">UNIFY-2</key>
            <summary>Remove reflection warnings</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="5" iconUrl="http://dev.clojure.org/jira/images/icons/status_resolved.gif">Resolved</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="fogus">Fogus</assignee>
                                <reporter username="fogus">Fogus</reporter>
                        <labels>
                        <label>performance</label>
                        <label>reflection</label>
                        <label>unify</label>
                    </labels>
                <created>Thu, 5 Jan 2012 07:26:54 -0600</created>
                <updated>Thu, 5 Jan 2012 07:37:14 -0600</updated>
                    <resolved>Thu, 5 Jan 2012 07:37:14 -0600</resolved>
                                                                    <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="27509" author="fogus" created="Thu, 5 Jan 2012 07:37:14 -0600"  >&lt;p&gt;Fixed in 6b6d1130bf857439d1863f6fc574a7a6541b84b8.&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                <customfield id="customfield_10002" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Approval</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10007">Ok</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                    <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                                                                                            </customfields>
    </item>

<item>
            <title>[TBENCH-16] Add performance testing for STM</title>
                <link>http://dev.clojure.org/jira/browse/TBENCH-16</link>
                <project id="10014" key="TBENCH">test.benchmark</project>
                        <description>&lt;p&gt;Write a test-suite to detect regressions in STM performance.&lt;/p&gt;
</description>
                <environment>64bit Linux, Quad-core</environment>
            <key id="15243">TBENCH-16</key>
            <summary>Add performance testing for STM</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="1" iconUrl="http://dev.clojure.org/jira/images/icons/status_open.gif">Open</status>
                    <resolution id="-1">Unresolved</resolution>
                                <assignee username="-1">Unassigned</assignee>
                                <reporter username="ska2342">Stefan Kamphausen</reporter>
                        <labels>
                        <label>performance</label>
                        <label>test</label>
                    </labels>
                <created>Thu, 23 Feb 2012 13:54:51 -0600</created>
                <updated>Thu, 23 Feb 2012 14:38:00 -0600</updated>
                                                                            <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="27824" author="ska2342" created="Thu, 23 Feb 2012 14:38:00 -0600"  >&lt;p&gt;Patch which adds an stm-namespace containing a collection of performance test functions.&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="10950" name="stm-perf-TBENCH-16.diff" size="22418" author="ska2342" created="Thu, 23 Feb 2012 14:38:00 -0600" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                                                                                            </customfields>
    </item>

<item>
            <title>[LOGIC-50] Rel relation PersistentHashSet becomes LazySeq after issuing a retraction</title>
                <link>http://dev.clojure.org/jira/browse/LOGIC-50</link>
                <project id="10020" key="LOGIC">core.logic</project>
                        <description>&lt;p&gt;The first retraction of facts from a relation transforms the internal PersistentHashSet -set var+atom, which guarantees uniqueness, into a LazySeq which allows duplicate facts to be added. Once the first retraction occurs, a LazySeq persists for the rest of the life of the relation. Only the value of the primary -set var+atom is affected, not the -index var+atom values.&lt;/p&gt;

&lt;p&gt;This issue is not an external correctness issue but does affect performance of subsequent duplicate fact additions which grow the size relation.&lt;/p&gt;

&lt;p&gt;Preparation:&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;user=&amp;gt; (defrel foo x y)
#&amp;lt;user$eval4287$fn__4288 user$eval4287$fn__4288@1a9d489b&amp;gt;
user=&amp;gt; (facts foo [[:joe :man][:jane :woman][:sue :boy]])
nil
user=&amp;gt; foo_2-set
#&amp;lt;Atom@52aaf223: #{[:joe :man] [:jane :woman] [:sue :boy]}&amp;gt;
user=&amp;gt; (class @foo_2-set)
clojure.lang.PersistentHashSet
user=&amp;gt; (retraction foo :jane :woman)
nil&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without patch applied:&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;user=&amp;gt; foo_2-set
#&amp;lt;Atom@52aaf223: ([:joe :man] [:sue :boy])&amp;gt;
user=&amp;gt; (class @foo_2-set)
clojure.lang.LazySeq
user=&amp;gt; (facts foo [[:joe :man][:jane :woman][:sue :boy]])
nil
user=&amp;gt; foo_2-set
#&amp;lt;Atom@52aaf223: ([:sue :boy] [:jane :woman] [:joe :man] [:joe :man] [:sue :boy])&amp;gt;
user=&amp;gt; (class @foo_2-set)
clojure.lang.LazySeq&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With patch applied:&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;user=&amp;gt; foo_2-set
#&amp;lt;Atom@31eb9b15: #{[:joe :man] [:sue :boy]}&amp;gt;
user=&amp;gt; (class @foo_2-set)
clojure.lang.PersistentHashSet
user=&amp;gt; (facts foo [[:joe :man][:jane :woman][:sue :boy]])
nil
user=&amp;gt; foo_2-set
#&amp;lt;Atom@31eb9b15: #{[:joe :man] [:jane :woman] [:sue :boy]}&amp;gt;
user=&amp;gt; (class @foo_2-set)
clojure.lang.PersistentHashSet&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I&apos;ve filed this as a Minor issue as it does not affect core.logic correctness and degraded performance can be avoided by not re-asserting duplicate facts.&lt;/p&gt;

&lt;p&gt;I will also issue a GitHub pull request which can be used or ignored at your convenience.&lt;/p&gt;</description>
                <environment></environment>
            <key id="15666">LOGIC-50</key>
            <summary>Rel relation PersistentHashSet becomes LazySeq after issuing a retraction</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="5" iconUrl="http://dev.clojure.org/jira/images/icons/status_resolved.gif">Resolved</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="dnolen">David Nolen</assignee>
                                <reporter username="abrooks">Aaron Brooks</reporter>
                        <labels>
                        <label>performance</label>
                    </labels>
                <created>Sat, 1 Sep 2012 20:15:44 -0500</created>
                <updated>Wed, 5 Sep 2012 11:32:52 -0500</updated>
                    <resolved>Wed, 5 Sep 2012 11:32:52 -0500</resolved>
                                                                    <due></due>
                    <votes>0</votes>
                        <watches>1</watches>
                        <comments>
                    <comment id="29337" author="dnolen" created="Sun, 2 Sep 2012 17:45:26 -0500"  >&lt;p&gt;Thanks! Is this meant to be applied to master?&lt;/p&gt;</comment>
                    <comment id="29351" author="abrooks" created="Mon, 3 Sep 2012 18:56:08 -0500"  >&lt;p&gt;(cf. here and &lt;a href=&quot;https://github.com/clojure/core.logic/pull/9#issuecomment-8243216&quot;&gt;GitHub&lt;/a&gt; I&apos;ll keep this thread on JIRA.) Yes, this is targeted for master. I don&apos;t know if this warrants a release unto itself.&lt;/p&gt;</comment>
                    <comment id="29353" author="abrooks" created="Tue, 4 Sep 2012 09:17:05 -0500"  >&lt;p&gt;I forgot to mention that I needed to add &quot;src/test/clojure&quot; to the project.clj :source-paths vector to get lein1/2&apos;s test command to run the tests. Is that expected? Is there another way to run the tests that I&apos;m missing? I&apos;m happy to file a separate ticket/patch to address this if project.clj needs to be modified.&lt;/p&gt;</comment>
                    <comment id="29354" author="dnolen" created="Tue, 4 Sep 2012 09:25:18 -0500"  >&lt;p&gt;Thanks for the information. Will apply to master. I actually run tests with &quot;mvn test&quot;, I&apos;ve updated :source-paths so other people can run the tests with lein.&lt;/p&gt;</comment>
                    <comment id="29355" author="dnolen" created="Tue, 4 Sep 2012 09:26:48 -0500"  >&lt;p&gt;I tried to apply the patch but I&apos;m getting a &quot;Patch format detection failed&quot;.&lt;/p&gt;</comment>
                    <comment id="29359" author="abrooks" created="Tue, 4 Sep 2012 16:16:15 -0500"  >&lt;p&gt;The attached patch should work fine with git-apply (in either &quot;cat foo.patch |git apply&quot; or &quot;git apply foo.patch&quot; form). I made the GitHub pull request as I figured that would be the easiest path to pull the changes in.&lt;/p&gt;</comment>
                    <comment id="29360" author="dnolen" created="Tue, 4 Sep 2012 18:04:12 -0500"  >&lt;p&gt;Yes the patch is not properly formatted and we&apos;re not supposed to take pull requests. The patch is missing attribution info. I normally apply patches with &quot;git am foo.patch&quot;. I used the following guide for figuring out how to generate patches that can be applied with &quot;git am&quot;, &lt;a href=&quot;http://ariejan.net/2009/10/26/how-to-create-and-apply-a-patch-with-git&quot;&gt;http://ariejan.net/2009/10/26/how-to-create-and-apply-a-patch-with-git&lt;/a&gt;.&lt;/p&gt;</comment>
                    <comment id="29377" author="abrooks" created="Wed, 5 Sep 2012 09:25:29 -0500"  >&lt;p&gt;My apologies for the extra run-around here. I&apos;ve attached an -002 version of the patch created with git-format-patch which should be amenable to git-am.&lt;/p&gt;</comment>
                    <comment id="29381" author="dnolen" created="Wed, 5 Sep 2012 11:32:52 -0500"  >&lt;p&gt;fixed, &lt;a href=&quot;http://github.com/clojure/core.logic/commit/9bc6eb42be28bfd2b493657344f6eea8f5ed657c&quot;&gt;http://github.com/clojure/core.logic/commit/9bc6eb42be28bfd2b493657344f6eea8f5ed657c&lt;/a&gt;. pushing out a 0.8.0-alpha3 release as well.&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11470" name="core.logic-rel-001.patch" size="1113" author="abrooks" created="Sat, 1 Sep 2012 20:15:44 -0500" />
                    <attachment id="11479" name="core.logic-rel-002.patch" size="1157" author="abrooks" created="Wed, 5 Sep 2012 09:25:29 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10001">Code</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[JDBC-31] distinct? throws clojure.lang.ArityException, when  applied with no arguments</title>
                <link>http://dev.clojure.org/jira/browse/JDBC-31</link>
                <project id="10021" key="JDBC">java.jdbc</project>
                        <description>&lt;p&gt;HSQLDB returns an empty ResultSet when using (.getGeneratedKeys stmt)&lt;br/&gt;
and no keys are generated. So this Exception is thrown for each record&lt;br/&gt;
without generated keys.&lt;/p&gt;

&lt;p&gt;While this Exception is caught in do-prepared-return-keys, this can lead to a huge overhead caused by the JVM exception handling. I did a performance test. &lt;/p&gt;

&lt;p&gt;Before Patch:&lt;/p&gt;

&lt;div class=&quot;preformatted panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;preformattedContent panelContent&quot;&gt;
&lt;pre&gt;clojure.java.test-jdbc&amp;gt; (time (sql/with-connection hsqldb-db (count (apply sql/insert-records :dummy  (map #(hash-map :name (str %) :id %) (range 10000))))))
&quot;Elapsed time: 3429.346743 msecs&quot;
10000
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After Patch:&lt;/p&gt;

&lt;div class=&quot;preformatted panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;preformattedContent panelContent&quot;&gt;
&lt;pre&gt; clojure.java.test-jdbc&amp;gt; (time (sql/with-connection hsqldb-db (count (apply sql/insert-records :dummy  (map #(hash-map :name (str %) :id %) (range 10000))))))
&quot;Elapsed time: 1397.444753 msecs&quot;
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;</description>
                <environment></environment>
            <key id="15438">JDBC-31</key>
            <summary>distinct? throws clojure.lang.ArityException, when  applied with no arguments</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="3" iconUrl="http://dev.clojure.org/jira/images/icons/priority_major.gif">Major</priority>
                    <status id="5" iconUrl="http://dev.clojure.org/jira/images/icons/status_resolved.gif">Resolved</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="seancorfield">Sean Corfield</assignee>
                                <reporter username="juergenhoetzel">J&#252;rgen H&#246;tzel</reporter>
                        <labels>
                        <label>patch,</label>
                        <label>performance</label>
                    </labels>
                <created>Sat, 12 May 2012 11:51:01 -0500</created>
                <updated>Sun, 10 Jun 2012 17:24:42 -0500</updated>
                    <resolved>Sun, 10 Jun 2012 17:24:42 -0500</resolved>
                                                                    <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="28770" author="seancorfield" created="Sun, 10 Jun 2012 17:24:42 -0500"  >&lt;p&gt;Thanx for the patch!&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11208" name="0001-add-make-cols-unique-test-case-empty-cols.patch" size="906" author="juergenhoetzel" created="Sat, 12 May 2012 11:51:01 -0500" />
                    <attachment id="11209" name="0002-distinct-throws-clojure.lang.ArityException-when-app.patch" size="1576" author="juergenhoetzel" created="Sat, 12 May 2012 11:51:01 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10002">Code and Test</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[JDBC-29] Performance improvement (Remove intermediate lazy sequence in resultset-seq)</title>
                <link>http://dev.clojure.org/jira/browse/JDBC-29</link>
                <project id="10021" key="JDBC">java.jdbc</project>
                        <description>&lt;p&gt;This improves performance on large result sets by up to 30%.&lt;/p&gt;</description>
                <environment></environment>
            <key id="15424">JDBC-29</key>
            <summary>Performance improvement (Remove intermediate lazy sequence in resultset-seq)</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="5" iconUrl="http://dev.clojure.org/jira/images/icons/status_resolved.gif">Resolved</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="seancorfield">Sean Corfield</assignee>
                                <reporter username="juergenhoetzel">J&#252;rgen H&#246;tzel</reporter>
                        <labels>
                        <label>patch,</label>
                        <label>performance</label>
                    </labels>
                <created>Thu, 10 May 2012 11:21:14 -0500</created>
                <updated>Thu, 10 May 2012 12:09:00 -0500</updated>
                    <resolved>Thu, 10 May 2012 12:09:00 -0500</resolved>
                                                                    <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="28422" author="seancorfield" created="Thu, 10 May 2012 12:09:00 -0500"  >&lt;p&gt;Nice catch!&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11183" name="0001-Don-t-create-intermediate-lazy-sequences-of-vectors.patch" size="970" author="juergenhoetzel" created="Thu, 10 May 2012 11:21:14 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                                                                                            </customfields>
    </item>

<item>
            <title>[CLJ-1090] Indirect function calls through Var instances fail to clear locals</title>
                <link>http://dev.clojure.org/jira/browse/CLJ-1090</link>
                <project id="10010" key="CLJ">Clojure</project>
                        <description>&lt;p&gt;If you make a function call indirectly by invoking a Var object (which derefs itself and invokes the result), the invocation parameters remain in the thread&apos;s local stack for the duration of the function call, even though they are needed only long enough to be passed into the deref&apos;d function. As a result, passing a lazy seq into a function invoked in its Var form may run out of memory if the seq is forced inside that function. For example:&lt;/p&gt;

&lt;p&gt;(defn total &lt;span class=&quot;error&quot;&gt;&amp;#91;xs&amp;#93;&lt;/span&gt; (reduce + 0 xs))&lt;br/&gt;
(total (range 1000000000))   ; this works, though takes a while&lt;br/&gt;
(#&apos;total (range 1000000000)) ; this dies with out of memory error&lt;/p&gt;

&lt;p&gt;I can provide a patch if it would be useful. The fix should be trivial, something along the lines of wrapping each argN in clojure/lang/Var.java inside a Util.ret1(argN, argN = null) as is done in RestFn.java.&lt;/p&gt;</description>
                <environment>Probably all, but observed on Ubuntu 12.04, OpenJDK 6</environment>
            <key id="15768">CLJ-1090</key>
            <summary>Indirect function calls through Var instances fail to clear locals</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="1" iconUrl="http://dev.clojure.org/jira/images/icons/status_open.gif">Open</status>
                    <resolution id="-1">Unresolved</resolution>
                                <assignee username="-1">Unassigned</assignee>
                                <reporter username="stfactual">Spencer Tipping</reporter>
                        <labels>
                        <label>performance</label>
                    </labels>
                <created>Mon, 22 Oct 2012 18:43:19 -0500</created>
                <updated>Fri, 30 Nov 2012 14:26:45 -0600</updated>
                                    <version>Release 1.4</version>
                                                        <due></due>
                    <votes>0</votes>
                        <watches>2</watches>
                        <comments>
                    <comment id="29776" author="stfactual" created="Tue, 23 Oct 2012 13:37:16 -0500"  >&lt;p&gt;Sorry, I typo&apos;d the example. (defn total ...) should be (defn sum ...).&lt;/p&gt;</comment>
                    <comment id="30054" author="halgari" created="Tue, 27 Nov 2012 11:45:43 -0600"  >&lt;p&gt;fixed typeo in example&lt;/p&gt;</comment>
                    <comment id="30055" author="halgari" created="Tue, 27 Nov 2012 11:47:35 -0600"  >&lt;p&gt;Couldn&apos;t reproduce the exception, but the 2nd example did chew through about 4x the amount of memory. Vetting. &lt;/p&gt;</comment>
                    <comment id="30095" author="halgari" created="Thu, 29 Nov 2012 14:57:28 -0600"  >&lt;p&gt;adding a patch. Since most of Clojure ends up running this code in one way or another, I&apos;d assert that tests are included as part of the normal Clojure test process. &lt;/p&gt;

&lt;p&gt;Patch simply calls Util.ret1(argx, argx=null) on all invoke calls. &lt;/p&gt;</comment>
                    <comment id="30097" author="halgari" created="Thu, 29 Nov 2012 15:17:52 -0600"  >&lt;p&gt;And as a note, both examples in the original report now have extremely similar memory usages. &lt;/p&gt;</comment>
                    <comment id="30118" author="stfactual" created="Fri, 30 Nov 2012 14:22:14 -0600"  >&lt;p&gt;Sounds great, and the patch looks good too. Let me know if I need to do anything else.&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11729" name="var-clear-locals.diff" size="19020" author="halgari" created="Thu, 29 Nov 2012 14:57:28 -0600" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                <customfield id="customfield_10002" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Approval</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10003">Vetted</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                    <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10001">Code</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[CLJ-1087] clojure.data/diff uses set union on key seqs</title>
                <link>http://dev.clojure.org/jira/browse/CLJ-1087</link>
                <project id="10010" key="CLJ">Clojure</project>
                        <description>&lt;p&gt;&lt;tt&gt;clojure.data/diff&lt;/tt&gt;, on line 118, defines:&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;java.util.Map
(diff-similar [a b]
  (diff-associative a b (set/union (keys a) (keys b))))&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;tt&gt;keys&lt;/tt&gt; returns a key seq, this seems like an error. &lt;tt&gt;clojure.set/union&lt;/tt&gt; has strange and inconsistent behavior with regard to non-sets, and in this case the two key seqs are concatenated. Based on a cursory benchmark, it seems that this bug &lt;img class=&quot;emoticon&quot; src=&quot;http://dev.clojure.org/jira/images/icons/emoticons/help_16.gif&quot; height=&quot;16&quot; width=&quot;16&quot; align=&quot;absmiddle&quot; alt=&quot;&quot; border=&quot;0&quot;/&gt; is a slight performance gain when the maps have no common keys, and a significant performance loss when the maps have the same keys. The results are still correct because of the merging reduce in &lt;tt&gt;diff-associative&lt;/tt&gt;.&lt;/p&gt;

&lt;p&gt;The patch is easy (just call set on each key seq).&lt;/p&gt;</description>
                <environment></environment>
            <key id="15753">CLJ-1087</key>
            <summary>clojure.data/diff uses set union on key seqs</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="1" iconUrl="http://dev.clojure.org/jira/images/icons/status_open.gif">Open</status>
                    <resolution id="-1">Unresolved</resolution>
                                <assignee username="-1">Unassigned</assignee>
                                <reporter username="tomoj">Tom Jack</reporter>
                        <labels>
                        <label>bug</label>
                        <label>performance</label>
                    </labels>
                <created>Mon, 15 Oct 2012 03:59:52 -0500</created>
                <updated>Mon, 15 Oct 2012 14:52:42 -0500</updated>
                                                                            <due></due>
                    <votes>0</votes>
                        <watches>1</watches>
                        <comments>
                    <comment id="29650" author="jafingerhut" created="Mon, 15 Oct 2012 14:52:04 -0500"  >&lt;p&gt;clj-1087-diff-perf-enhance-patch-v1.txt dated Oct 15 2012 implements Tom&apos;s suggested performance enhancement, although not exactly in the way he suggested.  It does calculate the union of the two key sequences.&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11562" name="clj-1087-diff-perf-enhance-patch-v1.txt" size="760" author="jafingerhut" created="Mon, 15 Oct 2012 14:52:04 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10001">Code</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[CLJ-1050] Remove a lock in the common case of  deref of delay</title>
                <link>http://dev.clojure.org/jira/browse/CLJ-1050</link>
                <project id="10010" key="CLJ">Clojure</project>
                        <description>&lt;p&gt;Currently, when deref is called in Delay.java, a lock on the Delay is always acquired.&lt;br/&gt;
This is wasteful as most of the time you just want to read the val.&lt;/p&gt;

&lt;p&gt;The attached patch changes this behaviour to the following:&lt;/p&gt;
&lt;ul class=&quot;alternate&quot; type=&quot;square&quot;&gt;
	&lt;li&gt;val is initialised to a known secret value. (During its constructor so it is visible from any thread).&lt;/li&gt;
	&lt;li&gt;When deref is called, val is checked to the known secret value.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;If it is not the secret value, then it has to be the value computed by the function and we return it.&lt;/li&gt;
	&lt;li&gt;If it is the secret value, then we lock this object and revert to the current behaviour.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This is faster than what is done currently and can be very much faster if there is contention on the delay.&lt;/p&gt;</description>
                <environment></environment>
            <key id="15645">CLJ-1050</key>
            <summary>Remove a lock in the common case of  deref of delay</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="5" iconUrl="http://dev.clojure.org/jira/images/icons/priority_trivial.gif">Trivial</priority>
                    <status id="6" iconUrl="http://dev.clojure.org/jira/images/icons/status_closed.gif">Closed</status>
                    <resolution id="2">Declined</resolution>
                                <assignee username="-1">Unassigned</assignee>
                                <reporter username="nicolasoury">Nicolas Oury</reporter>
                        <labels>
                        <label>enhancement</label>
                        <label>patch</label>
                        <label>performance</label>
                    </labels>
                <created>Sun, 26 Aug 2012 13:24:00 -0500</created>
                <updated>Tue, 18 Sep 2012 06:53:01 -0500</updated>
                    <resolved>Tue, 18 Sep 2012 06:53:01 -0500</resolved>
                            <version>Release 1.4</version>
                                                        <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="29270" author="nicolasoury" created="Mon, 27 Aug 2012 02:37:12 -0500"  >&lt;p&gt;Please close and reject. The patch is not working if val has non final fields.&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11454" name="0001-No-lock-in-the-common-path-for-delays.patch" size="1177" author="nicolasoury" created="Sun, 26 Aug 2012 13:24:00 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                                                                                            </customfields>
    </item>

<item>
            <title>[CLJ-1000] Performance drop in PersistentHashMap.valAt(...) in v.1.4 -- Util.hasheq(...) ?</title>
                <link>http://dev.clojure.org/jira/browse/CLJ-1000</link>
                <project id="10010" key="CLJ">Clojure</project>
                        <description>&lt;p&gt;It seems there is a 30-40% performance degradation of PersistentHashMap.valAt(...) in Clojure 1.4.&lt;br/&gt;
Possibly due to references to new CPU-hungry implementation of Util.hasheq(...).&lt;/p&gt;

&lt;p&gt;I have created a demo project with more details and some profiling information here:&lt;br/&gt;
&lt;a href=&quot;https://github.com/oshyshko/clj-perf&quot;&gt;https://github.com/oshyshko/clj-perf&lt;/a&gt;&lt;/p&gt;</description>
                <environment>Java(TM) SE Runtime Environment (build 1.7.0_04-b21)&lt;br/&gt;
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)</environment>
            <key id="15462">CLJ-1000</key>
            <summary>Performance drop in PersistentHashMap.valAt(...) in v.1.4 -- Util.hasheq(...) ?</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="3" iconUrl="http://dev.clojure.org/jira/images/icons/priority_major.gif">Major</priority>
                    <status id="6" iconUrl="http://dev.clojure.org/jira/images/icons/status_closed.gif">Closed</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="halgari">Timothy Baldridge</assignee>
                                <reporter username="oshyshko">Oleksandr Shyshko</reporter>
                        <labels>
                        <label>performance</label>
                    </labels>
                <created>Mon, 21 May 2012 22:06:54 -0500</created>
                <updated>Fri, 1 Mar 2013 09:49:21 -0600</updated>
                    <resolved>Tue, 11 Dec 2012 11:14:49 -0600</resolved>
                            <version>Release 1.4</version>
                                <fixVersion>Release 1.5</fixVersion>
                                        <due></due>
                    <votes>1</votes>
                        <watches>2</watches>
                        <comments>
                    <comment id="30047" author="cgrand" created="Tue, 27 Nov 2012 08:30:26 -0600"  >&lt;p&gt;I added a patch consisting of three commits:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;one which adds caching to seqs, sets, maps, vectors and queues&lt;/li&gt;
	&lt;li&gt;one that aligns the shape of Util/hasheq on the one Util/equiv (to have a consistent behavior from the JIT compiler: without that deoptimization was more penalizing for hasheq than for equiv)&lt;/li&gt;
	&lt;li&gt;one that fix hasheq on records (which was non consistent with its equiv impl.) &amp;#8211; and this commit relies on a static method introduced in the &quot;caching hasheq&quot; commit&lt;/li&gt;
&lt;/ul&gt;
</comment>
                    <comment id="30112" author="halgari" created="Fri, 30 Nov 2012 12:10:26 -0600"  >&lt;p&gt;In the process of screening this, I&apos;m not seeing much of a performance difference after applying the patch. &lt;/p&gt;

&lt;p&gt;before patch:&lt;br/&gt;
user=&amp;gt; (-main)&lt;/p&gt;

&lt;p&gt;Version:  1.5.0-master-SNAPSHOT&lt;br/&gt;
&quot;Elapsed time: 6373.752 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6578.037 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6476.399 msecs&quot;&lt;/p&gt;


&lt;p&gt;after patch:&lt;br/&gt;
user=&amp;gt; (-main)&lt;/p&gt;

&lt;p&gt;Version:  1.5.0-master-SNAPSHOT&lt;br/&gt;
&quot;Elapsed time: 6182.699 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6548.086 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6496.711 msecs&quot;&lt;/p&gt;


&lt;p&gt;clojure 1.4:&lt;br/&gt;
user=&amp;gt; (-main)&lt;/p&gt;

&lt;p&gt;Version:  1.4.0&lt;br/&gt;
&quot;Elapsed time: 6484.234 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6243.672 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6248.898 msecs&quot;&lt;/p&gt;

&lt;p&gt;clojure 1.3&lt;br/&gt;
user=&amp;gt; (-main)&lt;/p&gt;

&lt;p&gt;Version:  1.3.0&lt;br/&gt;
&quot;Elapsed time: 3584.966 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3618.189 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3372.979 msecs&quot;&lt;/p&gt;


&lt;p&gt;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? &lt;/p&gt;

&lt;p&gt;For reference I&apos;m running this code:&lt;/p&gt;

&lt;p&gt;(defn -main&lt;br/&gt;
  &lt;span class=&quot;error&quot;&gt;&amp;#91;&amp;amp; args&amp;#93;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;  (println)&lt;br/&gt;
  (println &quot;Version: &quot; (clojure-version))&lt;/p&gt;

&lt;p&gt;  (def mm 10000)&lt;/p&gt;

&lt;p&gt;  (def str-keys (map str (range mm)))&lt;br/&gt;
  (def m (zipmap str-keys (range mm)))&lt;br/&gt;
  (time (dotimes &lt;span class=&quot;error&quot;&gt;&amp;#91;i mm&amp;#93;&lt;/span&gt; (doseq &lt;span class=&quot;error&quot;&gt;&amp;#91;k str-keys&amp;#93;&lt;/span&gt; (m k))))&lt;/p&gt;

&lt;p&gt;  (def kw-keys (map #(keyword (str %)) (range mm)))&lt;br/&gt;
  (def m (zipmap kw-keys (range mm)))&lt;br/&gt;
  (time (dotimes &lt;span class=&quot;error&quot;&gt;&amp;#91;i mm&amp;#93;&lt;/span&gt; (doseq &lt;span class=&quot;error&quot;&gt;&amp;#91;k kw-keys&amp;#93;&lt;/span&gt; (m k))))&lt;/p&gt;

&lt;p&gt;  (def sym-keys (map #(symbol (str %)) (range mm)))&lt;br/&gt;
  (def m (zipmap sym-keys (range mm)))&lt;br/&gt;
  (time (dotimes &lt;span class=&quot;error&quot;&gt;&amp;#91;i mm&amp;#93;&lt;/span&gt; (doseq &lt;span class=&quot;error&quot;&gt;&amp;#91;k sym-keys&amp;#93;&lt;/span&gt; (m k))))&lt;/p&gt;

&lt;p&gt;  (println))&lt;/p&gt;</comment>
                    <comment id="30116" author="cgrand" created="Fri, 30 Nov 2012 14:10:19 -0600"  >&lt;p&gt;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).&lt;br/&gt;
So no, my patch doesn&apos;t improve anything with kws, syms or strs as keys. However when keys are collections, it fares better.&lt;/p&gt;

&lt;p&gt;In 1.4, for a &quot;regular&quot; object, it must fails two instanceof tests before calling .hashCode().&lt;br/&gt;
If we make keywords and symbols implement IHashEq and reverse the test order (first IHashEq, then Number) it should improve the performance of the above benchmark &amp;#8211; except for Strings.&lt;/p&gt;</comment>
                    <comment id="30117" author="halgari" created="Fri, 30 Nov 2012 14:16:42 -0600"  >&lt;p&gt;Marking as incomplete, should we also delete the patch as it seems like it should be in a different ticket?&lt;/p&gt;</comment>
                    <comment id="30153" author="cgrand" created="Mon, 3 Dec 2012 10:00:07 -0600"  >&lt;p&gt;In 1.3, #&apos;hash was going through Object.hashCode and thus was simple and fast. Plus collections hashes were cached.&lt;br/&gt;
In 1.4 and master, #&apos;hash goes now through two instanceof test (Number and IHasheq in this order) before trying Object.hashCode in last resort. Plus collections hashes are not cached.&lt;br/&gt;
As such I&apos;m not sure Util.hasheq inherent slowness (compared to Util.hash) and hasheq caching should be separated in two issues.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I recommend adding a collection test to the current benchmark:&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;(defn -main
[&amp;amp; args]

(println)
(println &lt;span class=&quot;code-quote&quot;&gt;&quot;Version: &quot;&lt;/span&gt; (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))&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;</comment>
                    <comment id="30158" author="halgari" created="Mon, 3 Dec 2012 10:38:45 -0600"  >&lt;p&gt;For some reason I can&apos;t get v2 to build against master. It applies cleanly, but fails to build.&lt;/p&gt;</comment>
                    <comment id="30165" author="cgrand" created="Mon, 3 Dec 2012 11:30:04 -0600"  >&lt;p&gt;Timothy: I inadvertently deleted a &quot;public&quot; modifier before commiting... fixed in caching-hasheq-v3.diff&lt;/p&gt;</comment>
                    <comment id="30203" author="halgari" created="Mon, 10 Dec 2012 11:00:29 -0600"  >&lt;p&gt;I now get the following results:&lt;/p&gt;

&lt;p&gt;Version:  1.4.0&lt;br/&gt;
&quot;Elapsed time: 6281.345 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6344.321 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6108.55 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 36172.135 msecs&quot;&lt;/p&gt;

&lt;p&gt;Version:  1.5.0-master-SNAPSHOT (pre-patch)&lt;br/&gt;
&quot;Elapsed time: 6126.337 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6320.857 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 6237.251 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 18167.05 msecs&quot;&lt;/p&gt;

&lt;p&gt;Version:  1.5.0-master-SNAPSHOT (post-patch)&lt;br/&gt;
&quot;Elapsed time: 6501.929 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3861.987 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3871.557 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 5049.067 msecs&quot;&lt;/p&gt;


&lt;p&gt;Marking as screened&lt;/p&gt;</comment>
                    <comment id="30206" author="oshyshko" created="Mon, 10 Dec 2012 15:53:06 -0600"  >&lt;p&gt;Please, could you add as a comment the bench result using 1.3 vs 1.5-master-post-patch?&lt;/p&gt;</comment>
                    <comment id="30217" author="oshyshko" created="Tue, 11 Dec 2012 14:13:31 -0600"  >&lt;p&gt;The performance with 1.5-master is now very close to 1.3 for 3/4 of the benchmark.&lt;/p&gt;

&lt;p&gt;However, this code is still showing 43% performance drop (3411 ms VS 6030 ms &amp;#8211; 1.3 VS 1.5-master):&lt;/p&gt;

&lt;p&gt;(def str-keys (map str (range mm)))&lt;br/&gt;
(def m (zipmap str-keys (range mm)))&lt;br/&gt;
(time (dotimes &lt;span class=&quot;error&quot;&gt;&amp;#91;i mm&amp;#93;&lt;/span&gt; (doseq &lt;span class=&quot;error&quot;&gt;&amp;#91;k str-keys&amp;#93;&lt;/span&gt; (m k))))&lt;/p&gt;


&lt;p&gt;Version:  1.3.0&lt;br/&gt;
&quot;Elapsed time: 3411.353 msecs&quot;  &amp;lt;---&lt;br/&gt;
&quot;Elapsed time: 3459.992 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3365.182 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3813.637 msecs&quot;&lt;/p&gt;

&lt;p&gt;Version:  1.4.0&lt;br/&gt;
&quot;Elapsed time: 5710.073 msecs&quot; &amp;lt;---&lt;br/&gt;
&quot;Elapsed time: 5817.356 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 5774.856 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 18754.482 msecs&quot;&lt;/p&gt;

&lt;p&gt;Version:  1.5.0-master-SNAPSHOT&lt;br/&gt;
&quot;Elapsed time: 6030.247 msecs&quot; &amp;lt;---&lt;br/&gt;
&quot;Elapsed time: 3372.637 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3267.481 msecs&quot;&lt;br/&gt;
&quot;Elapsed time: 3852.927 msecs&quot;&lt;/p&gt;


&lt;p&gt;To reproduce:&lt;br/&gt;
$ git clone &lt;a href=&quot;https://github.com/clojure/clojure.git&quot;&gt;https://github.com/clojure/clojure.git&lt;/a&gt;&lt;br/&gt;
$ cd clojure&lt;br/&gt;
$ mvn install -Dmaven.test.skip=true&lt;/p&gt;

&lt;p&gt;$ cd ..&lt;/p&gt;

&lt;p&gt;$ git clone &lt;a href=&quot;https://github.com/oshyshko/clj-perf.git&quot;&gt;https://github.com/oshyshko/clj-perf.git&lt;/a&gt;&lt;br/&gt;
$ cd clj-perf&lt;br/&gt;
$ lein run-all&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11743" name="caching-hasheq-v3.diff" size="10183" author="cgrand" created="Mon, 3 Dec 2012 11:30:04 -0600" />
                    <attachment id="11240" name="clj_13.png" size="139196" author="oshyshko" created="Mon, 21 May 2012 22:06:54 -0500" />
                    <attachment id="11241" name="clj_14.png" size="143427" author="oshyshko" created="Mon, 21 May 2012 22:06:54 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                <customfield id="customfield_10002" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Approval</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10007">Ok</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                    <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10001">Code</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[CLJ-988] the locking in MultiFn.java (synchronized methods) can cause lots of contention in multithreaded programs</title>
                <link>http://dev.clojure.org/jira/browse/CLJ-988</link>
                <project id="10010" key="CLJ">Clojure</project>
                        <description>&lt;p&gt;if you call a single multimethod a lot in multithreaded code you get lots of contention for the lock on the multimethod. this contention slows things down a lot.&lt;/p&gt;

&lt;p&gt;this is due to getMethod being synchronized. it would be great if there was some fast non-locking path through the multimethod.&lt;/p&gt;</description>
                <environment></environment>
            <key id="15419">CLJ-988</key>
            <summary>the locking in MultiFn.java (synchronized methods) can cause lots of contention in multithreaded programs</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="3" iconUrl="http://dev.clojure.org/jira/images/icons/priority_major.gif">Major</priority>
                    <status id="6" iconUrl="http://dev.clojure.org/jira/images/icons/status_closed.gif">Closed</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="stuart.sierra">Stuart Sierra</assignee>
                                <reporter username="hiredman">Kevin Downey</reporter>
                        <labels>
                        <label>bug</label>
                        <label>performance</label>
                    </labels>
                <created>Tue, 8 May 2012 11:14:53 -0500</created>
                <updated>Fri, 21 Sep 2012 14:21:33 -0500</updated>
                    <resolved>Fri, 21 Sep 2012 14:21:33 -0500</resolved>
                                            <fixVersion>Release 1.5</fixVersion>
                                        <due></due>
                    <votes>10</votes>
                        <watches>9</watches>
                        <comments>
                    <comment id="28411" author="hiredman" created="Tue, 8 May 2012 11:30:00 -0500"  >&lt;p&gt;&lt;a href=&quot;http://groups.google.com/group/clojure-dev/browse_thread/thread/6a8219ae3d4cd0ae?hl=en&quot;&gt;http://groups.google.com/group/clojure-dev/browse_thread/thread/6a8219ae3d4cd0ae?hl=en&lt;/a&gt;&lt;/p&gt;</comment>
                    <comment id="28446" author="santiago" created="Fri, 11 May 2012 06:38:34 -0500"  >&lt;p&gt;Here&apos;s a stab in the dark attempt at rewriting MultiFn to use atoms to swap between immutable copies of its otherwise mutable state. &lt;/p&gt;

&lt;p&gt;The four pieces of the MultiFn&apos;s state that are mutable and protected by locks are now contained in the MultiFn.State class, which is immutable and contains convenience functions for creating a new one with one field changed. An instance of this class is now held in an atom in the MultiFn called state. Changes to any of these four members are now done with an atomic swap of these State objects. &lt;/p&gt;

&lt;p&gt;The getMethod/findAndCacheBestMethod complex was rewritten to better enable the atomic logic. findAndCacheBestMethod was replaced with findBestMethod, which merely looks up the method; the caching logic was moved to getMethod so that it can be retried easily as part of the work that method does. &lt;/p&gt;

&lt;p&gt;As it was findAndCacheBestMethod seemingly had the potential to cause a stack overflow in a perfect storm of heavy concurrent modification, since it calls itself recursively if it finds that the hierarchy has changed while it has done its work. This logic is now done in the CAS looping of getMethod, so hopefully that is not even an unlikely possibility anymore.&lt;/p&gt;

&lt;p&gt;There is still seemingly a small race here, since the check is done of a regular variable against the value in a ref. Now as before, the ref could be updated just after you do the check, but before the MultiFn&apos;s state is updated. Of course, only the method lookup part of a MultiFn call was synchronized before; it could already change after the lookup but before the method itself executed, having a stale method finish seemingly after the method had been changed. Things are no different now in general, with the atom-based approach, so perhaps this race is not a big deal, as a stale value can&apos;t persist for long.&lt;/p&gt;

&lt;p&gt;The patch passes the tests and Clojure and multimethods seems to work.&lt;/p&gt;</comment>
                    <comment id="28482" author="hiredman" created="Sat, 12 May 2012 20:59:45 -0500"  >&lt;p&gt;this patch gets rid of the ugly lock contention issues. I have not been able to benchmark it vs. the old multimethod implementation, but looking at it, I would not be surprised if it is faster when the system is in a steady state.&lt;/p&gt;</comment>
                    <comment id="28747" author="stu" created="Fri, 8 Jun 2012 12:11:51 -0500"  >&lt;p&gt;This looks straightforward, except for getMethod. Can somebody with context add more discussion of how method caching works?&lt;/p&gt;

&lt;p&gt;Also, it would be great to have tests with this one.&lt;/p&gt;</comment>
                    <comment id="28810" author="santiago" created="Fri, 15 Jun 2012 04:44:53 -0500"  >&lt;p&gt;Obviously I didn&apos;t write the original code, so I&apos;m not the ideal&lt;br/&gt;
person to explain this stuff. But I did work with it a bit recently,&lt;br/&gt;
so in the hopes that I can be helpful, I&apos;m writing down my&lt;br/&gt;
understanding of the code as I worked with it. Since I came to the&lt;br/&gt;
code and sort of reverse engineered my way to this understanding,&lt;br/&gt;
hopefully laying this all out will make any mistakes or&lt;br/&gt;
misunderstandings I may have made easier to catch and correct. To&lt;br/&gt;
ensure some stability, I&apos;ll talk about the original MultiFn code as it&lt;br/&gt;
stands at this commit:&lt;br/&gt;
&lt;a href=&quot;https://github.com/clojure/clojure/blob/8fda34e4c77cac079b711da59d5fe49b74605553/src/jvm/clojure/lang/MultiFn.java&quot;&gt;https://github.com/clojure/clojure/blob/8fda34e4c77cac079b711da59d5fe49b74605553/src/jvm/clojure/lang/MultiFn.java&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are four pieces of state that change as the multimethod is either&lt;br/&gt;
populated with methods or called.&lt;/p&gt;

&lt;ul class=&quot;alternate&quot; type=&quot;square&quot;&gt;
	&lt;li&gt;methodTable: A persistent map from a dispatch value (Object) to&lt;br/&gt;
    a function (IFn). This is the obvious thing you think it is,&lt;br/&gt;
    determining which dispatch values call which function.&lt;/li&gt;
	&lt;li&gt;preferTable: A persistent map from a dispatch value (Object) to&lt;br/&gt;
    another value (Object), where the key &quot;is preferred&quot; to the value.&lt;/li&gt;
	&lt;li&gt;methodCache: A persistent map from a dispatch value (Object) to&lt;br/&gt;
    function (IFn). By default, the methodCache is assigned the same&lt;br/&gt;
    map value as the methodTable. If values are calculated out of the&lt;br/&gt;
    hierarchy during a dispatch, the originating value and the&lt;br/&gt;
    ultimate method it resolves to are inserted as additional items in&lt;br/&gt;
    the methodCache so that subsequent calls can jump right to the&lt;br/&gt;
    method without recursing down the hierarchy and preference table.&lt;/li&gt;
	&lt;li&gt;cachedHierarchy: An Object that refers to the hierarchy that is&lt;br/&gt;
    reflected in the latest cached values. It is used to check if the&lt;br/&gt;
    hierarchy has been updated since we last updated the cache. If it&lt;br/&gt;
    has been updated, then the cache is flushed.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I think reset(), addMethod(), removeMethod(), preferMethod(),&lt;br/&gt;
prefers(), isA(), dominates(), and resetCache() are extremely&lt;br/&gt;
straightforward in both the original code and the patch. In the&lt;br/&gt;
original code, the first four of those are synchronized, and the other&lt;br/&gt;
four are only called from methods that are synchronized (or from&lt;br/&gt;
methods only called from methods that are synchronized).&lt;/p&gt;

&lt;p&gt;Invoking a multimethod through its invoke() function will call&lt;br/&gt;
getFn(). getFn() will call the getMethod() function, which is&lt;br/&gt;
synchronized. This means any call of the multimethod will wait for and&lt;br/&gt;
take a lock as part of method invocation. The goal of the patch in&lt;br/&gt;
this issue is to remove this lock on calls into the multimethod. It in&lt;br/&gt;
fact removes the locks on all operations, and instead keeps its&lt;br/&gt;
internal mutable state by atomically swapping a private immutable&lt;br/&gt;
State object held in an Atom called state.&lt;/p&gt;

&lt;p&gt;The biggest change in the patch is to the&lt;br/&gt;
getFn()&lt;del&gt;&amp;gt;getMethod()&lt;/del&gt;&amp;gt;findAndCacheBestMethod() complex from the&lt;br/&gt;
original code. I&apos;ll describe how that code works first. &lt;/p&gt;

&lt;p&gt;In the original code, getFn() does nothing but bounce through&lt;br/&gt;
getMethod(). getMethod() tries three times to find a method to call,&lt;br/&gt;
after checking that the cache is up to date and flushing it if it&lt;br/&gt;
isn&apos;t:&lt;/p&gt;

&lt;p&gt;    1. It checks if there&apos;s a method for the dispatch value in the&lt;br/&gt;
    methodCache. &lt;/p&gt;

&lt;p&gt;    2. If not, it calls findAndCacheBestMethod() on the&lt;br/&gt;
    dispatch value. findAndCacheBestMethod() does the following:&lt;/p&gt;

&lt;p&gt;        1. It iterates through every map entry in the method table,&lt;br/&gt;
        keeping at each step the best entry it has found so far&lt;br/&gt;
        (according to the hierarchy and preference tables).  &lt;/p&gt;

&lt;p&gt;        2. If it did not find a suitable entry, it returns null.  &lt;/p&gt;

&lt;p&gt;        3. Otherwise, it checks if the hierarchy has been changed since the cache&lt;br/&gt;
        was last updated. If it has not changed, it inserts the method&lt;br/&gt;
        into the cache and returns it. If it has been changed, it&lt;br/&gt;
        resets the cache and calls itself recursively to repeat the process.&lt;/p&gt;

&lt;p&gt;    3. Failing all else, it will return the method for the default&lt;br/&gt;
    dispatch value.&lt;/p&gt;

&lt;p&gt;Again, remember everything in the list above happens in a call to a&lt;br/&gt;
synchronized function. Also note that as it is currently written,&lt;br/&gt;
findAndCacheBestMethod() uses recursion for iteration in a way that&lt;br/&gt;
grows the stack. This seems unlikely to cause a stack overflow unless&lt;br/&gt;
the multimethod is getting its hierarchy updated very rapidly for a&lt;br/&gt;
sustained period while someone else tries to call it. Nonetheless, the&lt;br/&gt;
hierarchy is held in an IRef that is updated independently of the&lt;br/&gt;
locking of the MultiFn. Finally, note that the multimethod is locked&lt;br/&gt;
only while the method is being found. Once it is found, the lock is&lt;br/&gt;
released and the method actually gets called afterwards without any&lt;br/&gt;
synchronization, meaning that by the time the method actually&lt;br/&gt;
executes, the multimethod may have already been modified in a way that&lt;br/&gt;
suggests a different method should have been called. Presumably this&lt;br/&gt;
is intended, understood, and not a big deal.&lt;/p&gt;

&lt;p&gt;Moving on now to the patch in this issue. As mentioned, the main&lt;br/&gt;
change is updating this entire apparatus to work with a single atomic&lt;br/&gt;
swap to control concurrency. This means that all updates to the&lt;br/&gt;
multimethod&apos;s state have to happen at one instant in time. Where the&lt;br/&gt;
original code could make multiple changes to the state at different&lt;br/&gt;
times, knowing it was safely protected by an exclusive lock, rewriting&lt;br/&gt;
for atom swaps requires us to reorganize the code so that all updates&lt;br/&gt;
to the state happen at the same time with a single CAS.&lt;/p&gt;

&lt;p&gt;To implement this change, I pulled the implicit looping logic from&lt;br/&gt;
findAndCacheBestMethod() up into getMethod() itself, and broke the&lt;br/&gt;
&quot;findBestMethod&quot; part into its own function, findBestMethod(), which&lt;br/&gt;
makes no update to any state while implementing the same&lt;br/&gt;
logic. getMethod() now has an explicit loop to avoid stack-consuming&lt;br/&gt;
recursion on retries. This infinite for loop covers all of the logic&lt;br/&gt;
in getMethod() and retries until a method is successfully found and a&lt;br/&gt;
CAS operation succeeds, or we determine that the method cannot be&lt;br/&gt;
found and we return the default dispatch value&apos;s implementation.&lt;/p&gt;

&lt;p&gt;I&apos;ll now describe the operation of the logic in the for loop. The&lt;br/&gt;
first two steps in the loop correspond to things getMethod() does&lt;br/&gt;
&quot;before&quot; its looping construct in the original code, but we have to do&lt;br/&gt;
in the loop to get the latest values.&lt;/p&gt;

&lt;p&gt;    1. First we dereference our state, and assign this value to both&lt;br/&gt;
    oldState and newState. We also set a variable called needWrite to&lt;br/&gt;
    false; this is so we can avoid doing a CAS (they&apos;re not free) when&lt;br/&gt;
    we have not actually updated the state.  &lt;/p&gt;

&lt;p&gt;    2. Check if the cache is stale, and flush it if so. If the cache&lt;br/&gt;
    gets flushed, set needWrite to true, as the state has changed.&lt;/p&gt;

&lt;p&gt;    3. Check if the methodCache has an entry for this dispatch&lt;br/&gt;
    value. If so, we are &quot;finished&quot; in the sense that we found the&lt;br/&gt;
    value we wanted. However, we may need to update the state. So, &lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;If needWrite is false, we can return without a CAS, so just&lt;br/&gt;
          break out of the loop and return the method.&lt;/li&gt;
&lt;/ul&gt;


&lt;ul&gt;
	&lt;li&gt;Otherwise, we need to update the state object with a CAS. If&lt;br/&gt;
          the CAS is successful, break out of the loop and return the&lt;br/&gt;
          target function. Otherwise, continue on the next iteration&lt;br/&gt;
          of the loop, skipping any other attempts to fetch the method&lt;br/&gt;
          later in the loop (useless work, at this point).&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;    4. The value was not in the methodCache, so call the function&lt;br/&gt;
    findBestMethod() to attempt to find a suitable method based on the&lt;br/&gt;
    hierarchy and preferences. If it does find us a suitable method,&lt;br/&gt;
    we now need to cache it ourselves. We create a new state object&lt;br/&gt;
    with the new method cache and attempt to update the state atom&lt;br/&gt;
    with a CAS (we definitely need a write here, so no need to check&lt;br/&gt;
    needWrite at this point).&lt;/p&gt;

&lt;p&gt;    The one thing that is possibly questionable is the check at this&lt;br/&gt;
    point to make sure the hierarchy has not been updated since the&lt;br/&gt;
    beginning of this method. I inserted this here to match the&lt;br/&gt;
    identical check at the corresponding point in&lt;br/&gt;
    findAndCacheBestMethod() in the original code. That is also a&lt;br/&gt;
    second check, since the cache is originally checked for freshness&lt;br/&gt;
    at the very beginning of getMethod() in the original code. That&lt;br/&gt;
    initial check happens at the beginning of the loop in the&lt;br/&gt;
    patch. Given that there is no synchronization with the method&lt;br/&gt;
    hierarchy, it is not clear to me that this second check is needed,&lt;br/&gt;
    since we are already proceeding with a snapshot from the beginning&lt;br/&gt;
    of the loop. Nonetheless, it can&apos;t hurt as far as I can tell, it&lt;br/&gt;
    is how the original code worked, and I assume there was some&lt;br/&gt;
    reason for that, so I kept the second check.&lt;/p&gt;

&lt;p&gt;    5. Finally, if findBestMethod() failed to find us a method for the&lt;br/&gt;
    dispatch value, find the method for the default dispatch value and&lt;br/&gt;
    return that by breaking out of the loop.&lt;/p&gt;

&lt;p&gt;So the organization of getMethod() in the patch is complicated by two&lt;br/&gt;
factors: (1) making the retry looping explicit and stackless, (2)&lt;br/&gt;
skipping the CAS when we don&apos;t need to update state, and (3) skipping&lt;br/&gt;
needless work later in the retry loop if we find a value but are&lt;br/&gt;
unable to succeed in our attempt to CAS. Invoking a multimethod that&lt;br/&gt;
has a stable hierarchy and a populated cache should not even have a&lt;br/&gt;
CAS operation (or memory allocation) on this code path, just a cache&lt;br/&gt;
lookup after the dispatch value is calculated.&lt;/p&gt;</comment>
                    <comment id="28811" author="santiago" created="Fri, 15 Jun 2012 04:45:58 -0500"  >&lt;p&gt;I&apos;ve updated this patch (removing the old version, which is entirely superseded by this one). The actual changes to MultiFn.java are identical (modulo any thing that came through in the rebase), but this newer patch has tests of basic multimethod usage, including defmulti, defmethod, remove-method, prefer-method and usage of these in a hierarchy that updates in a way interleaved with calls. &lt;/p&gt;</comment>
                    <comment id="28812" author="santiago" created="Fri, 15 Jun 2012 06:38:27 -0500"  >&lt;p&gt;I created a really, really simple benchmark to make sure this was an improvement. The following tests were on a quad-core hyper-threaded 2012 MBP. &lt;/p&gt;

&lt;p&gt;With two threads contending for a simple multimethod: &lt;br/&gt;
The lock-based multifns run at an average of 606ms, with about 12% user, 15% system CPU at around 150%. &lt;br/&gt;
The lockless multifns run at an average of 159ms, with about 25% user, 3% system CPU at around 195%.&lt;/p&gt;

&lt;p&gt;With four threads contending for a simple multimethod:&lt;br/&gt;
The lock-based multifns run at an average of 1.2s, with about 12% user, 15% system, CPU at around 150%.&lt;br/&gt;
The lockless multifns run at an average of 219ms, with about 50% user, 4% system, CPU at around 330%.&lt;/p&gt;

&lt;p&gt;You can get the code at &lt;a href=&quot;https://github.com/davidsantiago/multifn-perf&quot;&gt;https://github.com/davidsantiago/multifn-perf&lt;/a&gt; &lt;/p&gt;</comment>
                    <comment id="29159" author="santiago" created="Tue, 14 Aug 2012 22:02:00 -0500"  >&lt;p&gt;It&apos;s been a couple of months, and so I just wanted to check in and see if there was anything else needed to move this along. &lt;/p&gt;

&lt;p&gt;Also, Alan Malloy pointed out to me that my benchmarks above did not mention single-threaded performance. I actually wrote this into the tests above, but I neglected to report them at the time. Here are the results on the same machine as above (multithreaded versions are basically the same as the previous comment). &lt;/p&gt;

&lt;p&gt;With a single thread performing the same work:&lt;br/&gt;
The lock-based multifns run at an average of 142ms. &lt;br/&gt;
The lockless multifns run at an average of 115ms. &lt;/p&gt;

&lt;p&gt;So the lockless multimethods are still faster even in a single-threaded case, although the speedup is more modest compared to the speedups in the multithreaded cases above. This is not surprising, but it is good to know.&lt;/p&gt;</comment>
                    <comment id="29213" author="stuart.sierra" created="Fri, 17 Aug 2012 14:58:06 -0500"  >&lt;p&gt;Screened. The approach is sound. &lt;/p&gt;

&lt;p&gt;I can confirm similar performance measurements using David Santiago&apos;s benchmark, compared with Clojure 1.5 master as of commit f5f4faf.&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;Mean runtime (ms) of a multimethod when called repeatedly from N threads:

|            | N=1 | N=2 | N=4 |
|------------+-----+-----+-----|
| 1.5 master |  80 | 302 | 765 |
| lockless   |  63 |  88 | 125 |&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My only concern is that the extra allocations of the &lt;tt&gt;State&lt;/tt&gt; class will create more garbage, but this is probably not significant if we are already using persistent maps. It would be interesting to compare this implementation with one using thread-safe mutable data structures (e.g. ConcurrentHashMap) for the cache.&lt;/p&gt;</comment>
                    <comment id="29216" author="santiago" created="Fri, 17 Aug 2012 19:05:54 -0500"  >&lt;p&gt;I think your assessment that it&apos;s not significant compared to the current implementation using persistent maps is correct. Regarding the extra garbage, note that the new State is only created when the hierarchy has changed or there&apos;s a cache miss (related, obviously)... situations where you&apos;re already screwed. Then it won&apos;t have to happen again for the same method (until another change to the multimethod). So for most code, it won&apos;t happen very often. &lt;/p&gt;

&lt;p&gt;ConcurrentHashMap might be faster, it&apos;d be interesting to see. My instinct is to keep it as close to being &quot;made out of Clojure&quot; as possible. In fact, it&apos;s hard to see why this couldn&apos;t be rewritten in Clojure itself some day, as I believe Chas Emerick has suggested. Also, I would point out that two of the three maps are used from the Clojure side in core.clj. I assume they would be happier if they were persistent maps. &lt;/p&gt;

&lt;p&gt;Funny story: I was going to point out the parts of the code that were called from the clojure side just now, and alarmingly cannot find two of the functions. I think I must have misplaced them while rewriting the state into an immutable object. Going to attach a new patch with the fix and some tests for it in a few minutes.&lt;/p&gt;</comment>
                    <comment id="29218" author="santiago" created="Fri, 17 Aug 2012 19:44:54 -0500"  >&lt;p&gt;Latest patch for this issue. Supersedes issue988-lockless-multifn+tests.diff as of 08/17/2012.&lt;/p&gt;</comment>
                    <comment id="29219" author="santiago" created="Fri, 17 Aug 2012 19:49:13 -0500"  >&lt;p&gt;As promised, I reimplemented those two functions. I also added more multimethod tests to the test suite. The new tests should definitely prevent a similar mistake. While I was at it, I went through core.clj and found all the multimethod API functions I could and ensured that there were at least some basic functionality tests for all of them. The ones I found were: defmulti, defmethod, remove-all-methods, remove-method, prefer-method, methods, get-method, prefers (Some of those already had tests from the earlier version of the patch). &lt;/p&gt;

&lt;p&gt;Really sorry about catching this right after you vetted the patch. 12771 test assertions were apparently not affected by prefers and methods ceasing to function, but now there are 12780 to help prevent a similar error. Since you just went through it, I&apos;m leaving the older version of the patch up so you can easily see the difference to what I&apos;ve added. &lt;/p&gt;</comment>
                    <comment id="29448" author="richhickey" created="Sat, 15 Sep 2012 09:05:46 -0500"  >&lt;p&gt;&lt;a href=&quot;https://github.com/clojure/clojure/commit/83ebf814d5d6663c49c1b2d0d076b57638bff673&quot;&gt;https://github.com/clojure/clojure/commit/83ebf814d5d6663c49c1b2d0d076b57638bff673&lt;/a&gt; should fix these issues. The patch here was too much of a change to properly vet.&lt;/p&gt;

&lt;p&gt;If you could though, I&apos;d appreciate a patch with just the multimethod tests.&lt;/p&gt;</comment>
                    <comment id="29449" author="jafingerhut" created="Sat, 15 Sep 2012 10:59:01 -0500"  >&lt;p&gt;Patch clj-988-tests-only-patch-v1.txt dated Sep 15 2012 is a subset of David Santiago&apos;s &lt;br/&gt;
patch issue988-lockless-multifn+tests-120817.diff dated Aug 17 2012.  It includes only the tests from that patch.  Applies cleanly and passes tests with latest master after Rich&apos;s read/write lock change for multimethods was committed.&lt;/p&gt;</comment>
                    <comment id="29463" author="richhickey" created="Mon, 17 Sep 2012 09:20:43 -0500"  >&lt;p&gt;tests-only patch ok&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11498" name="clj-988-tests-only-patch-v1.txt" size="3569" author="jafingerhut" created="Sat, 15 Sep 2012 10:59:01 -0500" />
                    <attachment id="11441" name="issue988-lockless-multifn+tests-120817.diff" size="15875" author="santiago" created="Fri, 17 Aug 2012 19:44:54 -0500" />
                    <attachment id="11321" name="issue988-lockless-multifn+tests.diff" size="12422" author="santiago" created="Fri, 15 Jun 2012 04:45:58 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                <customfield id="customfield_10002" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Approval</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10007">Ok</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                    <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10002">Code and Test</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[CLJ-910] [Patch] Allow for type-hinting the method receiver in memfn</title>
                <link>http://dev.clojure.org/jira/browse/CLJ-910</link>
                <project id="10010" key="CLJ">Clojure</project>
                        <description>&lt;p&gt;The attached patch copies metadata given to the method name symbol of memfn to the method receiver in the expansion.  That way, memfn is able to be used even for type-hinted calls resulting in a big performance win.&lt;/p&gt;

&lt;div class=&quot;preformatted panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;preformattedContent panelContent&quot;&gt;
&lt;pre&gt;user&amp;gt; (time (dotimes [i 1000000] ((memfn intValue) 1)))
Reflection warning, NO_SOURCE_FILE:1 - call to intValue can&apos;t be resolved.
&quot;Elapsed time: 2579.229115 msecs&quot;
nil
user&amp;gt; (time (dotimes [i 1000000] ((memfn ^Number intValue) 1)))
&quot;Elapsed time: 12.015235 msecs&quot;
nil
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;</description>
                <environment></environment>
            <key id="15115">CLJ-910</key>
            <summary>[Patch] Allow for type-hinting the method receiver in memfn</summary>
                <type id="4" iconUrl="http://dev.clojure.org/jira/images/icons/improvement.gif">Enhancement</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="6" iconUrl="http://dev.clojure.org/jira/images/icons/status_closed.gif">Closed</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="-1">Unassigned</assignee>
                                <reporter username="tsdh">Tassilo Horn</reporter>
                        <labels>
                        <label>performance</label>
                        <label>reflection</label>
                    </labels>
                <created>Fri, 13 Jan 2012 06:02:28 -0600</created>
                <updated>Sat, 1 Sep 2012 08:37:48 -0500</updated>
                    <resolved>Sat, 1 Sep 2012 08:37:48 -0500</resolved>
                                            <fixVersion>Release 1.5</fixVersion>
                                        <due></due>
                    <votes>1</votes>
                        <watches>2</watches>
                        <comments>
                    <comment id="27912" author="tsdh" created="Thu, 8 Mar 2012 03:56:20 -0600"  >&lt;p&gt;Updated patch.&lt;/p&gt;</comment>
                    <comment id="29245" author="aaron" created="Tue, 21 Aug 2012 18:06:49 -0500"  >&lt;p&gt;Applies properly against d4170e65d001c8c2976f1bd7159484056b9a9d6d. Things look good to me. &lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="10985" name="0001-Make-memfn-allow-for-type-hinting-the-method-receive.patch" size="1168" author="tsdh" created="Thu, 8 Mar 2012 03:56:20 -0600" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                <customfield id="customfield_10002" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Approval</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10007">Ok</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                    <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10001">Code</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[CCACHE-27] Missing LU and LRU is linear complexity - performance</title>
                <link>http://dev.clojure.org/jira/browse/CCACHE-27</link>
                <project id="10171" key="CCACHE">core.cache</project>
                        <description>&lt;p&gt;Profiling some code with YourKit showed a hotspot in cache statistics on (miss) for LU and LRU caches.&lt;/p&gt;

&lt;p&gt;Basically two issues:  (count (keys {....})) is a count of a seq, not efficient. Replaced with a count of the map.&lt;/p&gt;

&lt;p&gt;Secondly, (apply f anything) is slow. Profiler showed that the (apply min-key) was really slow.  This is mitigated by using a c.d.priority-map instead.   On a priority-map, (ffirst {}) or (first (peek {}).&lt;/p&gt;

&lt;p&gt;Also inverted the logic for threshold comparison.  Since there is a (build-leastness-queue) that populates statistics, should caches should favor codepaths for the state of being full all the time?&lt;/p&gt;</description>
                <environment>Mac Oracle JDK, Linux OpenJDK</environment>
            <key id="15672">CCACHE-27</key>
            <summary>Missing LU and LRU is linear complexity - performance</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="3" iconUrl="http://dev.clojure.org/jira/images/icons/priority_major.gif">Major</priority>
                    <status id="1" iconUrl="http://dev.clojure.org/jira/images/icons/status_open.gif">Open</status>
                    <resolution id="-1">Unresolved</resolution>
                                <assignee username="fogus">Fogus</assignee>
                                <reporter username="gshayban@gmail.com">Ghadi Shayban</reporter>
                        <labels>
                        <label>enhancement</label>
                        <label>performance</label>
                    </labels>
                <created>Tue, 4 Sep 2012 11:26:54 -0500</created>
                <updated>Wed, 12 Sep 2012 09:27:56 -0500</updated>
                                                                            <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="29391" author="gshayban" created="Thu, 6 Sep 2012 22:49:14 -0500"  >&lt;p&gt;I take back the part about (apply) being slow.  I think it&apos;s more the fact that it&apos;s doing a linear scan on keys every single time.&lt;/p&gt;

&lt;p&gt;(apply) does show up a lot in a profiler, but I haven&apos;t figured out why.  (apply + (range big)) seems to even be slightly faster than (reduce +) on the same range.&lt;/p&gt;
</comment>
                    <comment id="29426" author="gshayban" created="Wed, 12 Sep 2012 09:27:56 -0500"  >&lt;p&gt;Patch in proper format&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="11492" name="priority-map-fixed.patch" size="5639" author="gshayban" created="Wed, 12 Sep 2012 09:27:56 -0500" />
                    <attachment id="11475" name="priority-map.patch" size="4909" author="gshayban@gmail.com" created="Tue, 4 Sep 2012 11:26:54 -0500" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10001">Code</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>

<item>
            <title>[ALGOM-4] algo.monad state-m fetch-val bug and efficiency issue</title>
                <link>http://dev.clojure.org/jira/browse/ALGOM-4</link>
                <project id="10072" key="ALGOM">algo.monads</project>
                        <description>&lt;p&gt;;; the bug&lt;/p&gt;

&lt;p&gt;(defn fetch-val&lt;br/&gt;
  &quot;Return a state-monad function that assumes the state to be a map and&lt;br/&gt;
   returns the value corresponding to the given key. The state is not modified.&quot;&lt;br/&gt;
  &lt;span class=&quot;error&quot;&gt;&amp;#91;key&amp;#93;&lt;/span&gt;&lt;br/&gt;
  (domonad state-m&lt;br/&gt;
    &lt;span class=&quot;error&quot;&gt;&amp;#91;s (fetch-state)&amp;#93;&lt;/span&gt;&lt;br/&gt;
    (key s))) ;; does not work for integer or string keys&lt;/p&gt;

&lt;p&gt;;; I propose replacing it with (get s key)&lt;/p&gt;

&lt;p&gt;;; the efficiency issue :&lt;br/&gt;
;;&lt;br/&gt;
;; domonad with monad parameter binds all the monad functions, &lt;br/&gt;
;; looking these up in the state-m map on each call&lt;br/&gt;
;;&lt;br/&gt;
;; solution :&lt;/p&gt;

&lt;p&gt;(defn fetch-val&lt;br/&gt;
  &quot;Return a state-monad function that assumes the state to be a map and&lt;br/&gt;
   returns the value corresponding to the given key. The state is not modified.&quot;&lt;br/&gt;
  &lt;span class=&quot;error&quot;&gt;&amp;#91;key&amp;#93;&lt;/span&gt;&lt;br/&gt;
  (fn &lt;span class=&quot;error&quot;&gt;&amp;#91;s&amp;#93;&lt;/span&gt;&lt;br/&gt;
    &lt;span class=&quot;error&quot;&gt;&amp;#91;(get s key) s&amp;#93;&lt;/span&gt;))&lt;/p&gt;


&lt;p&gt;;; - we avoid the monad map lookups&lt;br/&gt;
;; - coding style brought up to par with the rest of state-m functions&lt;/p&gt;</description>
                <environment>irrelevant</environment>
            <key id="15686">ALGOM-4</key>
            <summary>algo.monad state-m fetch-val bug and efficiency issue</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="1" iconUrl="http://dev.clojure.org/jira/images/icons/status_open.gif">Open</status>
                    <resolution id="-1">Unresolved</resolution>
                                <assignee username="khinsen">Konrad Hinsen</assignee>
                                <reporter username="cark">Sacha De Vos</reporter>
                        <labels>
                        <label>bug</label>
                        <label>performance</label>
                    </labels>
                <created>Sat, 8 Sep 2012 20:47:32 -0500</created>
                <updated>Sat, 8 Sep 2012 20:47:32 -0500</updated>
                                                                            <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                                <attachments>
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                                                                                            </customfields>
    </item>
</channel>
</rss>