RFR: [6904367]: (coll) IdentityHashMap is resized before exceeding the expected maximum size

Ivan Gerasimov ivan.gerasimov at oracle.com
Wed Jul 9 00:05:47 UTC 2014


Ah, yes, sure.
I overlooked the reference to the table :-)

On 09.07.2014 2:42, Peter Levart wrote:
>
> On 07/09/2014 12:06 AM, Ivan Gerasimov wrote:
>>
>> On 09.07.2014 1:44, Peter Levart wrote:
>>>
>>> On 07/08/2014 11:39 PM, Ivan Gerasimov wrote:
>>>> Might be worth to add modCount++ before this line:
>>>>
>>>>   487         table = newTable;
>>>>   488         return true;
>>>>
>>> Not quite, I think. The map has just been resized, but it's contents 
>>> has not changed yet logically.
>>>
>> IdentityHashMapIterator's methods assume that if modCount didn't 
>> change, then the indices calculated earlier remain valid, and this is 
>> wrong in the case of resize.
>>
>> Sincerely yours,
>> Ivan
>
> IdentityHashMapIterator:
>
>   713     private abstract class IdentityHashMapIterator<T> implements Iterator<T> {
>   714         int index = (size != 0 ? 0 : table.length); // current slot.
>   715         int expectedModCount = modCount; // to support fast-fail
>   716         int lastReturnedIndex = -1;      // to allow remove()
>   717         boolean indexValid; // To avoid unnecessary next computation
>   718         Object[] traversalTable = table; // reference to main table or copy
>
> ...takes a snap-shot of reference to current table when created, so 
> indexes would still be valid ...
>
> ...but resize() also clears old table as it copies elements to new table:
>
>   478                 oldTable[j] = null;
>   479                 oldTable[j+1] = null;
>
> So it would appear that modCount should be incremented even before the 
> copying loop.
>
> But as it seems, no user call-backs are possible during resize() in 
> same thread and normal writes are not ordered anyway for other threads 
> and after each "successful" resize() at least one new key will be 
> added to the map so modCount will be incremented before control is 
> returned to user code anyway.
>
> Regards, Peter
>
>>
>>
>>> Regards, Peter
>>>
>>>> On 09.07.2014 0:07, Martin Buchholz wrote:
>>>>> I updated my webrev and it is again "feature-complete".
>>>>> http://cr.openjdk.java.net/~martin/webrevs/openjdk9/IdentityHashMap-capacity/ 
>>>>> <http://cr.openjdk.java.net/%7Emartin/webrevs/openjdk9/IdentityHashMap-capacity/>
>>>>> (old webrev at
>>>>> http://cr.openjdk.java.net/~martin/webrevs/openjdk9/IdentityHashMap-capacity.0/ 
>>>>> <http://cr.openjdk.java.net/%7Emartin/webrevs/openjdk9/IdentityHashMap-capacity.0/>
>>>>> )
>>>>>
>>>>> This incorporates Peter's idea of making resize return a boolean, 
>>>>> keeps the map unchanged if resize throws, moves the check for 
>>>>> capacity exceeded into resize, and minimizes bytecode in put(). 
>>>>>  I'm happy with this (except for degraded behavior near MAX_CAPACITY).
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> On Tue, Jul 8, 2014 at 8:06 AM, Peter Levart 
>>>>> <peter.levart at gmail.com <mailto:peter.levart at gmail.com>> wrote:
>>>>>
>>>>>     On 07/08/2014 03:00 PM, Ivan Gerasimov wrote:
>>>>>
>>>>>             I took your latest version of the patch and modified
>>>>>             it a little:
>>>>>
>>>>>             http://cr.openjdk.java.net/~plevart/jdk9-dev/IdentityHashMap/webrev.01/
>>>>>             <http://cr.openjdk.java.net/%7Eplevart/jdk9-dev/IdentityHashMap/webrev.01/>
>>>>>
>>>>>
>>>>>         But isn't it post-insert-resize vs pre-insert-resize
>>>>>         problem Doug mentioned above?
>>>>>         I've tested a similar fix and it showed slow down of the
>>>>>         put() operation.
>>>>>
>>>>>     Hi Ivan,
>>>>>
>>>>>     Might be that it has to do with # of bytecodes in the method
>>>>>     and in-lining threshold. I modified it once more, to make
>>>>>     put() method as short as possible:
>>>>>
>>>>>     http://cr.openjdk.java.net/~plevart/jdk9-dev/IdentityHashMap/webrev.05/
>>>>>     <http://cr.openjdk.java.net/%7Eplevart/jdk9-dev/IdentityHashMap/webrev.05/>
>>>>>
>>>>>     With this, I ran the following JMH benchmark:
>>>>>
>>>>>     @State(Scope.Thread)
>>>>>     public class IHMBench {
>>>>>
>>>>>         Map<Object, Object> map = new IdentityHashMap<Object,
>>>>>     Object>();
>>>>>
>>>>>         @Benchmark
>>>>>         public void putNewObject(Blackhole bh) {
>>>>>             Object o = new Object();
>>>>>             bh.consume(map.put(o, o));
>>>>>             if (map.size() > 100000) {
>>>>>                 map = new IdentityHashMap<Object, Object>();
>>>>>             }
>>>>>         }
>>>>>     }
>>>>>
>>>>>     I get the following results on my i7/Linux using:
>>>>>
>>>>>     java -Xmx4G -Xms4G -XX:+UseParallelGC -jar benchmarks.jar -f 0
>>>>>     -i 10 -wi 8 -gc 1 -t 1
>>>>>
>>>>>     Original:
>>>>>
>>>>>     Benchmark                     Mode   Samples  Score  Score
>>>>>     error    Units
>>>>>     j.t.IHMBench.putNewObject    thrpt        10 13088296.198
>>>>>     <tel:13088296.198> 403446.449    ops/s
>>>>>
>>>>>     Patched:
>>>>>
>>>>>     Benchmark                     Mode   Samples  Score  Score
>>>>>     error    Units
>>>>>     j.t.IHMBench.putNewObject    thrpt        10 13180594.537
>>>>>     282047.154    ops/s
>>>>>
>>>>>
>>>>>     Can you run your test with webrev.05 and see what you get ?
>>>>>
>>>>>     Regards, Peter
>>>>>
>>>>>
>>>>
>>>
>>
>




More information about the core-libs-dev mailing list