Skip to content

Commit

Permalink
Use specialised primitive long queue in FreeIdKeeper
Browse files Browse the repository at this point in the history
Use primitive long queue instead of Deque<Long> in FreeIdKeeper
to avoid conversion between primitive long and object wrapper
during id reuse.
  • Loading branch information
MishaDemianenko committed Dec 5, 2017
1 parent 4cae0cb commit bab91be
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 15 deletions.
Expand Up @@ -22,9 +22,8 @@
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;


import org.neo4j.collection.primitive.PrimitiveLongArrayQueue;
import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongCollections;
import org.neo4j.io.fs.StoreChannel; import org.neo4j.io.fs.StoreChannel;
import org.neo4j.kernel.impl.store.UnderlyingStorageException; import org.neo4j.kernel.impl.store.UnderlyingStorageException;
Expand All @@ -50,8 +49,8 @@ public class FreeIdKeeper implements Closeable
{ {
private static final int ID_ENTRY_SIZE = Long.BYTES; private static final int ID_ENTRY_SIZE = Long.BYTES;


private final Deque<Long> freeIds = new ArrayDeque<>(); private final PrimitiveLongArrayQueue freeIds = new PrimitiveLongArrayQueue();
private final Deque<Long> readFromDisk = new ArrayDeque<>(); private final PrimitiveLongArrayQueue readFromDisk = new PrimitiveLongArrayQueue();
private final StoreChannel channel; private final StoreChannel channel;
private final int batchSize; private final int batchSize;
private final boolean aggressiveMode; private final boolean aggressiveMode;
Expand Down Expand Up @@ -103,7 +102,7 @@ static long countFreeIds( StoreChannel channel ) throws IOException


public void freeId( long id ) public void freeId( long id )
{ {
freeIds.add( id ); freeIds.enqueue( id );
freeIdCount++; freeIdCount++;


if ( freeIds.size() >= batchSize ) if ( freeIds.size() >= batchSize )
Expand Down Expand Up @@ -133,7 +132,7 @@ public long getId()
long result; long result;
if ( freeIds.size() > 0 && aggressiveMode ) if ( freeIds.size() > 0 && aggressiveMode )
{ {
result = freeIds.removeFirst(); result = freeIds.dequeue();
freeIdCount--; freeIdCount--;
} }
else else
Expand All @@ -158,7 +157,7 @@ public long[] getIds( int numberOfIds )
int cursor = 0; int cursor = 0;
while ( (cursor < reusableIds) && !freeIds.isEmpty() ) while ( (cursor < reusableIds) && !freeIds.isEmpty() )
{ {
ids[cursor++] = freeIds.removeFirst(); ids[cursor++] = freeIds.dequeue();
} }
while ( cursor < reusableIds ) while ( cursor < reusableIds )
{ {
Expand All @@ -176,7 +175,7 @@ private long getIdFromDisk()
} }
if ( !readFromDisk.isEmpty() ) if ( !readFromDisk.isEmpty() )
{ {
return readFromDisk.removeFirst(); return readFromDisk.dequeue();
} }
else else
{ {
Expand Down Expand Up @@ -224,7 +223,7 @@ private void readIdBatch0() throws IOException
for ( int i = 0; i < idsRead; i++ ) for ( int i = 0; i < idsRead; i++ )
{ {
long id = readBuffer.getLong(); long id = readBuffer.getLong();
readFromDisk.add( id ); readFromDisk.enqueue( id );
} }
if ( aggressiveMode ) if ( aggressiveMode )
{ {
Expand Down Expand Up @@ -253,7 +252,7 @@ private long flushFreeIds0( ByteBuffer writeBuffer ) throws IOException
writeBuffer.clear(); writeBuffer.clear();
while ( !freeIds.isEmpty() ) while ( !freeIds.isEmpty() )
{ {
long id = freeIds.removeFirst(); long id = freeIds.dequeue();
if ( id == NO_RESULT ) if ( id == NO_RESULT )
{ {
continue; continue;
Expand Down
@@ -0,0 +1,165 @@
/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.collection.primitive;

import java.util.NoSuchElementException;

/**
* Simple array based FIFO queue for primitive longs.
* Newly enqueued element is added into the end of the queue, and dequeue will return
* element from the head of the queue. (See CLRS 10.1 for more detailed description)
*
* Queue capacity should always be power of two to be able to use
* '&' mask operation with {@link #values} length.
*/
public class PrimitiveLongArrayQueue implements PrimitiveLongCollection
{
private static final int DEFAULT_CAPACITY = 16;
private long[] values;
private int head;
private int tail;

public PrimitiveLongArrayQueue()
{
this( DEFAULT_CAPACITY );
}

PrimitiveLongArrayQueue( int capacity )
{
assert (capacity != 0) && ((capacity & (capacity - 1)) == 0) : "Capacity should be power of 2.";
initValues( capacity );
}

@Override
public <E extends Exception> void visitKeys( PrimitiveLongVisitor<E> visitor ) throws E
{
throw new UnsupportedOperationException();
}

@Override
public boolean isEmpty()
{
return head == tail;
}

@Override
public void clear()
{
initValues( DEFAULT_CAPACITY );
}

@Override
public int size()
{
return (tail - head) & (values.length - 1);
}

@Override
public void close()
{
values = PrimitiveLongCollections.EMPTY_LONG_ARRAY;
}

@Override
public PrimitiveLongIterator iterator()
{
return new PrimitiveLongArrayQueueIterator();
}

public long dequeue()
{
if ( isEmpty() )
{
throw new IllegalStateException( "Fail to poll first element. Queue is empty." );
}
long value = values[head];
head = (head + 1) & (values.length - 1);
return value;
}

public void enqueue( long value )
{
values[tail] = value;
tail = (tail + 1) & (values.length - 1);
if ( tail == head )
{
ensureCapacity();
}
}

public void addAll( PrimitiveLongArrayQueue otherQueue )
{
while ( !otherQueue.isEmpty() )
{
enqueue( otherQueue.dequeue() );
}
}

private void initValues( int capacity )
{
values = new long[capacity];
head = 0;
tail = 0;
}

private void ensureCapacity()
{
int newCapacity = values.length << 1;
if ( newCapacity < 0 )
{
throw new IllegalStateException( "Fail to increase capacity queue capacity." );
}
long[] newValues = new long[newCapacity];
int elementsFromHeadTillEnd = values.length - head;
System.arraycopy( values, head, newValues, 0, elementsFromHeadTillEnd );
System.arraycopy( values, 0, newValues, elementsFromHeadTillEnd, head );
tail = values.length;
head = 0;
values = newValues;
}

private class PrimitiveLongArrayQueueIterator implements PrimitiveLongIterator
{
private int position;

PrimitiveLongArrayQueueIterator()
{
this.position = head;
}

@Override
public boolean hasNext()
{
return position != tail;
}

@Override
public long next()
{
if ( hasNext() )
{
long value = values[position];
position = (position + 1) & (values.length - 1);
return value;
}
throw new NoSuchElementException();
}
}
}

0 comments on commit bab91be

Please sign in to comment.