Skip to content

Commit

Permalink
Merge pull request #8356 from tinwelint/3.2-more-cache-friendly-trave…
Browse files Browse the repository at this point in the history
…rsal

More cache friendly traversal iterators
  • Loading branch information
MishaDemianenko committed Nov 22, 2016
2 parents 0475711 + 4507e66 commit 2546639
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void wikipediaExample() throws Exception
graph.makeEdge( "c", "end", "length", 4L );
graph.makeEdge( "start", "d", "length", (short)2 );
graph.makeEdge( "d", "e", "length", (byte)3 );
graph.makeEdge( "e", "end", "length", (int)2 );
graph.makeEdge( "e", "end", "length", 2 );

// WHEN
WeightedPath path = finder.findSinglePath( start, end );
Expand Down Expand Up @@ -222,7 +222,7 @@ public PathExpander<Double> reverse()
WeightedPath path = traversalFinder.findSinglePath( nodeA, nodeC );
assertEquals( (Double) 5.0D, (Double) path.weight() );
assertPathDef( path, "A", "B", "C" );
assertEquals( MapUtil.<Node,Double>genericMap( nodeA, 0D, nodeB, 2D, nodeC, 5D ), seenBranchStates );
assertEquals( MapUtil.<Node,Double>genericMap( nodeA, 0D, nodeB, 2D ), seenBranchStates );
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
import org.neo4j.storageengine.api.RelationshipItem;

/**
* Convert a {@link RelationshipItem} cursor into a {@link RelationshipIterator} that implements {@link Resource).
* Convert a {@link RelationshipItem} cursor into a {@link RelationshipIterator} that implements {@link Resource}.
*/
public class CursorRelationshipIterator implements RelationshipIterator, Resource
{
private Cursor<RelationshipItem> cursor;
private boolean hasDeterminedNext;
private boolean hasNext;

private long id;
Expand All @@ -42,7 +43,6 @@ public class CursorRelationshipIterator implements RelationshipIterator, Resourc
public CursorRelationshipIterator( Cursor<RelationshipItem> resourceCursor )
{
cursor = resourceCursor;
hasNext = nextCursor();
}

private boolean nextCursor()
Expand All @@ -62,6 +62,11 @@ private boolean nextCursor()
@Override
public boolean hasNext()
{
if ( !hasDeterminedNext )
{
hasNext = nextCursor();
hasDeterminedNext = true;
}
return hasNext;
}

Expand All @@ -83,7 +88,7 @@ public long next()
}
finally
{
hasNext = nextCursor();
hasDeterminedNext = false;
}
}
throw new NoSuchElementException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,19 @@ protected void evaluate( TraversalContext context )
setEvaluation( context.evaluate( this, null ) );
}

@Override
public void initialize( final PathExpander expander, TraversalContext metadata )
{
evaluate( metadata );
expandRelationships( expander );
}

@Override
public TraversalBranch next( PathExpander expander, TraversalContext context )
{
if ( relationships == null )
{
expandRelationships( expander );
}
while ( relationships.hasNext() )
{
Relationship relationship = relationships.next();
Expand Down Expand Up @@ -172,16 +177,19 @@ public void prune()
relationships = PRUNED_ITERATOR;
}

@Override
public int length()
{
return depthAndEvaluationBits&0x3FFFFFFF;
}

@Override
public TraversalBranch parent()
{
return this.parent;
}

@Override
public int expanded()
{
return expandedCount;
Expand All @@ -205,6 +213,7 @@ public void evaluation( Evaluation eval )
setEvaluation( Evaluation.of( includes() & eval.includes(), continues() & eval.continues() ) );
}

@Override
public Node startNode()
{
return findStartBranch().endNode();
Expand All @@ -220,16 +229,19 @@ private TraversalBranch findStartBranch()
return branch;
}

@Override
public Node endNode()
{
return source;
}

@Override
public Relationship lastRelationship()
{
return howIGotHere;
}

@Override
public Iterable<Relationship> relationships()
{
LinkedList<Relationship> relationships = new LinkedList<Relationship>();
Expand Down Expand Up @@ -271,6 +283,7 @@ protected Relationship fetchNextOrNull()
};
}

@Override
public Iterable<Node> nodes()
{
LinkedList<Node> nodes = new LinkedList<Node>();
Expand Down Expand Up @@ -313,6 +326,7 @@ protected Node fetchNextOrNull()
};
}

@Override
public Iterator<PropertyContainer> iterator()
{
LinkedList<PropertyContainer> entities = new LinkedList<PropertyContainer>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2002-2016 "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.kernel.impl.api.store;

import org.junit.Test;

import java.util.function.Supplier;

import org.neo4j.cursor.Cursor;
import org.neo4j.kernel.impl.util.collection.ContinuableArrayCursor;
import org.neo4j.storageengine.api.RelationshipItem;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;

public class CursorRelationshipIteratorTest
{
@Test
public void shouldLazilyGoToNext() throws Exception
{
// GIVEN
Cursor<RelationshipItem> cursor = spy( new ContinuableArrayCursor<>( new Supplier<RelationshipItem[]>()
{
private boolean first = true;

@Override
public RelationshipItem[] get()
{
if ( first )
{
first = false;
return new RelationshipItem[] {mock( RelationshipItem.class ), mock( RelationshipItem.class )};
}
return null;
}
} ) );

try ( CursorRelationshipIterator iterator = new CursorRelationshipIterator( cursor ) )
{
verifyZeroInteractions( cursor );

// WHEN/THEN
assertTrue( iterator.hasNext() );
verify( cursor, times( 1 ) ).next();
iterator.next();
verify( cursor, times( 1 ) ).next();

assertTrue( iterator.hasNext() );
verify( cursor, times( 2 ) ).next();
iterator.next();
verify( cursor, times( 2 ) ).next();

assertFalse( iterator.hasNext() );
verify( cursor, times( 3 ) ).next();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2002-2016 "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.kernel.impl.traversal;

import org.junit.Test;

import java.util.Collections;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PathExpander;
import org.neo4j.graphdb.traversal.BranchState;
import org.neo4j.graphdb.traversal.TraversalBranch;
import org.neo4j.graphdb.traversal.TraversalContext;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import static org.neo4j.graphdb.traversal.Evaluation.INCLUDE_AND_CONTINUE;

public class TraversalBranchImplTest
{
@SuppressWarnings( "unchecked" )
@Test
public void shouldExpandOnFirstAccess() throws Exception
{
// GIVEN
TraversalBranch parent = mock( TraversalBranch.class );
Node source = mock( Node.class );
TraversalBranchImpl branch = new TraversalBranchImpl( parent, source );
@SuppressWarnings( "rawtypes" )
PathExpander expander = mock( PathExpander.class );
when( expander.expand( eq( branch ), any( BranchState.class ) ) ).thenReturn( Collections.emptySet() );
TraversalContext context = mock( TraversalContext.class );
when( context.evaluate( eq( branch ), any( BranchState.class ) ) ).thenReturn( INCLUDE_AND_CONTINUE );

// WHEN initializing
branch.initialize( expander, context );

// THEN the branch should not be expanded
verifyZeroInteractions( source );

// and WHEN actually traversing from it
branch.next( expander, context );

// THEN we should expand it
verify( expander ).expand( any( Path.class ), any( BranchState.class ) );
}
}

0 comments on commit 2546639

Please sign in to comment.