<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recent changes to Comparative Performance</title><link>https://sourceforge.net/p/javimmutablecollections/wiki/Comparative%2520Performance/</link><description>Recent changes to Comparative Performance</description><atom:link href="https://sourceforge.net/p/javimmutablecollections/wiki/Comparative%20Performance/feed" rel="self"/><language>en</language><lastBuildDate>Sun, 02 Mar 2014 22:55:15 -0000</lastBuildDate><atom:link href="https://sourceforge.net/p/javimmutablecollections/wiki/Comparative%20Performance/feed" rel="self" type="application/rss+xml"/><item><title>Comparative Performance modified by Brian Burton</title><link>https://sourceforge.net/p/javimmutablecollections/wiki/Comparative%2520Performance/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;Let's start off by stating the obvious.  Mutable collections are faster than immutable ones.  That's a basic fact of life.  Consider a hash table for example.  A mutable hash map can simply replace a value in an internal array in response to a put() call while an immutable one has to create a number of new objects to build a new version of the map reflecting the change.  So, yes, mutable collections are faster.&lt;/p&gt;
&lt;p&gt;The real questions are: how much faster are mutable collections and will you really notice the difference.  Based on benchmark runs a JImmutableHashMap is about 2-3 times slower than a HashMap but is about 1.5x faster than a TreeMap.  Unless your application spends most of its time CPU bound updating collections you probably won't notice much of a difference using an immutable collection.&lt;/p&gt;
&lt;p&gt;Here is a sample run using the org.javimmutable.collections.hash.TimingComparison benchmark program that comes with javimmutable-collections.  The program uses a random number generator to create sequences of puts, gets, and deletes.  The program can be tweaked in a variety of ways but the primary setting is the number of loops (operations) to perform.  The program repeats the same series of random operations on a TreeMap, a JImmutables.map(), and a JImmutables.array().  These test runs were performed on a MacBook Pro with heap settings -Xms384m -Xmx512m.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;subset&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;TreeMap&lt;/span&gt; &lt;span class="n"&gt;averages&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;many&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;
&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;74847&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24925&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;150228&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;74814&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;74847&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24925&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;150228&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;74814&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;74847&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24925&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;150228&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;74814&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;75044&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24846&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;150110&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;75013&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;75044&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24846&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;150110&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;75013&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;75044&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24846&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;150110&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;75013&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;75093&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24995&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;149912&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;75050&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;81&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;75093&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24995&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;149912&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;75050&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;75093&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;24995&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;149912&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;75050&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;

&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;79.6&lt;/span&gt;  &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;55.6&lt;/span&gt;  &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see the TreeMap completed 250,000 operations in about 80 milliseconds on average winding up with a map containing 75k elements.  JImmutables.map() completed the same operations in 55 ms and JImmutables.array() in 50 ms.  Using the same program a HashMap averages around 21 ms.&lt;/p&gt;
&lt;p&gt;Increasing the number of operations to 500k (double) the average times are 52 ms for HashMap, 200 ms for TreeMap, 128 ms for JImmutables.map() and 118 ms for JImmutables.array().  The resulting collections contain approximately 150k elements.  For the same run JImmutables.sortedMap() takes approximately 375 ms.&lt;/p&gt;
&lt;p&gt;Cranking the number of operations up some more (1.5 million) we get these times: HashMap 262 ms, JImmutables.map() 530 ms, JImmutables.array() 476 ms.  The final collection contained 448k elements.  For these I used heap settings -Xms384m -Xmx768m.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;subset&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="n"&gt;million&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt; &lt;span class="n"&gt;averages&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;many&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;
&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;449849&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149741&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900410&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448534&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;269&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;449849&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149741&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900410&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448534&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;521&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;449849&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149741&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900410&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448534&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;466&lt;/span&gt;

&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;449889&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149428&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900683&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448581&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;267&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;449889&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149428&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900683&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448581&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;517&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;449889&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149428&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900683&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448581&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;

&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;450106&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149794&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900100&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448768&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;265&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;450106&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149794&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900100&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448768&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;522&lt;/span&gt;
&lt;span class="n"&gt;jimm&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="n"&gt;adds&lt;/span&gt; &lt;span class="mi"&gt;450106&lt;/span&gt; &lt;span class="n"&gt;removes&lt;/span&gt; &lt;span class="mi"&gt;149794&lt;/span&gt; &lt;span class="n"&gt;gets&lt;/span&gt; &lt;span class="mi"&gt;900100&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;448768&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;

&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;260.0&lt;/span&gt;  &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;505.9&lt;/span&gt;  &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;456.2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From these test runs with maps it's clear that java.util.HashMap is wicked fast as would be expected but the fully immutable alternatives are still within a factor of 2-3 of it even for collections as large as 448k elements.  Saying something is 2-3 times slower than something else sounds bad but keep in mind that this test performed one and a half million operations, wound up with a collection of 448k elements, and the difference in execution time between the best mutable and immutable versions was only about 245 milliseconds!  In exchange for that quarter second your program would have all the benefits of immutability including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;elimination of the need for locking or any potential for lock contention impacting performance&lt;/li&gt;
&lt;li&gt;elimination of the need for defensive copying or any potential for different parts of the program using the same map changing the data unexpectedly and breaking other parts of the program&lt;/li&gt;
&lt;li&gt;improvements in maintainability from ability to reason about how and when collections might change&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your program is CPU bound and works with enormous data structures it might require the use of mutable data structures.  However for most programs switching to fully immutable data structures would have no noticeable effect on performance but would provide major benefits in the design and maintainability of the system.&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Brian Burton</dc:creator><pubDate>Sun, 02 Mar 2014 22:55:15 -0000</pubDate><guid>https://sourceforge.net78feaf3802d61b7e1de81cef24384d06634bea08</guid></item></channel></rss>