├── results └── result-2015-09-07T1525.txt ├── src ├── main │ ├── java │ │ └── se │ │ │ └── simonevertsson │ │ │ ├── gpu │ │ │ ├── buffer │ │ │ │ ├── BufferContainer.java │ │ │ │ ├── DataBuffers.java │ │ │ │ ├── QueryBuffers.java │ │ │ │ └── BufferContainerGenerator.java │ │ │ ├── query │ │ │ │ ├── SpanningTree.java │ │ │ │ ├── QueryResult.java │ │ │ │ ├── QueryContext.java │ │ │ │ ├── relationship │ │ │ │ │ ├── join │ │ │ │ │ │ ├── SolutionValidator.java │ │ │ │ │ │ ├── SolutionRelationshipCombiner.java │ │ │ │ │ │ ├── SolutionCombinationCounter.java │ │ │ │ │ │ ├── PossibleSolutions.java │ │ │ │ │ │ ├── SolutionPruner.java │ │ │ │ │ │ ├── SolutionCombinationGenerator.java │ │ │ │ │ │ ├── SolutionInitializer.java │ │ │ │ │ │ └── CandidateRelationshipJoiner.java │ │ │ │ │ └── search │ │ │ │ │ │ ├── CandidateRelationshipFinder.java │ │ │ │ │ │ ├── CandidateRelationshipCounter.java │ │ │ │ │ │ ├── CandidateRelationshipSearcher.java │ │ │ │ │ │ └── CandidateRelationships.java │ │ │ │ ├── candidate │ │ │ │ │ ├── refinement │ │ │ │ │ │ ├── CandidateRefinement.java │ │ │ │ │ │ └── CandidateRefinery.java │ │ │ │ │ └── initialization │ │ │ │ │ │ ├── CandidateChecker.java │ │ │ │ │ │ ├── CandidateExplorer.java │ │ │ │ │ │ └── CandidateInitializer.java │ │ │ │ ├── QuerySolution.java │ │ │ │ ├── GpuQuery.java │ │ │ │ ├── SpanningTreeGenerator.java │ │ │ │ └── QueryUtils.java │ │ │ ├── dictionary │ │ │ │ ├── TypeDictionary.java │ │ │ │ ├── QueryIdDictionary.java │ │ │ │ └── LabelDictionary.java │ │ │ ├── kernel │ │ │ │ └── QueryKernels.java │ │ │ └── graph │ │ │ │ ├── GpuGraph.java │ │ │ │ └── GpuGraphConverter.java │ │ │ ├── runner │ │ │ ├── QueryLabel.java │ │ │ ├── QueryRelationship.java │ │ │ ├── AliasDictionary.java │ │ │ ├── CypherQueryRunner.java │ │ │ ├── QueryGraph.java │ │ │ ├── GpuQueryRunner.java │ │ │ └── QueryNode.java │ │ │ ├── Main.java │ │ │ ├── experiments │ │ │ ├── StatisticsUtils.java │ │ │ └── QueryGraphGenerator.java │ │ │ └── db │ │ │ └── DatabaseService.java │ └── opencl │ │ └── se │ │ └── simonevertsson │ │ ├── CountCandidateRelationships.cl │ │ ├── CheckCandidates.cl │ │ ├── FindCandidateRelationships.cl │ │ ├── ValidateSolutions.cl │ │ ├── CombineRelationships.cl │ │ ├── PruneSolutions.cl │ │ ├── CountSolutionCombinations.cl │ │ ├── RefineCandidates.cl │ │ ├── ExploreCandidates.cl │ │ └── GenerateSolutionCombinations.cl └── test │ └── java │ └── se │ └── simonevertsson │ ├── MockQuery.java │ ├── runner │ └── AliasDictionaryTest.java │ ├── experiments │ └── QueryGraphGeneratorTest.java │ ├── gpu │ ├── query │ │ ├── QueryUtilsTest.java │ │ ├── SpanningTreeGeneratorTest.java │ │ ├── candidate │ │ │ ├── initialization │ │ │ │ ├── CandidateCheckerTest.java │ │ │ │ └── CandidateExplorerTest.java │ │ │ └── refinement │ │ │ │ └── CandidateRefineryTest.java │ │ └── relationship │ │ │ └── search │ │ │ └── CandidateRelationshipCounterTest.java │ ├── dictionary │ │ └── LabelDictionaryTest.java │ └── graph │ │ └── GpuGraphConverterTest.java │ └── MockHelper.java ├── README.md ├── .gitignore └── pom.xml /results/result-2015-09-07T1525.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/buffer/BufferContainer.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.buffer; 2 | 3 | public class BufferContainer { 4 | public DataBuffers dataBuffers; 5 | public QueryBuffers queryBuffers; 6 | 7 | public BufferContainer(DataBuffers dataBuffers, QueryBuffers queryBuffers) { 8 | this.dataBuffers = dataBuffers; 9 | this.queryBuffers = queryBuffers; 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/buffer/DataBuffers.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.buffer; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | 5 | public class DataBuffers { 6 | public CLBuffer dataRelationshipIndicesBuffer; 7 | public CLBuffer dataLabelsBuffer; 8 | public CLBuffer dataLabelIndicesBuffer; 9 | public CLBuffer dataNodeRelationshipsBuffer; 10 | public CLBuffer dataRelationshipTypesBuffer; 11 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/buffer/QueryBuffers.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.buffer; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import org.bridj.Pointer; 5 | 6 | public class QueryBuffers { 7 | public CLBuffer candidateIndicatorsBuffer; 8 | public CLBuffer queryNodeLabelsBuffer; 9 | public CLBuffer queryNodeLabelIndicesBuffer; 10 | public CLBuffer queryNodeRelationshipsBuffer; 11 | public CLBuffer queryRelationshipIndicesBuffer; 12 | public Pointer candidateIndicatorsPointer; 13 | public CLBuffer queryRelationshipTypesBuffer; 14 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/runner/QueryLabel.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import org.neo4j.graphdb.Label; 4 | 5 | public class QueryLabel implements Label { 6 | private final String name; 7 | 8 | public QueryLabel(String name) { 9 | this.name = name; 10 | } 11 | 12 | public String name() { 13 | return this.name; 14 | } 15 | 16 | 17 | @Override 18 | public boolean equals(Object that) { 19 | return (that instanceof Label) && this.name().equals(((Label) that).name()); 20 | } 21 | 22 | 23 | @Override 24 | public int hashCode() { 25 | return this.name().hashCode(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/SpanningTree.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import org.neo4j.graphdb.Relationship; 5 | 6 | import java.util.List; 7 | 8 | public class SpanningTree { 9 | 10 | private List relationships; 11 | 12 | private List visitOrder; 13 | 14 | public SpanningTree(List spanningTreeRelationships, List visitOrder) { 15 | this.relationships = spanningTreeRelationships; 16 | this.visitOrder = visitOrder; 17 | } 18 | 19 | public List getRelationships() { 20 | return relationships; 21 | } 22 | 23 | public List getVisitOrder() { 24 | return visitOrder; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/MockQuery.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson; 2 | 3 | import se.simonevertsson.gpu.buffer.BufferContainer; 4 | import se.simonevertsson.gpu.query.QueryContext; 5 | import se.simonevertsson.gpu.kernel.QueryKernels; 6 | 7 | /** 8 | * Created by simon on 2015-06-23. 9 | */ 10 | public class MockQuery { 11 | 12 | public final QueryContext queryContext; 13 | public final QueryKernels queryKernels; 14 | public final BufferContainer bufferContainer; 15 | 16 | public MockQuery(QueryContext queryContext, QueryKernels queryKernels, BufferContainer bufferContainer) { 17 | this.queryContext = queryContext; 18 | this.queryKernels = queryKernels; 19 | this.bufferContainer = bufferContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/QueryResult.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import java.util.List; 4 | 5 | public class QueryResult { 6 | 7 | private final List querySolutions; 8 | private final long conversionExecutionTime; 9 | private final long queryExecutionTime; 10 | 11 | public QueryResult(List querySolutions, long conversionExecutionTime, long queryExecutionTime) { 12 | this.querySolutions = querySolutions; 13 | this.conversionExecutionTime = conversionExecutionTime; 14 | this.queryExecutionTime = queryExecutionTime; 15 | } 16 | 17 | 18 | public List getQuerySolutions() { 19 | return querySolutions; 20 | } 21 | 22 | public long getConversionExecutionTime() { 23 | return conversionExecutionTime; 24 | } 25 | 26 | public long getQueryExecutionTime() { 27 | return queryExecutionTime; 28 | } 29 | 30 | public long getTotalExecutionTime() { 31 | return conversionExecutionTime + queryExecutionTime; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/runner/AliasDictionaryTest.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import junit.framework.TestCase; 4 | import se.simonevertsson.runner.AliasDictionary; 5 | 6 | /** 7 | * Created by simon.evertsson on 2015-09-02. 8 | */ 9 | public class AliasDictionaryTest extends TestCase { 10 | 11 | public void testShouldReturnCorrectAliases() { 12 | // Given 13 | 14 | // When 15 | String A = AliasDictionary.createAlias(0); 16 | String B = AliasDictionary.createAlias(1); 17 | String Z = AliasDictionary.createAlias(25); 18 | String AA = AliasDictionary.createAlias(26); 19 | String AB = AliasDictionary.createAlias(27); 20 | String AZ = AliasDictionary.createAlias(51); 21 | String AAC = AliasDictionary.createAlias(54); 22 | 23 | // Then 24 | assertEquals("A", A); 25 | assertEquals("B", B); 26 | assertEquals("Z", Z); 27 | assertEquals("AA", AA); 28 | assertEquals("AB", AB); 29 | assertEquals("AZ", AZ); 30 | assertEquals("AAC", AAC); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/dictionary/TypeDictionary.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.dictionary; 2 | 3 | import java.util.HashMap; 4 | 5 | public class TypeDictionary { 6 | 7 | private HashMap typeToIds; 8 | 9 | private HashMap idToTypes; 10 | 11 | private int nextId; 12 | 13 | public TypeDictionary() { 14 | typeToIds = new HashMap<>(); 15 | idToTypes = new HashMap<>(); 16 | nextId = 1; 17 | } 18 | 19 | public int insertType(String type) { 20 | if (typeToIds.containsKey(type)) { 21 | return getIdForType(type); 22 | } else { 23 | int currentId = nextId; 24 | typeToIds.put(type, currentId); 25 | nextId++; 26 | return currentId; 27 | } 28 | } 29 | 30 | public int getIdForType(String type) { 31 | return typeToIds.get(type) != null ? typeToIds.get(type) : -1; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | StringBuilder builder = new StringBuilder(); 37 | for (String type : typeToIds.keySet()) { 38 | builder.append(type + ": " + typeToIds.get(type) + "\n"); 39 | } 40 | return builder.toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/QueryContext.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import se.simonevertsson.gpu.dictionary.LabelDictionary; 4 | import se.simonevertsson.gpu.dictionary.TypeDictionary; 5 | import se.simonevertsson.gpu.graph.GpuGraph; 6 | import se.simonevertsson.runner.QueryGraph; 7 | 8 | public class QueryContext { 9 | public LabelDictionary labelDictionary; 10 | public TypeDictionary typeDictionary; 11 | public QueryGraph queryGraph; 12 | public GpuGraph gpuData; 13 | public GpuGraph gpuQuery; 14 | public int queryNodeCount; 15 | public int dataNodeCount; 16 | public int queryRelationshipCount; 17 | 18 | public QueryContext(GpuGraph gpuData, GpuGraph gpuQuery, QueryGraph queryGraph, LabelDictionary labelDictionary, TypeDictionary typeDictionary) { 19 | this.gpuData = gpuData; 20 | this.gpuQuery = gpuQuery; 21 | this.queryGraph = queryGraph; 22 | this.dataNodeCount = gpuData.getRelationshipIndices().length - 1; 23 | this.queryNodeCount = gpuQuery.getRelationshipIndices().length - 1; 24 | this.queryRelationshipCount = queryGraph.relationships.size(); 25 | this.labelDictionary = labelDictionary; 26 | this.typeDictionary = typeDictionary; 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/experiments/QueryGraphGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.experiments; 2 | 3 | import junit.framework.TestCase; 4 | import org.neo4j.graphdb.Node; 5 | import se.simonevertsson.MockHelper; 6 | import se.simonevertsson.experiments.QueryGraphGenerator; 7 | import se.simonevertsson.runner.QueryGraph; 8 | 9 | import java.util.ArrayList; 10 | 11 | /** 12 | * Created by simon.evertsson on 2015-07-14. 13 | */ 14 | public class QueryGraphGeneratorTest extends TestCase { 15 | 16 | ArrayList allNodes; 17 | 18 | 19 | @Override 20 | protected void setUp() throws Exception { 21 | super.setUp(); 22 | this.allNodes = new ArrayList<>(); 23 | this.allNodes = MockHelper.generateMockQueryGraph().nodes; 24 | } 25 | 26 | public void testReturnsAValidQueryGraph() { 27 | // Arrange 28 | QueryGraphGenerator queryGraphGenerator = new QueryGraphGenerator(this.allNodes, 4, 3); 29 | queryGraphGenerator.setMinimumQueryGraphAmount(1); 30 | 31 | // Act 32 | ArrayList result = queryGraphGenerator.generate(1); 33 | 34 | // Assert 35 | assertNotNull(result); 36 | assertEquals(1, result.size()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/gpu/query/QueryUtilsTest.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import junit.framework.TestCase; 4 | import org.bridj.Pointer; 5 | import se.simonevertsson.gpu.query.QueryUtils; 6 | 7 | /** 8 | * Created by simon on 2015-06-01. 9 | */ 10 | public class QueryUtilsTest extends TestCase { 11 | 12 | public void testGatherCandidates() throws Exception { 13 | // Given 14 | 15 | int dataNodeCount = 4; 16 | 17 | boolean[] candidateIndicators = { 18 | false, true, false, false, 19 | false, false, false, false, 20 | false, false, false, false, 21 | false, false, false, false, 22 | }; 23 | 24 | int[] expectedResult = new int[] { 25 | 1 26 | }; 27 | 28 | int nodeId = 0; 29 | 30 | Pointer candidateIndicatorsPointer = Pointer.pointerToBooleans(candidateIndicators); 31 | 32 | // When 33 | int[] result = QueryUtils.gatherCandidateArray(candidateIndicatorsPointer, dataNodeCount, nodeId); 34 | 35 | 36 | 37 | 38 | // Then 39 | for(int i = 0; i < expectedResult.length; i++) { 40 | assertEquals(expectedResult[i], result[i]); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/gpu/query/SpanningTreeGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import junit.framework.TestCase; 4 | import se.simonevertsson.MockHelper; 5 | import se.simonevertsson.gpu.dictionary.LabelDictionary; 6 | import se.simonevertsson.gpu.dictionary.TypeDictionary; 7 | import se.simonevertsson.runner.QueryGraph; 8 | 9 | /** 10 | * Created by simon on 2015-05-12. 11 | */ 12 | public class SpanningTreeGeneratorTest extends TestCase { 13 | 14 | public void testGenerateQueryGraph() throws Exception { 15 | // Given 16 | QueryGraph queryGraph = MockHelper.generateBasicMockQueryGraph(); 17 | LabelDictionary labelDictionary = new LabelDictionary(); 18 | TypeDictionary typeDictionary = new TypeDictionary(); 19 | SpanningTreeGenerator spanningTreeGenerator = new SpanningTreeGenerator(); 20 | 21 | // When 22 | SpanningTree result = spanningTreeGenerator.generate(queryGraph); 23 | 24 | // Then 25 | assertEquals(3, result.getRelationships().size()); 26 | assertEquals(4, result.getVisitOrder().size()); 27 | assertEquals(1, (long)result.getVisitOrder().get(0).getId()); 28 | assertEquals(0, (long)result.getVisitOrder().get(1).getId()); 29 | assertEquals(2, (long)result.getVisitOrder().get(2).getId()); 30 | assertEquals(3, (long)result.getVisitOrder().get(3).getId()); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/CountCandidateRelationships.cl: -------------------------------------------------------------------------------- 1 | __kernel void count_candidate_relationships( 2 | int q_start_node, 3 | int q_end_node, 4 | int q_relationship_type, 5 | __global int* d_relationships, 6 | __global int* d_relationship_types, 7 | __global int* d_relationship_indices, 8 | 9 | __global int* candidate_relationship_counts, 10 | __global int* candidates_array, 11 | __global bool* candidate_indicators, 12 | int d_node_count) 13 | { 14 | int workset_index = get_global_id(0); 15 | candidate_relationship_counts[workset_index] = 0; 16 | 17 | int candidate_start_node = candidates_array[workset_index]; 18 | 19 | int c_relationship_start_index = d_relationship_indices[candidate_start_node]; 20 | int c_relationship_end_index = d_relationship_indices[candidate_start_node+1]; 21 | 22 | for(int i = c_relationship_start_index; i < c_relationship_end_index; i++) { 23 | int candidate_end_node = d_relationships[i]; 24 | int candidate_relationship_type = d_relationship_types[i]; 25 | bool valid_relationship = q_relationship_type != -1 ? candidate_relationship_type == q_relationship_type : true; 26 | if(valid_relationship && candidate_indicators[q_end_node*d_node_count + candidate_end_node]) { 27 | candidate_relationship_counts[workset_index] += candidate_indicators[q_end_node*d_node_count + candidate_end_node]; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/dictionary/QueryIdDictionary.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.dictionary; 2 | 3 | import java.util.*; 4 | 5 | public class QueryIdDictionary { 6 | 7 | private HashMap idToQueryId; 8 | 9 | private HashMap queryIdToIds; 10 | 11 | private int nextQueryId; 12 | 13 | public QueryIdDictionary() { 14 | this.idToQueryId = new HashMap(); 15 | this.queryIdToIds = new HashMap(); 16 | this.nextQueryId = 0; 17 | } 18 | 19 | public int add(long id) { 20 | if (this.idToQueryId.get(id) == null) { 21 | this.idToQueryId.put(id, this.nextQueryId); 22 | this.queryIdToIds.put(this.nextQueryId, id); 23 | this.nextQueryId++; 24 | } 25 | return this.idToQueryId.get(id); 26 | } 27 | 28 | public int getQueryId(long id) { 29 | return this.idToQueryId.get(id); 30 | } 31 | 32 | public long getId(int queryId) { 33 | return this.queryIdToIds.get(queryId); 34 | } 35 | 36 | 37 | @Override 38 | public String toString() { 39 | StringBuilder builder = new StringBuilder(); 40 | 41 | Set ids = this.idToQueryId.keySet(); 42 | List idList = new ArrayList(ids); 43 | Collections.sort(idList); 44 | 45 | for (long id : idList) { 46 | builder.append("Id " + id + ": " + this.idToQueryId.get(id)); 47 | builder.append('\n'); 48 | } 49 | 50 | return builder.toString(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # neo4j-gpu 2 | This repository contains the source code of my master's thesis where I've accelerated graph pattern queries by querying data stored in Neo4j using the GPU and comparing the performance to Cypher-queries in Neo4j. 3 | 4 | # Thesis report 5 | The published thesis can be found [here](http://uu.diva-portal.org/smash/record.jsf?pid=diva2%3A1056415&dswid=-7116#sthash.K6R0JhDe.dpbs) and its full-text can be read [here.](http://uu.diva-portal.org/smash/get/diva2:1056415/FULLTEXT01.pdf) 6 | 7 | ## Abstract 8 | Over the last decade the popularity of utilizing the parallel nature of the graphical processing unit in general purpose problems has grown a lot. Today GPUs are used in many different fields where one of them is the acceleration of database systems. Graph databases are a kind of database systems that have gained popularity in recent years. Such databases excel especially for data which is highly interconnected. Querying a graph database often requires finding subgraphs which structurally matches a query graph, i.e. isomorphic subgraphs. In this thesis a method for performing subgraph isomorphism queries named GPUGDA is proposed, extending previous work of GPU-accelerating subgraph isomorphism queries. The query performance of GPUGDA was evaluated and compared to the performance of storing the same graph in Neo4j and making queries in Cypher, the query language of Neo4j. The results show large speedups of up to 470x when the query graph is dense whilst performing slightly worse than Neo4j for sparse query graphs in larger databases. 9 | -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/CheckCandidates.cl: -------------------------------------------------------------------------------- 1 | __kernel void check_candidates( 2 | __global int* q_node_labels, 3 | int q_node, 4 | int q_node_label_start_index, 5 | int q_node_label_end_index, 6 | int q_node_degree, 7 | 8 | __global int* d_label_indices, 9 | __global int* d_labels, 10 | __global int* d_relationships, 11 | __global int* d_relationship_indices, 12 | 13 | __global bool* candidate_indicators, 14 | int data_node_count) 15 | { 16 | int i = get_global_id(0); 17 | 18 | int d_start_label_index = d_label_indices[i]; 19 | int d_end_label_index = d_label_indices[i+1]; 20 | bool labels_match = true; 21 | 22 | if(q_node_labels[q_node_label_start_index] != -1) { 23 | for(int j = q_node_label_start_index; j < q_node_label_end_index; j++) { 24 | int q_node_label = q_node_labels[j]; 25 | bool label_exists = false; 26 | for(int k = d_start_label_index; k < d_end_label_index; k++) { 27 | if(d_labels[k] == q_node_label) { 28 | label_exists = true; 29 | break; 30 | } 31 | } 32 | if(!label_exists) { 33 | labels_match = false; 34 | break; 35 | } 36 | } 37 | } 38 | 39 | int d_node_degree = d_relationships[d_relationship_indices[i]] != -1 ? (d_relationship_indices[i+1] - d_relationship_indices[i]) : 0; 40 | 41 | candidate_indicators[ q_node*data_node_count + i ] = (labels_match && q_node_degree <= d_node_degree); 42 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Intellij ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 5 | 6 | *.iml 7 | 8 | ## Directory-based project format: 9 | .idea/ 10 | # if you remove the above rule, at least ignore the following: 11 | 12 | # User-specific stuff: 13 | # .idea/workspace.xml 14 | # .idea/tasks.xml 15 | # .idea/dictionaries 16 | 17 | # Sensitive or high-churn files: 18 | # .idea/dataSources.ids 19 | # .idea/dataSources.xml 20 | # .idea/sqlDataSources.xml 21 | # .idea/dynamic.xml 22 | # .idea/uiDesigner.xml 23 | 24 | # Gradle: 25 | # .idea/gradle.xml 26 | # .idea/libraries 27 | 28 | # Mongo Explorer plugin: 29 | # .idea/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.ipr 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | /out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | 51 | 52 | ### Java ### 53 | *.class 54 | 55 | # Mobile Tools for Java (J2ME) 56 | .mtj.tmp/ 57 | 58 | # Package Files # 59 | *.jar 60 | *.war 61 | *.ear 62 | 63 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 64 | hs_err_pid* 65 | 66 | 67 | ### Maven ### 68 | target/ 69 | pom.xml.tag 70 | pom.xml.releaseBackup 71 | pom.xml.versionsBackup 72 | pom.xml.next 73 | release.properties 74 | dependency-reduced-pom.xml 75 | buildNumber.properties 76 | -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/gpu/dictionary/LabelDictionaryTest.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.dictionary; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class LabelDictionaryTest extends TestCase { 6 | 7 | public void testInsertLabelShouldNotIncreaseIdForSameLabel() throws Exception { 8 | // Given 9 | LabelDictionary labelDictionary = new LabelDictionary(); 10 | 11 | // When 12 | int firstResult = labelDictionary.insertLabel("TEST_LABEL"); 13 | int secondResult = labelDictionary.insertLabel("TEST_LABEL"); 14 | 15 | // Then 16 | assertEquals(firstResult, secondResult); 17 | } 18 | 19 | public void testInsertLabelShouldIncreaseIdForDifferentLabels() throws Exception { 20 | // Given 21 | LabelDictionary labelDictionary = new LabelDictionary(); 22 | 23 | // When 24 | int firstResult = labelDictionary.insertLabel("TEST_LABEL1"); 25 | int secondResult = labelDictionary.insertLabel("TEST_LABEL2"); 26 | 27 | // Then 28 | assertEquals(true, (firstResult != secondResult)); 29 | } 30 | 31 | public void testToString() throws Exception { 32 | // Given 33 | LabelDictionary labelDictionary = new LabelDictionary(); 34 | int firstResult = labelDictionary.insertLabel("TEST_LABEL1"); 35 | int secondResult = labelDictionary.insertLabel("TEST_LABEL2"); 36 | 37 | // When 38 | String result = labelDictionary.toString(); 39 | 40 | // Then 41 | assertEquals("TEST_LABEL1: 1\nTEST_LABEL2: 2\n", result); 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/FindCandidateRelationships.cl: -------------------------------------------------------------------------------- 1 | __kernel void find_candidate_relationships( 2 | int q_start_node, 3 | int q_end_node, 4 | int q_relationship_type, 5 | __global int* d_relationships, 6 | __global int* d_relationship_types, 7 | __global int* d_relationship_indices, 8 | 9 | __global int* candidate_relationship_end_nodes, 10 | __global int* candidate_relationship_indices, 11 | 12 | __global int* candidate_relationship_end_node_indices, 13 | __global int* candidate_relationship_start_nodes, 14 | __global bool* candidate_indicators, 15 | int d_node_count) 16 | { 17 | int workset_index = get_global_id(0); 18 | 19 | int candidate_start_node = candidate_relationship_start_nodes[workset_index]; 20 | 21 | int c_relationship_start_index = d_relationship_indices[candidate_start_node]; 22 | int c_relationship_end_index = d_relationship_indices[candidate_start_node+1]; 23 | 24 | int current_output_index = candidate_relationship_end_node_indices[workset_index]; 25 | for(int i = c_relationship_start_index; i < c_relationship_end_index; i++) { 26 | 27 | int candidate_end_node = d_relationships[i]; 28 | int candidate_relationship_type = d_relationship_types[i]; 29 | bool valid_relationship = q_relationship_type != -1 ? candidate_relationship_type == q_relationship_type : true; 30 | if(valid_relationship && candidate_indicators[q_end_node*d_node_count + candidate_end_node]) { 31 | candidate_relationship_end_nodes[current_output_index] = candidate_end_node; 32 | candidate_relationship_indices[current_output_index] = i; 33 | current_output_index++; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/runner/QueryRelationship.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.graphdb.Node; 5 | import org.neo4j.graphdb.Relationship; 6 | import org.neo4j.graphdb.RelationshipType; 7 | 8 | public class QueryRelationship implements Relationship { 9 | 10 | private final long id; 11 | private final Node startNode; 12 | private final Node endNode; 13 | private final RelationshipType type; 14 | 15 | public QueryRelationship(long id, Node startNode, Node endNode, RelationshipType type) { 16 | this.id = id; 17 | this.startNode = startNode; 18 | this.endNode = endNode; 19 | this.type = type; 20 | } 21 | 22 | public long getId() { 23 | return this.id; 24 | } 25 | 26 | public void delete() { 27 | 28 | } 29 | 30 | public Node getStartNode() { 31 | return this.startNode; 32 | } 33 | 34 | public Node getEndNode() { 35 | return this.endNode; 36 | } 37 | 38 | public Node getOtherNode(Node node) { 39 | return null; 40 | } 41 | 42 | public Node[] getNodes() { 43 | return new Node[0]; 44 | } 45 | 46 | public RelationshipType getType() { 47 | return this.type; 48 | } 49 | 50 | public boolean isType(RelationshipType relationshipType) { 51 | return false; 52 | } 53 | 54 | public GraphDatabaseService getGraphDatabase() { 55 | return null; 56 | } 57 | 58 | public boolean hasProperty(String s) { 59 | return false; 60 | } 61 | 62 | public Object getProperty(String s) { 63 | return null; 64 | } 65 | 66 | public Object getProperty(String s, Object o) { 67 | return null; 68 | } 69 | 70 | public void setProperty(String s, Object o) { 71 | 72 | } 73 | 74 | public Object removeProperty(String s) { 75 | return null; 76 | } 77 | 78 | public Iterable getPropertyKeys() { 79 | return null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/ValidateSolutions.cl: -------------------------------------------------------------------------------- 1 | __kernel void validate_solutions( 2 | int q_start_node, 3 | int q_end_node, 4 | int query_node_count, 5 | int query_relationship_count, 6 | __global int* possible_solution_elements, 7 | __global int* possible_solution_relationships, 8 | 9 | __global int* c_start_nodes, 10 | __global int* c_end_node_indices, 11 | __global int* c_end_nodes, 12 | __global int* c_relationship_indices, 13 | 14 | int start_node_count, 15 | 16 | __global bool* valid_relationships_indicators 17 | ) 18 | { 19 | int possible_solution_index = get_global_id(0); 20 | 21 | valid_relationships_indicators[possible_solution_index] = false; 22 | 23 | for(int i = 0; i < start_node_count; i++) { 24 | if(c_start_nodes[i] == possible_solution_elements[possible_solution_index*query_node_count + q_start_node]) { 25 | int end_node_index_start = c_end_node_indices[i]; 26 | int end_node_index_end = c_end_node_indices[i+1]; 27 | for(int j = end_node_index_start; j < end_node_index_end; j++) { 28 | 29 | if(c_end_nodes[j] == possible_solution_elements[possible_solution_index*query_node_count + q_end_node]) { 30 | int relationship_index = c_relationship_indices[j]; 31 | bool relationship_valid = true; 32 | for(int k = 0; k < query_relationship_count; k++) { 33 | if(possible_solution_relationships[possible_solution_index*query_relationship_count + k] == relationship_index) { 34 | relationship_valid = false; 35 | break; 36 | } 37 | } 38 | if(relationship_valid) { 39 | valid_relationships_indicators[possible_solution_index] = true; 40 | return; 41 | } 42 | } 43 | 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/CombineRelationships.cl: -------------------------------------------------------------------------------- 1 | __kernel void combine_relationships( 2 | int q_start_node, 3 | int q_end_node, 4 | int query_node_count, 5 | int query_relationship_count, 6 | __global int* possible_solution_elements, 7 | __global int* possible_solution_relationships, 8 | 9 | __global int* c_start_nodes, 10 | __global int* c_end_node_indices, 11 | __global int* c_end_nodes, 12 | __global int* c_relationship_indices, 13 | 14 | int start_node_count, 15 | 16 | __global int* output_indices, 17 | __global int* relationship_indices 18 | ) 19 | { 20 | int possible_solution_index = get_global_id(0); 21 | 22 | int output_index = output_indices[possible_solution_index]; 23 | 24 | for(int i = 0; i < start_node_count; i++) { 25 | if(c_start_nodes[i] == possible_solution_elements[possible_solution_index*query_node_count + q_start_node]) { 26 | int end_node_index_start = c_end_node_indices[i]; 27 | int end_node_index_end = c_end_node_indices[i+1]; 28 | for(int j = end_node_index_start; j < end_node_index_end; j++) { 29 | 30 | if(c_end_nodes[j] == possible_solution_elements[possible_solution_index*query_node_count + q_end_node]) { 31 | int relationship_index = c_relationship_indices[j]; 32 | bool relationship_valid = true; 33 | for(int k = 0; k < query_relationship_count; k++) { 34 | if(possible_solution_relationships[possible_solution_index*query_relationship_count + k] == relationship_index) { 35 | relationship_valid = false; 36 | break; 37 | } 38 | } 39 | if(relationship_valid) { 40 | relationship_indices[output_index] = relationship_index; 41 | output_index++; 42 | } 43 | } 44 | 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/PruneSolutions.cl: -------------------------------------------------------------------------------- 1 | __kernel void prune_solutions( 2 | int q_node_count, 3 | int q_relationship_count, 4 | int q_relationhip, 5 | 6 | __global int* old_possible_solution_elements, 7 | __global int* old_possible_solution_relationships, 8 | __global int* valid_relationships, 9 | __global int* output_indices, 10 | 11 | __global int* pruned_possible_solution_elements, 12 | __global int* pruned_possible_solution_relationships 13 | ) 14 | { 15 | int possible_solution_index = get_global_id(0); 16 | int output_start = output_indices[possible_solution_index]; 17 | int output_end = output_indices[possible_solution_index+1]; 18 | 19 | if(output_end > output_start) { 20 | for(int output_index = output_start; output_index < output_end; output_index++) { 21 | int old_solution_element_index = (possible_solution_index)*q_node_count; 22 | int old_solution_relationship_index = (possible_solution_index)*q_relationship_count; 23 | int new_solution_element_index = output_index*q_node_count; 24 | int new_solution_relationship_index = output_index*q_relationship_count; 25 | 26 | /* Copy solution elements to new indices */ 27 | for(int i = 0; i < q_node_count; i++ ) { 28 | pruned_possible_solution_elements[new_solution_element_index + i] = 29 | old_possible_solution_elements[old_solution_element_index + i]; 30 | } 31 | 32 | /* Copy solution relationships to new indices */ 33 | for(int j = 0; j < q_relationship_count; j++ ) { 34 | pruned_possible_solution_relationships[new_solution_relationship_index + j] = 35 | old_possible_solution_relationships[old_solution_relationship_index + j]; 36 | } 37 | 38 | /* Add current valid relationship to relationship indices */ 39 | pruned_possible_solution_relationships[new_solution_relationship_index + q_relationhip] = valid_relationships[output_index]; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/runner/AliasDictionary.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import org.neo4j.graphdb.Node; 4 | 5 | import java.util.*; 6 | 7 | public class AliasDictionary { 8 | private HashMap aliasToIds; 9 | 10 | private HashMap idToAliases; 11 | 12 | public AliasDictionary() { 13 | aliasToIds = new HashMap<>(); 14 | idToAliases = new HashMap<>(); 15 | } 16 | 17 | public void insertAlias(Node node, String alias) { 18 | aliasToIds.put(alias, (int) node.getId()); 19 | idToAliases.put((int) node.getId(), alias); 20 | } 21 | 22 | public int getIdForAlias(String alias) { 23 | return aliasToIds.get(alias); 24 | } 25 | 26 | public String getAliasForId(int id) { 27 | return idToAliases.get(id); 28 | } 29 | 30 | public Set getAllAliases() { 31 | return aliasToIds.keySet(); 32 | } 33 | 34 | public int getAliasCount() { 35 | return aliasToIds.size(); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | StringBuilder builder = new StringBuilder(); 41 | 42 | Set ids = this.idToAliases.keySet(); 43 | List idList = new ArrayList(ids); 44 | Collections.sort(idList); 45 | 46 | for (int id : idList) { 47 | builder.append("Id " + id + ": " + this.idToAliases.get(id)); 48 | builder.append('\n'); 49 | } 50 | 51 | return builder.toString(); 52 | } 53 | 54 | public static final int ALPHABET_SIZE = 26; 55 | 56 | public static final int ASCII_CHARACTER_START = 65; 57 | 58 | public static String createAlias(int currentNode) { 59 | StringBuilder builder = new StringBuilder(); 60 | 61 | int aliasLength = (currentNode / ALPHABET_SIZE) + 1; 62 | int rest = currentNode; 63 | for (int i = 0; i < aliasLength; i++) { 64 | int aliasCharacter = rest % ALPHABET_SIZE; 65 | builder.append((char) (ASCII_CHARACTER_START + aliasCharacter)); 66 | 67 | rest = rest - aliasCharacter - ALPHABET_SIZE; 68 | } 69 | 70 | return builder.reverse().toString(); 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/se/simonevertsson/gpu/graph/GpuGraphConverterTest.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.graph; 2 | 3 | import junit.framework.TestCase; 4 | import se.simonevertsson.MockHelper; 5 | import se.simonevertsson.gpu.dictionary.LabelDictionary; 6 | import se.simonevertsson.gpu.dictionary.TypeDictionary; 7 | import se.simonevertsson.runner.QueryGraph; 8 | 9 | import java.util.Arrays; 10 | 11 | public class GpuGraphConverterTest extends TestCase { 12 | 13 | public void testGeneratesCorrectLabels() throws Exception { 14 | // Given 15 | QueryGraph queryGraph = MockHelper.generateMockQueryGraph(); 16 | LabelDictionary labelDictionary = new LabelDictionary(); 17 | TypeDictionary typeDictionary = new TypeDictionary(); 18 | GpuGraphConverter gpuGraphConverter = new GpuGraphConverter(queryGraph.nodes, labelDictionary, typeDictionary); 19 | 20 | // When 21 | GpuGraph result = gpuGraphConverter.convert(); 22 | 23 | // Then 24 | assertEquals(Arrays.toString(new int[] {0,1,2,2,3}), Arrays.toString(result.getLabelIndices())); 25 | assertEquals(Arrays.toString(new int[] {1,2,3}), Arrays.toString(result.getNodeLabels())); 26 | } 27 | 28 | public void testGeneratesCorrectRelationships() throws Exception { 29 | // Given 30 | QueryGraph queryGraph = MockHelper.generateMockQueryGraph(); 31 | LabelDictionary labelDictionary = new LabelDictionary(); 32 | TypeDictionary typeDictionary = new TypeDictionary(); 33 | GpuGraphConverter gpuGraphConverter = new GpuGraphConverter(queryGraph.nodes, labelDictionary, typeDictionary); 34 | 35 | // When 36 | GpuGraph result = gpuGraphConverter.convert(); 37 | 38 | // Then 39 | assertEquals(Arrays.toString(new int[] {0,2,4,5,5}), Arrays.toString(result.getRelationshipIndices())); 40 | assertEquals(Arrays.toString(new int[] {1,2,2,3,3}), Arrays.toString(result.getNodeRelationships())); 41 | assertEquals(Arrays.toString(new int[] {-1,1,-1,2,-1}), Arrays.toString(result.getRelationshipTypes())); 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/Main.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson; 2 | 3 | import se.simonevertsson.db.DatabaseService; 4 | import se.simonevertsson.experiments.PerformanceExperiment; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Executes a performance experiment and stores the result in "/results" 10 | */ 11 | public class Main { 12 | 13 | private static String databasePath; 14 | private static String databaseConfigPath; 15 | private static int maxQueryGraphNodeCount; 16 | private static int minQueryGraphRelationshipCount; 17 | private static int experimentIterations; 18 | 19 | public static void main(String[] args) throws IOException { 20 | validateInputArguments(args); 21 | 22 | // Setup database connection 23 | DatabaseService databaseService = new DatabaseService(databasePath, databaseConfigPath); 24 | 25 | PerformanceExperiment performanceExperiment = new PerformanceExperiment(databaseService, maxQueryGraphNodeCount, minQueryGraphRelationshipCount, experimentIterations); 26 | performanceExperiment.runExperiment(); 27 | 28 | //Tear down database 29 | System.out.println("Terminating database connection..."); 30 | databaseService.shutdown(); 31 | System.out.println("Database connection terminated."); 32 | } 33 | 34 | private static void validateInputArguments(String[] args) { 35 | try { 36 | databasePath = args[0]; 37 | databaseConfigPath = args[1]; 38 | maxQueryGraphNodeCount = Integer.parseInt(args[2]); 39 | minQueryGraphRelationshipCount = Integer.parseInt(args[3]); 40 | experimentIterations = Integer.parseInt(args[4]); 41 | } catch (Exception e) { 42 | StringBuilder builder = new StringBuilder(); 43 | builder.append("Invalid input arguments.\n"); 44 | builder.append("expected: databasePath(String) databaseConfigPath(String) maxQueryGraphNodeCount(int) minQueryGraphRelationshipCount(int) experimentIterations(int)\n"); 45 | builder.append("actual:" + String.join(" ", args) + "\n"); 46 | throw new IllegalArgumentException(builder.toString()); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/kernel/QueryKernels.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.kernel; 2 | 3 | import com.nativelibs4java.opencl.CLContext; 4 | import com.nativelibs4java.opencl.CLQueue; 5 | import com.nativelibs4java.opencl.JavaCL; 6 | import se.simonevertsson.*; 7 | 8 | import java.io.IOException; 9 | 10 | public class QueryKernels { 11 | public CLContext context; 12 | public CLQueue queue; 13 | public CheckCandidates checkCandidatesKernel; 14 | public ExploreCandidates exploreCandidatesKernel; 15 | public RefineCandidates refineCandidatesKernel; 16 | public CountCandidateRelationships countCandidateRelationshipsKernel; 17 | public FindCandidateRelationships findCandidateRelationshipsKernel; 18 | public CountSolutionCombinations countSolutionCombinationsKernel; 19 | public GenerateSolutionCombinations generateSolutionCombinationsKernel; 20 | public ValidateSolutions validateSolutionsKernel; 21 | public CombineRelationships combineRelationshipsKernel; 22 | public PruneSolutions pruneSolutionsKernel; 23 | 24 | public QueryKernels() throws IOException { 25 | initializeQueryKernels(); 26 | } 27 | 28 | private void initializeQueryKernels() throws IOException { 29 | this.context = JavaCL.createBestContext(); 30 | this.queue = this.context.createDefaultQueue(); 31 | this.checkCandidatesKernel = new CheckCandidates(this.context); 32 | this.exploreCandidatesKernel = new ExploreCandidates(this.context); 33 | this.refineCandidatesKernel = new RefineCandidates(this.context); 34 | this.countCandidateRelationshipsKernel = new CountCandidateRelationships(this.context); 35 | this.findCandidateRelationshipsKernel = new FindCandidateRelationships(this.context); 36 | this.countSolutionCombinationsKernel = new CountSolutionCombinations(this.context); 37 | this.generateSolutionCombinationsKernel = new GenerateSolutionCombinations(this.context); 38 | this.validateSolutionsKernel = new ValidateSolutions(this.context); 39 | this.combineRelationshipsKernel = new CombineRelationships(this.context); 40 | this.pruneSolutionsKernel = new PruneSolutions(this.context); 41 | } 42 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | Neo4j-GPU 5 | se.simonevertsson 6 | Neo4j-GPU 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | 11 | 12 | sonatype 13 | Sonatype OSS Snapshots Repository 14 | http://oss.sonatype.org/content/groups/public 15 | 16 | 17 | nativelibs4java 18 | nativelibs4java Maven2 Repository 19 | http://nativelibs4java.sourceforge.net/maven 20 | 21 | 22 | 23 | 24 | 25 | sonatype 26 | Sonatype OSS Snapshots Repository 27 | http://oss.sonatype.org/content/groups/public 28 | 29 | 30 | 31 | 32 | 33 | com.nativelibs4java 34 | javacl 35 | 1.0-SNAPSHOT 36 | 37 | 38 | 39 | org.neo4j 40 | neo4j 41 | 2.2.3 42 | 43 | 44 | 45 | 46 | 47 | 48 | 53 | com.nativelibs4java 54 | maven-javacl-plugin 55 | 1.0-SNAPSHOT 56 | 57 | 58 | 59 | compile 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/join/SolutionValidator.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.join; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import org.bridj.Pointer; 7 | import se.simonevertsson.gpu.query.QueryContext; 8 | import se.simonevertsson.gpu.kernel.QueryKernels; 9 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationships; 10 | 11 | import java.io.IOException; 12 | 13 | public class SolutionValidator { 14 | private final QueryKernels queryKernels; 15 | private final QueryContext queryContext; 16 | 17 | public SolutionValidator(QueryKernels queryKernels, QueryContext queryContext) { 18 | this.queryKernels = queryKernels; 19 | this.queryContext = queryContext; 20 | } 21 | 22 | public CLBuffer validateSolutions(PossibleSolutions possibleSolutions, CandidateRelationships candidateRelationships) throws IOException { 23 | int possibleSolutionCount = (int) possibleSolutions.getSolutionElements().getElementCount() / this.queryContext.queryNodeCount; 24 | 25 | CLBuffer validationIndicators = this.queryKernels.context.createBuffer( 26 | CLMem.Usage.Output, 27 | Pointer.pointerToBooleans(new boolean[possibleSolutionCount])); 28 | 29 | int[] globalSizes = new int[]{possibleSolutionCount}; 30 | 31 | CLEvent validateSolutionsEvent = this.queryKernels.validateSolutionsKernel.validate_solutions( 32 | this.queryKernels.queue, 33 | candidateRelationships.getQueryStartNodeId(), 34 | candidateRelationships.getQueryEndNodeId(), 35 | this.queryContext.queryNodeCount, 36 | this.queryContext.queryRelationshipCount, 37 | possibleSolutions.getSolutionElements(), 38 | possibleSolutions.getSolutionRelationships(), 39 | candidateRelationships.getCandidateStartNodes(), 40 | candidateRelationships.getCandidateEndNodeIndices(), 41 | candidateRelationships.getCandidateEndNodes(), 42 | candidateRelationships.getRelationshipIndices(), 43 | candidateRelationships.getStartNodeCount(), 44 | validationIndicators, 45 | globalSizes, 46 | null 47 | ); 48 | 49 | validateSolutionsEvent.waitFor(); 50 | 51 | return validationIndicators; 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/runner/CypherQueryRunner.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import org.neo4j.graphdb.Result; 5 | import org.neo4j.graphdb.Transaction; 6 | import se.simonevertsson.db.DatabaseService; 7 | import se.simonevertsson.gpu.query.QueryResult; 8 | import se.simonevertsson.gpu.query.QuerySolution; 9 | 10 | import java.util.*; 11 | 12 | /** 13 | * Class which executes Cypher queries on a supplied {@link DatabaseService} from a given {@link QueryGraph}. 14 | */ 15 | public class CypherQueryRunner { 16 | 17 | 18 | /** 19 | * Executes the Cypher runner generated from the supplied {@link QueryGraph} on the supplied {@link DatabaseService} and 20 | * returns the result of the runner as a {@link QueryResult}. 21 | * 22 | * @param dbService A valid database service 23 | * @param queryGraph The runner graph which whose Cypher representation will be executed on the supplied database service. 24 | * @return 25 | */ 26 | public QueryResult runCypherQueryForSolutions(DatabaseService dbService, QueryGraph queryGraph) { 27 | long tick = System.currentTimeMillis(); 28 | 29 | List querySolutions; 30 | try (Transaction tx = dbService.beginTx()) { 31 | Result result = dbService.excuteCypherQuery(queryGraph.toCypherQueryString()); 32 | querySolutions = handleResult(result, queryGraph); 33 | tx.success(); 34 | } 35 | 36 | long tock = System.currentTimeMillis(); 37 | long queryExecutionTime = tock - tick; 38 | 39 | return new QueryResult(querySolutions, 0, queryExecutionTime); 40 | } 41 | 42 | private List handleResult(Result result, QueryGraph queryGraph) { 43 | ArrayList results = new ArrayList<>(); 44 | while (result.hasNext()) { 45 | List> solutionElements = new ArrayList<>(); 46 | Map resultRow = result.next(); 47 | for (String key : result.columns()) { 48 | Node resultNode = ((Node) resultRow.get(key)); 49 | Map.Entry solutionElement = new AbstractMap.SimpleEntry<>(key, (int) resultNode.getId()); 50 | solutionElements.add(solutionElement); 51 | } 52 | 53 | QuerySolution querySolution = new QuerySolution(queryGraph, solutionElements); 54 | results.add(querySolution); 55 | } 56 | result.close(); 57 | return results; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/dictionary/LabelDictionary.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.dictionary; 2 | 3 | import java.util.Map; 4 | import java.util.TreeMap; 5 | 6 | /** 7 | * Dictionary class which contains Node labels and their corresponding generated GPU-friendly representation 8 | */ 9 | public class LabelDictionary { 10 | 11 | private Map labelToIds; 12 | 13 | private Map idToLabels; 14 | 15 | private int nextId; 16 | 17 | /** 18 | * Initializes a new label dictionary 19 | */ 20 | public LabelDictionary() { 21 | labelToIds = new TreeMap<>(); 22 | idToLabels = new TreeMap<>(); 23 | nextId = 1; 24 | } 25 | 26 | /** 27 | * Inserts the supplied label to this dictionary, generating a new GPU representation if this label doesn't exist 28 | * in this dictionary yet. 29 | * 30 | * @param label The label which will get a generated translation and be inserted to this dictionary 31 | * @return 32 | */ 33 | public int insertLabel(String label) { 34 | if (labelToIds.containsKey(label)) { 35 | return getIdForLabel(label); 36 | } else { 37 | int currentId = nextId; 38 | labelToIds.put(label, currentId); 39 | idToLabels.put(currentId, label); 40 | nextId++; 41 | return currentId; 42 | } 43 | } 44 | 45 | /** 46 | * Returns the GPU translation for the supplied label 47 | * 48 | * @param label A node label whose generated translation should be returned 49 | * @return The GPU translation for the given label or {@code -1} if it doesn't exist. 50 | */ 51 | public int getIdForLabel(String label) { 52 | return labelToIds.getOrDefault(label, -1); 53 | } 54 | 55 | /** 56 | * Returns the Node label for the supplied GPU translation 57 | * 58 | * @param labelId A GPU label previously translated from a Node label using {@link LabelDictionary#insertLabel(String)} 59 | * @return The label for the given GPU label id or {@code null} if it doesn't exist. 60 | */ 61 | public String getLabelForId(int labelId) { 62 | return idToLabels.get(labelId); 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | StringBuilder builder = new StringBuilder(); 68 | for (Map.Entry labelEntry : labelToIds.entrySet()) { 69 | builder.append(labelEntry.getKey() + ": " + labelToIds.get(labelEntry.getKey()) + "\n"); 70 | } 71 | return builder.toString(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/join/SolutionRelationshipCombiner.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.join; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import org.bridj.Pointer; 7 | import se.simonevertsson.gpu.query.QueryContext; 8 | import se.simonevertsson.gpu.kernel.QueryKernels; 9 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationships; 10 | 11 | import java.io.IOException; 12 | 13 | public class SolutionRelationshipCombiner { 14 | private final QueryKernels queryKernels; 15 | private final QueryContext queryContext; 16 | 17 | public SolutionRelationshipCombiner(QueryKernels queryKernels, QueryContext queryContext) { 18 | this.queryKernels = queryKernels; 19 | this.queryContext = queryContext; 20 | } 21 | 22 | public CLBuffer combineRelationships(PossibleSolutions possibleSolutions, CandidateRelationships candidateRelationships, int[] outputIndicesArray) throws IOException { 23 | 24 | CLBuffer outputIndicesBuffer = this.queryKernels.context.createBuffer( 25 | CLMem.Usage.Output, 26 | Pointer.pointerToInts(outputIndicesArray), 27 | true); 28 | 29 | CLBuffer relationshipIndicesBuffer = this.queryKernels.context.createBuffer( 30 | CLMem.Usage.Output, 31 | Pointer.pointerToInts(new int[outputIndicesArray[outputIndicesArray.length - 1]]), 32 | true); 33 | 34 | 35 | int[] globalSizes = new int[]{possibleSolutions.getSolutionCount()}; 36 | 37 | CLEvent validateSolutionsEvent = this.queryKernels.combineRelationshipsKernel.combine_relationships( 38 | this.queryKernels.queue, 39 | candidateRelationships.getQueryStartNodeId(), 40 | candidateRelationships.getQueryEndNodeId(), 41 | this.queryContext.queryNodeCount, 42 | this.queryContext.queryRelationshipCount, 43 | possibleSolutions.getSolutionElements(), 44 | possibleSolutions.getSolutionRelationships(), 45 | candidateRelationships.getCandidateStartNodes(), 46 | candidateRelationships.getCandidateEndNodeIndices(), 47 | candidateRelationships.getCandidateEndNodes(), 48 | candidateRelationships.getRelationshipIndices(), 49 | candidateRelationships.getStartNodeCount(), 50 | outputIndicesBuffer, 51 | relationshipIndicesBuffer, 52 | globalSizes, 53 | null 54 | ); 55 | 56 | validateSolutionsEvent.waitFor(); 57 | 58 | return relationshipIndicesBuffer; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/candidate/refinement/CandidateRefinement.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.candidate.refinement; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import se.simonevertsson.gpu.query.QueryContext; 5 | import se.simonevertsson.gpu.query.QueryUtils; 6 | import se.simonevertsson.gpu.buffer.BufferContainer; 7 | import se.simonevertsson.gpu.buffer.QueryBuffers; 8 | import se.simonevertsson.gpu.kernel.QueryKernels; 9 | 10 | import java.io.IOException; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class CandidateRefinement { 15 | 16 | private final QueryBuffers queryBuffers; 17 | private final int queryNodeCount; 18 | private final int dataNodeCount; 19 | private final CandidateRefinery candidateRefinery; 20 | private final QueryContext queryContext; 21 | 22 | public CandidateRefinement(QueryContext queryContext, QueryKernels queryKernels, BufferContainer bufferContainer) { 23 | this.queryContext = queryContext; 24 | this.queryBuffers = bufferContainer.queryBuffers; 25 | this.queryNodeCount = queryContext.queryNodeCount; 26 | this.dataNodeCount = queryContext.dataNodeCount; 27 | this.candidateRefinery = new CandidateRefinery(queryKernels, bufferContainer, queryContext); 28 | } 29 | 30 | public void refine(List visitOrder) throws IOException { 31 | boolean[] oldCandidateIndicators = QueryUtils.pointerBooleanToArray(this.queryBuffers.candidateIndicatorsPointer, this.dataNodeCount * this.queryNodeCount); 32 | boolean candidateIndicatorsHasChanged = true; 33 | while (candidateIndicatorsHasChanged) { 34 | System.out.println("Refining candidates."); 35 | for (Node queryNode : visitOrder) { 36 | int queryNodeId = this.queryContext.gpuQuery.getNodeIdDictionary().getQueryId(queryNode.getId()); 37 | int[] candidateArray = QueryUtils.gatherCandidateArray(this.queryBuffers.candidateIndicatorsPointer, this.dataNodeCount, queryNodeId); 38 | if (candidateArray.length > 0) { 39 | this.candidateRefinery.refineCandidates(queryNodeId, candidateArray); 40 | } else { 41 | throw new IllegalStateException("Candidate refinement yielded no candidates for runner node " + queryNodeId); 42 | } 43 | } 44 | 45 | boolean[] newCandidateIndicators = QueryUtils.pointerBooleanToArray(this.queryBuffers.candidateIndicatorsPointer, this.dataNodeCount * this.queryNodeCount); 46 | 47 | candidateIndicatorsHasChanged = !Arrays.equals(oldCandidateIndicators, newCandidateIndicators); 48 | oldCandidateIndicators = newCandidateIndicators; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/candidate/initialization/CandidateChecker.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.candidate.initialization; 2 | 3 | import com.nativelibs4java.opencl.CLEvent; 4 | import org.neo4j.graphdb.Direction; 5 | import org.neo4j.graphdb.Node; 6 | import se.simonevertsson.gpu.query.QueryContext; 7 | import se.simonevertsson.gpu.buffer.BufferContainer; 8 | import se.simonevertsson.gpu.buffer.DataBuffers; 9 | import se.simonevertsson.gpu.buffer.QueryBuffers; 10 | import se.simonevertsson.gpu.graph.GpuGraph; 11 | import se.simonevertsson.gpu.kernel.QueryKernels; 12 | 13 | import java.io.IOException; 14 | 15 | public class CandidateChecker { 16 | 17 | private final QueryKernels queryKernels; 18 | private final QueryBuffers queryBuffers; 19 | private final DataBuffers dataBuffers; 20 | private final int dataNodeCount; 21 | private final QueryContext queryContext; 22 | 23 | public CandidateChecker(QueryContext queryContext, QueryKernels queryKernels, BufferContainer bufferContainer, int dataNodeCount) { 24 | this.queryContext = queryContext; 25 | this.queryKernels = queryKernels; 26 | this.queryBuffers = bufferContainer.queryBuffers; 27 | this.dataBuffers = bufferContainer.dataBuffers; 28 | this.dataNodeCount = dataNodeCount; 29 | } 30 | 31 | public void checkCandidates(GpuGraph gpuQuery, Node queryNode) throws IOException { 32 | int queryNodeId = this.queryContext.gpuQuery.getNodeIdDictionary().getQueryId(queryNode.getId()); 33 | 34 | int queryLabelStartIndex = gpuQuery.getLabelIndices()[queryNodeId]; 35 | int queryLabelEndIndex = gpuQuery.getLabelIndices()[queryNodeId + 1]; 36 | int queryNodeDegree = queryNode.getDegree(Direction.OUTGOING); 37 | int[] globalSizes = new int[]{this.dataNodeCount}; 38 | 39 | 40 | CLEvent checkCandidatesEvent = this.queryKernels.checkCandidatesKernel.check_candidates( 41 | this.queryKernels.queue, 42 | this.queryBuffers.queryNodeLabelsBuffer, 43 | queryNodeId, 44 | queryLabelStartIndex, 45 | queryLabelEndIndex, 46 | queryNodeDegree, 47 | this.dataBuffers.dataLabelIndicesBuffer, 48 | this.dataBuffers.dataLabelsBuffer, 49 | this.dataBuffers.dataNodeRelationshipsBuffer, 50 | this.dataBuffers.dataRelationshipIndicesBuffer, 51 | this.queryBuffers.candidateIndicatorsBuffer, 52 | this.dataNodeCount, 53 | globalSizes, 54 | null); 55 | 56 | this.queryBuffers.candidateIndicatorsPointer = 57 | this.queryBuffers.candidateIndicatorsBuffer.read(this.queryKernels.queue, checkCandidatesEvent); 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/join/SolutionCombinationCounter.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.join; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import org.bridj.Pointer; 7 | import se.simonevertsson.gpu.query.QueryContext; 8 | import se.simonevertsson.gpu.kernel.QueryKernels; 9 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationships; 10 | 11 | import java.io.IOException; 12 | 13 | public class SolutionCombinationCounter { 14 | private final QueryKernels queryKernels; 15 | private final QueryContext queryContext; 16 | 17 | public SolutionCombinationCounter(QueryKernels queryKernels, QueryContext queryContext) { 18 | this.queryKernels = queryKernels; 19 | this.queryContext = queryContext; 20 | } 21 | 22 | public Pointer countSolutionCombinations(PossibleSolutions possibleSolutions, CandidateRelationships candidateRelationships, boolean startNodeVisited) throws IOException { 23 | int possibleSolutionCount = (int) possibleSolutions.getSolutionElements().getElementCount() / this.queryContext.queryNodeCount; 24 | 25 | CLBuffer 26 | combinationCountsBuffer = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, possibleSolutionCount); 27 | 28 | int[] globalSizes = new int[]{(int) possibleSolutionCount}; 29 | 30 | CLBuffer startNodeVisitedBuffer = this.queryKernels.context.createBuffer( 31 | CLMem.Usage.Input, 32 | Pointer.pointerToBooleans(startNodeVisited), true); 33 | 34 | CLEvent countSolutionCombinationsEvent = this.queryKernels.countSolutionCombinationsKernel.count_solution_combinations( 35 | this.queryKernels.queue, 36 | candidateRelationships.getQueryStartNodeId(), 37 | candidateRelationships.getQueryEndNodeId(), 38 | this.queryContext.queryNodeCount, 39 | this.queryContext.queryRelationshipCount, 40 | possibleSolutions.getSolutionElements(), 41 | possibleSolutions.getSolutionRelationships(), 42 | candidateRelationships.getCandidateStartNodes(), 43 | candidateRelationships.getCandidateEndNodeIndices(), 44 | candidateRelationships.getCandidateEndNodes(), 45 | candidateRelationships.getRelationshipIndices(), 46 | startNodeVisitedBuffer, 47 | candidateRelationships.getStartNodeCount(), 48 | combinationCountsBuffer, 49 | globalSizes, 50 | null 51 | ); 52 | 53 | return combinationCountsBuffer.read(this.queryKernels.queue, countSolutionCombinationsEvent); 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/graph/GpuGraph.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.graph; 2 | 3 | import org.apache.lucene.util.ArrayUtil; 4 | import se.simonevertsson.gpu.dictionary.QueryIdDictionary; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | 9 | public class GpuGraph { 10 | 11 | private QueryIdDictionary nodeIdDictionary; 12 | 13 | private QueryIdDictionary relationshipIdDictionary; 14 | 15 | private int[] labelIndices; 16 | 17 | private int[] nodeLabels; 18 | 19 | private int[] relationshipIndices; 20 | 21 | private int[] nodeRelationships; 22 | 23 | private int[] relationshipTypes; 24 | 25 | public GpuGraph(ArrayList labelIndices, ArrayList nodeLabels, ArrayList relationshipIndices, ArrayList nodeRelationships, ArrayList relationshipTypes, QueryIdDictionary nodeIdDictionary, QueryIdDictionary relationshipIdDictionary) { 26 | this.labelIndices = ArrayUtil.toIntArray(labelIndices); 27 | this.nodeLabels = ArrayUtil.toIntArray(nodeLabels); 28 | this.relationshipIndices = ArrayUtil.toIntArray(relationshipIndices); 29 | this.nodeRelationships = ArrayUtil.toIntArray(nodeRelationships); 30 | this.relationshipTypes = ArrayUtil.toIntArray(relationshipTypes); 31 | this.nodeIdDictionary = nodeIdDictionary; 32 | this.relationshipIdDictionary = relationshipIdDictionary; 33 | } 34 | 35 | public int[] getNodeLabels() { 36 | return nodeLabels; 37 | } 38 | 39 | public int[] getRelationshipIndices() { 40 | return relationshipIndices; 41 | } 42 | 43 | public int[] getNodeRelationships() { 44 | return nodeRelationships; 45 | } 46 | 47 | public int[] getLabelIndices() { 48 | return labelIndices; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | StringBuilder builder = new StringBuilder(); 54 | builder.append("Node labels: " + Arrays.toString(this.nodeLabels) + "\n"); 55 | builder.append("Label indicies: " + Arrays.toString(this.labelIndices) + "\n"); 56 | builder.append("Node relationships: " + Arrays.toString(this.nodeRelationships) + "\n"); 57 | builder.append("Relationship types: " + Arrays.toString(this.relationshipTypes) + "\n"); 58 | builder.append("Relationship indicies: " + Arrays.toString(this.relationshipIndices)); 59 | return builder.toString(); 60 | } 61 | 62 | public int[] getRelationshipTypes() { 63 | return relationshipTypes; 64 | } 65 | 66 | public QueryIdDictionary getNodeIdDictionary() { 67 | return nodeIdDictionary; 68 | } 69 | 70 | public QueryIdDictionary getRelationshipIdDictionary() { 71 | return relationshipIdDictionary; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/experiments/StatisticsUtils.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.experiments; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Utils class which contains classes to calculate means and standard deviations of runtimes, speedups etc. 8 | */ 9 | public class StatisticsUtils { 10 | 11 | static double stdDevOfGpuSpeedup(ArrayList gpuRuntimes, ArrayList cypherRuntimes, double gpuSpeedupAvg) { 12 | double deviationSum = 0; 13 | for (int i = 0; i < gpuRuntimes.size(); i++) { 14 | double dataPoint = (double) cypherRuntimes.get(i) / (double) gpuRuntimes.get(i); 15 | deviationSum += Math.pow(dataPoint - gpuSpeedupAvg, 2); 16 | } 17 | return Math.sqrt(deviationSum / (double) gpuRuntimes.size()); 18 | } 19 | 20 | static double meanOfGpuSpeedup(ArrayList gpuRuntimes, ArrayList cypherRuntimes) { 21 | double sum = 0; 22 | for (int i = 0; i < gpuRuntimes.size(); i++) { 23 | sum += (double) cypherRuntimes.get(i) / (double) gpuRuntimes.get(i); 24 | } 25 | return sum / (double) gpuRuntimes.size(); 26 | } 27 | 28 | static double stdDevOfSpeedupIncludingConversionTime(ArrayList conversionRuntimes, ArrayList gpuRuntimes, ArrayList cypherRuntimes, double conversionSpeedupAvg) { 29 | double deviationSum = 0; 30 | for (int i = 0; i < conversionRuntimes.size(); i++) { 31 | double dataPoint = (double) cypherRuntimes.get(i) / (double) (conversionRuntimes.get(i) + gpuRuntimes.get(i)); 32 | deviationSum += Math.pow(dataPoint - conversionSpeedupAvg, 2); 33 | } 34 | return Math.sqrt(deviationSum / (double) conversionRuntimes.size()); 35 | } 36 | 37 | static double meanOfSpeedupIncludingConversionTime(ArrayList conversionRuntimes, ArrayList gpuRuntimes, ArrayList cypherRuntimes) { 38 | double sum = 0; 39 | for (int i = 0; i < conversionRuntimes.size(); i++) { 40 | sum += (double) cypherRuntimes.get(i) / (double) (conversionRuntimes.get(i) + gpuRuntimes.get(i)); 41 | } 42 | return sum / (double) conversionRuntimes.size(); 43 | } 44 | 45 | static double stdDev(List conversionRunTimes, double conversionAvg) { 46 | double deviationSum = 0; 47 | for (long conversionRunTime : conversionRunTimes) { 48 | deviationSum += Math.pow((double) conversionRunTime - conversionAvg, 2); 49 | } 50 | return Math.sqrt(deviationSum / (double) conversionRunTimes.size()); 51 | } 52 | 53 | static double mean(ArrayList values) { 54 | long sum = 0; 55 | for (long value : values) { 56 | sum += value; 57 | } 58 | return (double) sum / (double) values.size(); 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/QuerySolution.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import se.simonevertsson.runner.AliasDictionary; 4 | import se.simonevertsson.runner.QueryGraph; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class QuerySolution { 10 | 11 | private List> solutionElements; 12 | 13 | private QueryGraph queryGraph; 14 | 15 | public QuerySolution(QueryGraph queryGraph, List> solutionElements) { 16 | this.queryGraph = queryGraph; 17 | this.solutionElements = solutionElements; 18 | } 19 | 20 | public void sort(AliasDictionary aliasDictionary) { 21 | List> sortedSolution = this.solutionElements; 22 | 23 | for (Map.Entry solutionElement : this.solutionElements) { 24 | String alias = solutionElement.getKey(); 25 | sortedSolution.set(aliasDictionary.getIdForAlias(alias), solutionElement); 26 | } 27 | } 28 | 29 | 30 | @Override 31 | public String toString() { 32 | StringBuilder builder = new StringBuilder(); 33 | builder.append(this.queryGraph.toCypherQueryStringPrefix()); 34 | builder.append(" WHERE "); 35 | boolean firstReturnNode = true; 36 | for (Map.Entry solutionElement : this.solutionElements) { 37 | if (!firstReturnNode) { 38 | builder.append(" AND "); 39 | } else { 40 | firstReturnNode = false; 41 | } 42 | builder.append("id(" + solutionElement.getKey() + ")=" + solutionElement.getValue()); 43 | } 44 | builder.append(this.queryGraph.toCypherQueryStringSuffix()); 45 | return builder.toString(); 46 | } 47 | 48 | @Override 49 | public boolean equals(Object obj) { 50 | boolean result = false; 51 | if (obj instanceof QuerySolution) { 52 | QuerySolution that = (QuerySolution) obj; 53 | if (this.solutionElements.size() == that.solutionElements.size()) { 54 | for (Map.Entry thisSolutionElement : this.solutionElements) { 55 | boolean matchFound = false; 56 | for (Map.Entry thatSolutionElement : that.solutionElements) { 57 | if (thisSolutionElement.getKey().compareTo(thatSolutionElement.getKey()) == 0) { 58 | if (thisSolutionElement.getValue().intValue() == thatSolutionElement.getValue().intValue()) { 59 | matchFound = true; 60 | break; 61 | } 62 | } 63 | } 64 | if (!matchFound) { 65 | return false; 66 | } 67 | } 68 | result = true; 69 | } 70 | } 71 | return result; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/join/PossibleSolutions.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.join; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLQueue; 5 | import se.simonevertsson.gpu.query.QueryContext; 6 | import se.simonevertsson.gpu.query.QueryUtils; 7 | 8 | import java.util.Arrays; 9 | 10 | public class PossibleSolutions { 11 | 12 | private final int solutionCount; 13 | private final CLQueue clQueue; 14 | private final QueryContext queryContext; 15 | private CLBuffer solutionElements; 16 | 17 | private CLBuffer solutionRelationships; 18 | 19 | public PossibleSolutions(CLBuffer solutionElements, CLBuffer solutionRelationships, QueryContext queryContext, CLQueue queue) { 20 | this.solutionElements = solutionElements; 21 | this.solutionRelationships = solutionRelationships; 22 | this.queryContext = queryContext; 23 | this.clQueue = queue; 24 | 25 | this.solutionCount = (int) (this.solutionElements.getElementCount() / this.queryContext.queryNodeCount); 26 | } 27 | 28 | 29 | public CLBuffer getSolutionElements() { 30 | return solutionElements; 31 | } 32 | 33 | public CLBuffer getSolutionRelationships() { 34 | return solutionRelationships; 35 | } 36 | 37 | public int getSolutionCount() { 38 | return solutionCount; 39 | } 40 | 41 | public String toString() { 42 | int solutionElementCount = this.queryContext.queryNodeCount; 43 | int solutionRelationshipsCount = this.queryContext.queryRelationshipCount; 44 | StringBuilder builder = new StringBuilder(); 45 | builder.append("Solutions: "); 46 | int[] solutionElements = QueryUtils.pointerIntegerToArray(this.solutionElements.read(this.clQueue), (int) this.solutionElements.getElementCount()); 47 | int[] solutionRelationships = QueryUtils.pointerIntegerToArray(this.solutionRelationships.read(this.clQueue), (int) this.solutionRelationships.getElementCount()); 48 | 49 | int elementOffset = 0; 50 | int relationshipOffset = 0; 51 | for (int i = 0; i < this.solutionCount; i++) { 52 | builder.append("{\n"); 53 | builder.append(Arrays.toString(Arrays.copyOfRange(solutionElements, elementOffset, elementOffset + solutionElementCount))); 54 | builder.append('\n'); 55 | builder.append(Arrays.toString(Arrays.copyOfRange(solutionRelationships, relationshipOffset, relationshipOffset + solutionRelationshipsCount))); 56 | builder.append("}\n"); 57 | elementOffset += solutionElementCount; 58 | relationshipOffset += solutionRelationshipsCount; 59 | } 60 | 61 | return builder.toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/search/CandidateRelationshipFinder.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.search; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import se.simonevertsson.gpu.buffer.BufferContainer; 7 | import se.simonevertsson.gpu.buffer.DataBuffers; 8 | import se.simonevertsson.gpu.buffer.QueryBuffers; 9 | import se.simonevertsson.gpu.kernel.QueryKernels; 10 | 11 | import java.io.IOException; 12 | 13 | public class CandidateRelationshipFinder { 14 | 15 | private final QueryKernels queryKernels; 16 | private final QueryBuffers queryBuffers; 17 | private final DataBuffers dataBuffers; 18 | private final int dataNodeCount; 19 | 20 | public CandidateRelationshipFinder(QueryKernels queryKernels, BufferContainer bufferContainer, int dataNodeCount) { 21 | this.queryKernels = queryKernels; 22 | this.queryBuffers = bufferContainer.queryBuffers; 23 | this.dataBuffers = bufferContainer.dataBuffers; 24 | this.dataNodeCount = dataNodeCount; 25 | } 26 | 27 | public CandidateRelationships findCandidateRelationships(CandidateRelationships candidateRelationships) throws IOException { 28 | 29 | CLBuffer candidateRelationshipEndNodes = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, candidateRelationships.getEndNodeCount()); 30 | 31 | CLBuffer candidateRelationshipIndices = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, candidateRelationships.getEndNodeCount()); 32 | 33 | 34 | int[] globalSizes = new int[]{candidateRelationships.getStartNodeCount()}; 35 | 36 | CLEvent findCandidateRelationshipsEvent = this.queryKernels.findCandidateRelationshipsKernel.find_candidate_relationships( 37 | this.queryKernels.queue, 38 | 39 | candidateRelationships.getQueryStartNodeId(), 40 | candidateRelationships.getQueryEndNodeId(), 41 | candidateRelationships.getRelationshipType(), 42 | 43 | this.dataBuffers.dataNodeRelationshipsBuffer, 44 | this.dataBuffers.dataRelationshipTypesBuffer, 45 | this.dataBuffers.dataRelationshipIndicesBuffer, 46 | 47 | candidateRelationshipEndNodes, 48 | candidateRelationshipIndices, 49 | 50 | candidateRelationships.getCandidateEndNodeIndices(), 51 | candidateRelationships.getCandidateStartNodes(), 52 | this.queryBuffers.candidateIndicatorsBuffer, 53 | this.dataNodeCount, 54 | 55 | globalSizes, 56 | null 57 | ); 58 | 59 | findCandidateRelationshipsEvent.waitFor(); 60 | candidateRelationships.setCandidateEndNodes(candidateRelationshipEndNodes); 61 | candidateRelationships.setCandidateRelationshipIndices(candidateRelationshipIndices); 62 | return candidateRelationships; 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/candidate/initialization/CandidateExplorer.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.candidate.initialization; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import se.simonevertsson.gpu.query.QueryContext; 7 | import se.simonevertsson.gpu.buffer.BufferContainer; 8 | import se.simonevertsson.gpu.buffer.DataBuffers; 9 | import se.simonevertsson.gpu.buffer.QueryBuffers; 10 | import se.simonevertsson.gpu.kernel.QueryKernels; 11 | 12 | import java.io.IOException; 13 | import java.nio.IntBuffer; 14 | 15 | public class CandidateExplorer { 16 | 17 | private final QueryKernels queryKernels; 18 | private final QueryBuffers queryBuffers; 19 | private final DataBuffers dataBuffers; 20 | private final QueryContext queryContext; 21 | 22 | public CandidateExplorer(QueryKernels queryKernels, BufferContainer bufferContainer, QueryContext queryContext) { 23 | this.queryKernels = queryKernels; 24 | this.queryBuffers = bufferContainer.queryBuffers; 25 | this.dataBuffers = bufferContainer.dataBuffers; 26 | this.queryContext = queryContext; 27 | } 28 | 29 | public void exploreCandidates(int queryNodeId, int[] candidateArray) throws IOException { 30 | int queryNodeAdjacencyIndexStart = this.queryContext.gpuQuery.getRelationshipIndices()[queryNodeId]; 31 | int queryNodeAdjacencyIndexEnd = this.queryContext.gpuQuery.getRelationshipIndices()[queryNodeId + 1]; 32 | 33 | CLBuffer candidatesArray 34 | = this.queryKernels.context.createIntBuffer(CLMem.Usage.Input, IntBuffer.wrap(candidateArray), true); 35 | int[] globalSizes = new int[]{candidateArray.length}; 36 | 37 | CLEvent exploreCandidatesEvent = this.queryKernels.exploreCandidatesKernel.explore_candidates( 38 | this.queryKernels.queue, 39 | queryNodeId, 40 | this.queryBuffers.queryNodeRelationshipsBuffer, 41 | this.queryBuffers.queryRelationshipTypesBuffer, 42 | this.queryBuffers.queryRelationshipIndicesBuffer, 43 | this.queryBuffers.queryNodeLabelsBuffer, 44 | this.queryBuffers.queryNodeLabelIndicesBuffer, 45 | queryNodeAdjacencyIndexStart, 46 | queryNodeAdjacencyIndexEnd, 47 | this.dataBuffers.dataNodeRelationshipsBuffer, 48 | this.dataBuffers.dataRelationshipTypesBuffer, 49 | this.dataBuffers.dataRelationshipIndicesBuffer, 50 | this.dataBuffers.dataLabelsBuffer, 51 | this.dataBuffers.dataLabelIndicesBuffer, 52 | candidatesArray, 53 | this.queryBuffers.candidateIndicatorsBuffer, 54 | this.queryContext.dataNodeCount, 55 | globalSizes, 56 | null 57 | ); 58 | 59 | this.queryBuffers.candidateIndicatorsPointer = 60 | this.queryBuffers.candidateIndicatorsBuffer.read(this.queryKernels.queue, exploreCandidatesEvent); 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/search/CandidateRelationshipCounter.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.search; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import org.bridj.Pointer; 7 | import se.simonevertsson.gpu.query.QueryUtils; 8 | import se.simonevertsson.gpu.buffer.BufferContainer; 9 | import se.simonevertsson.gpu.buffer.DataBuffers; 10 | import se.simonevertsson.gpu.buffer.QueryBuffers; 11 | import se.simonevertsson.gpu.kernel.QueryKernels; 12 | 13 | import java.io.IOException; 14 | import java.nio.IntBuffer; 15 | 16 | public class CandidateRelationshipCounter { 17 | 18 | private final QueryKernels queryKernels; 19 | private final QueryBuffers queryBuffers; 20 | private final DataBuffers dataBuffers; 21 | private final int dataNodeCount; 22 | 23 | 24 | public CandidateRelationshipCounter(QueryKernels queryKernels, BufferContainer bufferContainer, int dataNodeCount) { 25 | this.queryKernels = queryKernels; 26 | this.queryBuffers = bufferContainer.queryBuffers; 27 | this.dataBuffers = bufferContainer.dataBuffers; 28 | this.dataNodeCount = dataNodeCount; 29 | } 30 | 31 | public Pointer countCandidateRelationships(CandidateRelationships candidateRelationships) throws IOException { 32 | 33 | int[] candidateStartNodes = QueryUtils.gatherCandidateArray( 34 | this.queryBuffers.candidateIndicatorsPointer, 35 | this.dataNodeCount, 36 | (int) candidateRelationships.getQueryStartNodeId()); 37 | 38 | CLBuffer 39 | candidateStartNodesBuffer = this.queryKernels.context.createIntBuffer(CLMem.Usage.Input, IntBuffer.wrap(candidateStartNodes), true); 40 | 41 | CLBuffer candidateRelationshipCounts = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, candidateStartNodes.length); 42 | 43 | 44 | int[] globalSizes = new int[]{(int) candidateStartNodes.length}; 45 | 46 | CLEvent countRelationshipCandidatesEvent = this.queryKernels.countCandidateRelationshipsKernel.count_candidate_relationships( 47 | this.queryKernels.queue, 48 | candidateRelationships.getQueryStartNodeId(), 49 | candidateRelationships.getQueryEndNodeId(), 50 | candidateRelationships.getRelationshipType(), 51 | this.dataBuffers.dataNodeRelationshipsBuffer, 52 | this.dataBuffers.dataRelationshipTypesBuffer, 53 | this.dataBuffers.dataRelationshipIndicesBuffer, 54 | candidateRelationshipCounts, 55 | candidateStartNodesBuffer, 56 | this.queryBuffers.candidateIndicatorsBuffer, 57 | this.dataNodeCount, 58 | globalSizes, 59 | null 60 | ); 61 | 62 | candidateRelationships.setCandidateStartNodes(candidateStartNodesBuffer); 63 | 64 | 65 | return candidateRelationshipCounts.read(this.queryKernels.queue, countRelationshipCandidatesEvent); 66 | } 67 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/join/SolutionPruner.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.join; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import se.simonevertsson.gpu.query.QueryContext; 7 | import se.simonevertsson.gpu.kernel.QueryKernels; 8 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationships; 9 | 10 | import java.io.IOException; 11 | import java.nio.IntBuffer; 12 | 13 | public class SolutionPruner { 14 | private final QueryKernels queryKernels; 15 | private final QueryContext queryContext; 16 | 17 | public SolutionPruner(QueryKernels queryKernels, QueryContext queryContext) { 18 | this.queryKernels = queryKernels; 19 | this.queryContext = queryContext; 20 | } 21 | 22 | public PossibleSolutions prunePossibleSolutions(CandidateRelationships candidateRelationships, PossibleSolutions oldPossibleSolutions, CLBuffer validRelationships, int[] outputIndexArray) throws IOException { 23 | int possibleSolutionCount = (int) oldPossibleSolutions.getSolutionElements().getElementCount() / this.queryContext.queryNodeCount; 24 | 25 | CLBuffer outputIndices = this.queryKernels.context.createIntBuffer( 26 | CLMem.Usage.Input, 27 | IntBuffer.wrap(outputIndexArray), 28 | true); 29 | 30 | CLBuffer prunedPossibleSolutionElements = this.queryKernels.context.createIntBuffer( 31 | CLMem.Usage.Output, 32 | outputIndexArray[outputIndexArray.length - 1] * this.queryContext.queryNodeCount 33 | ); 34 | 35 | CLBuffer prunedPossibleSolutionRelationships = this.queryKernels.context.createIntBuffer( 36 | CLMem.Usage.Output, 37 | outputIndexArray[outputIndexArray.length - 1] * this.queryContext.queryRelationshipCount 38 | ); 39 | 40 | int relationshipId = this.queryContext.gpuQuery.getRelationshipIdDictionary() 41 | .getQueryId(candidateRelationships.getRelationship().getId()); 42 | 43 | int[] globalSizes = new int[]{possibleSolutionCount}; 44 | 45 | CLEvent pruneSolutionsEvent = this.queryKernels.pruneSolutionsKernel.prune_solutions( 46 | this.queryKernels.queue, 47 | this.queryContext.queryNodeCount, 48 | this.queryContext.queryRelationshipCount, 49 | relationshipId, 50 | 51 | oldPossibleSolutions.getSolutionElements(), 52 | oldPossibleSolutions.getSolutionRelationships(), 53 | validRelationships, 54 | outputIndices, 55 | prunedPossibleSolutionElements, 56 | prunedPossibleSolutionRelationships, 57 | globalSizes, 58 | null 59 | ); 60 | 61 | pruneSolutionsEvent.waitFor(); 62 | 63 | PossibleSolutions prunedPossibleSolutions = new PossibleSolutions(prunedPossibleSolutionElements, prunedPossibleSolutionRelationships, this.queryContext, this.queryKernels.queue); 64 | 65 | return prunedPossibleSolutions; 66 | } 67 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/candidate/refinement/CandidateRefinery.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.candidate.refinement; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import se.simonevertsson.gpu.query.QueryContext; 7 | import se.simonevertsson.gpu.buffer.BufferContainer; 8 | import se.simonevertsson.gpu.buffer.DataBuffers; 9 | import se.simonevertsson.gpu.buffer.QueryBuffers; 10 | import se.simonevertsson.gpu.kernel.QueryKernels; 11 | 12 | import java.io.IOException; 13 | import java.nio.IntBuffer; 14 | 15 | public class CandidateRefinery { 16 | private final QueryKernels queryKernels; 17 | private final QueryBuffers queryBuffers; 18 | private final DataBuffers dataBuffers; 19 | private final QueryContext queryContext; 20 | private final int dataNodeCount; 21 | 22 | public CandidateRefinery(QueryKernels queryKernels, BufferContainer bufferContainer, QueryContext queryContext) { 23 | this.queryKernels = queryKernels; 24 | this.queryBuffers = bufferContainer.queryBuffers; 25 | this.dataBuffers = bufferContainer.dataBuffers; 26 | this.queryContext = queryContext; 27 | this.dataNodeCount = queryContext.dataNodeCount; 28 | 29 | } 30 | 31 | public void refineCandidates(int queryNodeId, int[] candidateArray) throws IOException { 32 | int queryNodeAdjacencyIndexStart = this.queryContext.gpuQuery.getRelationshipIndices()[queryNodeId]; 33 | int queryNodeAdjacencyIndexEnd = this.queryContext.gpuQuery.getRelationshipIndices()[queryNodeId + 1]; 34 | 35 | IntBuffer candidatesArrayBuffer = IntBuffer.wrap(candidateArray); 36 | CLBuffer candidatesArray 37 | = this.queryKernels.context.createIntBuffer(CLMem.Usage.Input, candidatesArrayBuffer, true); 38 | int[] globalSizes = new int[]{candidateArray.length}; 39 | 40 | CLEvent refineCandidatesEvent = this.queryKernels.refineCandidatesKernel.refine_candidates( 41 | this.queryKernels.queue, 42 | queryNodeId, 43 | this.queryBuffers.queryNodeRelationshipsBuffer, 44 | this.queryBuffers.queryRelationshipTypesBuffer, 45 | this.queryBuffers.queryRelationshipIndicesBuffer, 46 | this.queryBuffers.queryNodeLabelsBuffer, 47 | this.queryBuffers.queryNodeLabelIndicesBuffer, 48 | queryNodeAdjacencyIndexStart, 49 | queryNodeAdjacencyIndexEnd, 50 | this.dataBuffers.dataNodeRelationshipsBuffer, 51 | this.dataBuffers.dataRelationshipTypesBuffer, 52 | this.dataBuffers.dataRelationshipIndicesBuffer, 53 | this.dataBuffers.dataLabelsBuffer, 54 | this.dataBuffers.dataLabelIndicesBuffer, 55 | candidatesArray, 56 | this.queryBuffers.candidateIndicatorsBuffer, 57 | this.queryContext.dataNodeCount, 58 | globalSizes, 59 | null 60 | ); 61 | 62 | this.queryBuffers.candidateIndicatorsPointer = this.queryBuffers.candidateIndicatorsBuffer.read(this.queryKernels.queue, refineCandidatesEvent); 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/runner/QueryGraph.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import org.neo4j.graphdb.Label; 4 | import org.neo4j.graphdb.Node; 5 | import org.neo4j.graphdb.Relationship; 6 | import se.simonevertsson.gpu.query.SpanningTree; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class QueryGraph { 11 | 12 | public ArrayList nodes = new ArrayList<>(); 13 | 14 | public ArrayList relationships = new ArrayList<>(); 15 | 16 | private SpanningTree spanningTree; 17 | 18 | public AliasDictionary aliasDictionary = new AliasDictionary(); 19 | 20 | public String toCypherQueryString() { 21 | StringBuilder builder = new StringBuilder(); 22 | builder.append(toCypherQueryStringPrefix()); 23 | builder.append(toCypherQueryStringSuffix()); 24 | return builder.toString(); 25 | } 26 | 27 | public String toCypherQueryStringPrefix() { 28 | StringBuilder builder = new StringBuilder(); 29 | builder.append("MATCH "); 30 | boolean firstRelationship = true; 31 | for (Relationship relationship : this.relationships) { 32 | String startNodeAlias = this.aliasDictionary.getAliasForId((int) relationship.getStartNode().getId()); 33 | String endNodeAlias = this.aliasDictionary.getAliasForId((int) relationship.getEndNode().getId()); 34 | if (!firstRelationship) { 35 | builder.append(", "); 36 | } else { 37 | firstRelationship = false; 38 | } 39 | builder.append("("); 40 | builder.append(startNodeAlias); 41 | for (Label label : relationship.getStartNode().getLabels()) { 42 | builder.append(':'); 43 | builder.append(label.name()); 44 | } 45 | builder.append(")-"); 46 | if (relationship.getType() != null) { 47 | builder.append("[:" + relationship.getType().name() + "]"); 48 | } 49 | builder.append("->"); 50 | builder.append("("); 51 | builder.append(endNodeAlias); 52 | for (Label label : relationship.getEndNode().getLabels()) { 53 | builder.append(':'); 54 | builder.append(label.name()); 55 | } 56 | builder.append(")"); 57 | } 58 | return builder.toString(); 59 | } 60 | 61 | public String toCypherQueryStringSuffix() { 62 | StringBuilder builder = new StringBuilder(); 63 | builder.append(" RETURN "); 64 | boolean firstAlias = true; 65 | for (String alias : this.aliasDictionary.getAllAliases()) { 66 | if (!firstAlias) { 67 | builder.append(", "); 68 | } else { 69 | firstAlias = false; 70 | } 71 | builder.append(alias); 72 | } 73 | builder.append(";"); 74 | return builder.toString(); 75 | } 76 | 77 | @Override 78 | public boolean equals(Object obj) { 79 | 80 | return obj instanceof QueryGraph && (((QueryGraph) obj).toCypherQueryString().equals(this.toCypherQueryString())); 81 | } 82 | 83 | public SpanningTree getSpanningTree() { 84 | return spanningTree; 85 | } 86 | 87 | public void setSpanningTree(SpanningTree spanningTree) { 88 | this.spanningTree = spanningTree; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/CountSolutionCombinations.cl: -------------------------------------------------------------------------------- 1 | __kernel void count_solution_combinations( 2 | int q_start_node, 3 | int q_end_node, 4 | int query_node_count, 5 | int query_relationship_count, 6 | __global int* possible_solution_elements, 7 | __global int* possible_solution_relationships, 8 | 9 | __global int* c_start_nodes, 10 | __global int* c_end_node_indices, 11 | __global int* c_end_nodes, 12 | __global int* c_relationship_indices, 13 | 14 | __global bool* start_node_visited, 15 | int start_node_count, 16 | 17 | __global int* combination_counts 18 | ) 19 | { 20 | int possible_solution_index = get_global_id(0); 21 | combination_counts[possible_solution_index] = 0; 22 | int count = 0; 23 | 24 | /* The START node of the query edge is the visited node */ 25 | if(start_node_visited[0]) { 26 | for(int i = 0; i < start_node_count; i++) { 27 | if(c_start_nodes[i] == possible_solution_elements[possible_solution_index*query_node_count + q_start_node]) { 28 | int end_node_index_start = c_end_node_indices[i]; 29 | int end_node_index_end = c_end_node_indices[i+1]; 30 | for(int j = end_node_index_start; j < end_node_index_end; j++) { 31 | int relationship_index = c_relationship_indices[j]; 32 | bool relationship_valid = true; 33 | for(int k = 0; k < query_relationship_count; k++) { 34 | if(possible_solution_relationships[possible_solution_index*query_relationship_count + k] == relationship_index) { 35 | relationship_valid = false; 36 | break; 37 | } 38 | } 39 | count += (relationship_valid ? 1 : 0); 40 | } 41 | } 42 | } 43 | } else{ 44 | /* The END node of the query edge is the visited node */ 45 | for(int i = 0; i < start_node_count; i++) { 46 | int end_node_index_start = c_end_node_indices[i]; 47 | int end_node_index_end = c_end_node_indices[i+1]; 48 | for(int j = end_node_index_start; j < end_node_index_end; j++) { 49 | //count += (c_end_nodes[j] == possible_solution_elements[possible_solution_index*query_node_count + q_end_node]) ? 1 : 0; 50 | if(c_end_nodes[j] == possible_solution_elements[possible_solution_index*query_node_count + q_end_node]) { 51 | int relationship_index = c_relationship_indices[j]; 52 | bool relationship_valid = true; 53 | for(int k = 0; k < query_relationship_count; k++) { 54 | if(possible_solution_relationships[possible_solution_index*query_relationship_count + k] == relationship_index) { 55 | relationship_valid = false; 56 | break; 57 | } 58 | } 59 | count += (relationship_valid ? 1 : 0); 60 | } 61 | } 62 | } 63 | } 64 | 65 | combination_counts[possible_solution_index] = count; 66 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/search/CandidateRelationshipSearcher.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.search; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLMem; 5 | import org.bridj.Pointer; 6 | import org.neo4j.graphdb.Relationship; 7 | import se.simonevertsson.gpu.buffer.BufferContainer; 8 | import se.simonevertsson.gpu.buffer.QueryBuffers; 9 | import se.simonevertsson.gpu.kernel.QueryKernels; 10 | import se.simonevertsson.gpu.query.QueryContext; 11 | import se.simonevertsson.gpu.query.QueryUtils; 12 | 13 | import java.io.IOException; 14 | import java.nio.IntBuffer; 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | 18 | public class CandidateRelationshipSearcher { 19 | 20 | private final QueryContext queryContext; 21 | private final QueryBuffers queryBuffers; 22 | private final int dataNodeCount; 23 | private final CandidateRelationshipCounter candidateRelationshipCounter; 24 | private final QueryKernels queryKernels; 25 | private final CandidateRelationshipFinder candidateRelationshipFinder; 26 | 27 | public CandidateRelationshipSearcher(QueryContext queryContext, QueryKernels queryKernels, BufferContainer bufferContainer) { 28 | this.queryBuffers = bufferContainer.queryBuffers; 29 | this.queryContext = queryContext; 30 | this.queryKernels = queryKernels; 31 | this.dataNodeCount = queryContext.dataNodeCount; 32 | this.candidateRelationshipCounter = new CandidateRelationshipCounter(queryKernels, bufferContainer, this.dataNodeCount); 33 | this.candidateRelationshipFinder = new CandidateRelationshipFinder(queryKernels, bufferContainer, this.dataNodeCount); 34 | } 35 | 36 | public HashMap searchCandidateRelationships() throws IOException { 37 | ArrayList relationships = this.queryContext.queryGraph.relationships; 38 | HashMap candidateRelationshipsHashMap = new HashMap<>(); 39 | 40 | for (Relationship relationship : relationships) { 41 | CandidateRelationships candidateRelationships = new CandidateRelationships(relationship, this.queryContext, this.queryKernels); 42 | 43 | Pointer candidateRelationshipCountsPointer = this.candidateRelationshipCounter.countCandidateRelationships(candidateRelationships); 44 | 45 | int[] candidateRelationshipEndNodeIndices = QueryUtils.generatePrefixScanArray(candidateRelationshipCountsPointer, candidateRelationships.getStartNodeCount()); 46 | CLBuffer 47 | candidateRelationshipEndNodeIndicesBuffer = this.queryKernels.context.createIntBuffer(CLMem.Usage.Input, IntBuffer.wrap(candidateRelationshipEndNodeIndices), true); 48 | 49 | candidateRelationships.setCandidateEndNodeIndices(candidateRelationshipEndNodeIndicesBuffer); 50 | candidateRelationships.setEndNodeCount(candidateRelationshipEndNodeIndices[candidateRelationshipEndNodeIndices.length - 1]); 51 | 52 | this.candidateRelationshipFinder.findCandidateRelationships(candidateRelationships); 53 | 54 | candidateRelationshipsHashMap.put((int) relationship.getId(), candidateRelationships); 55 | 56 | } 57 | 58 | return candidateRelationshipsHashMap; 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/candidate/initialization/CandidateInitializer.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.candidate.initialization; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import org.neo4j.graphdb.Relationship; 5 | import se.simonevertsson.gpu.query.QueryContext; 6 | import se.simonevertsson.gpu.query.QueryUtils; 7 | import se.simonevertsson.gpu.buffer.BufferContainer; 8 | import se.simonevertsson.gpu.buffer.QueryBuffers; 9 | import se.simonevertsson.gpu.kernel.QueryKernels; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | public class CandidateInitializer { 15 | private final QueryContext queryContext; 16 | private final QueryBuffers queryBuffers; 17 | private final CandidateChecker candidateChecker; 18 | private final CandidateExplorer candidateExplorer; 19 | 20 | 21 | public CandidateInitializer(QueryContext queryContext, QueryKernels queryKernels, BufferContainer bufferContainer) { 22 | this.queryContext = queryContext; 23 | this.queryBuffers = bufferContainer.queryBuffers; 24 | this.candidateChecker = new CandidateChecker(queryContext, queryKernels, bufferContainer, this.queryContext.dataNodeCount); 25 | this.candidateExplorer = new CandidateExplorer(queryKernels, bufferContainer, this.queryContext); 26 | } 27 | 28 | public void candidateInitialization(List visitOrder) throws IOException { 29 | boolean initializedQueryNodes[] = new boolean[this.queryContext.queryNodeCount]; 30 | for (Node queryNode : visitOrder) { 31 | 32 | int queryNodeId = this.queryContext.gpuQuery.getNodeIdDictionary().getQueryId(queryNode.getId()); 33 | 34 | if (!initializedQueryNodes[queryNodeId]) { 35 | this.candidateChecker.checkCandidates(this.queryContext.gpuQuery, queryNode); 36 | } 37 | 38 | int queryNodeRelationshipStartIndex = this.queryContext.gpuQuery.getRelationshipIndices()[queryNodeId]; 39 | int queryNodeRelationshipEndIndex = this.queryContext.gpuQuery.getRelationshipIndices()[queryNodeId + 1]; 40 | if (queryNodeRelationshipStartIndex != queryNodeRelationshipEndIndex) { 41 | 42 | // The current runner node has relationships, hence we explore the neighborhood 43 | int[] candidateArray = QueryUtils.gatherCandidateArray( 44 | this.queryBuffers.candidateIndicatorsPointer, 45 | this.queryContext.dataNodeCount, 46 | queryNodeId); 47 | 48 | if (candidateArray.length > 0) { 49 | this.candidateExplorer.exploreCandidates(queryNodeId, candidateArray); 50 | 51 | // We have explored the neighborhood, mark runner nodes related to the current runner node as initialized 52 | for (Relationship neighborhoodRelationship : queryNode.getRelationships()) { 53 | int neighborQueryNodeId = this.queryContext.gpuQuery.getNodeIdDictionary() 54 | .getQueryId(neighborhoodRelationship.getEndNode().getId()); 55 | ; 56 | initializedQueryNodes[neighborQueryNodeId] = true; 57 | } 58 | } else { 59 | throw new IllegalStateException("No candidates for runner node " + queryNodeId + " were found."); 60 | } 61 | } 62 | 63 | initializedQueryNodes[queryNodeId] = true; 64 | 65 | 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/join/SolutionCombinationGenerator.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.join; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import com.nativelibs4java.opencl.CLEvent; 5 | import com.nativelibs4java.opencl.CLMem; 6 | import org.bridj.Pointer; 7 | import se.simonevertsson.gpu.query.QueryContext; 8 | import se.simonevertsson.gpu.kernel.QueryKernels; 9 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationships; 10 | 11 | import java.io.IOException; 12 | import java.nio.IntBuffer; 13 | 14 | public class SolutionCombinationGenerator { 15 | private final QueryKernels queryKernels; 16 | private final QueryContext queryContext; 17 | 18 | public SolutionCombinationGenerator(QueryKernels queryKernels, QueryContext queryContext) { 19 | this.queryKernels = queryKernels; 20 | this.queryContext = queryContext; 21 | } 22 | 23 | public PossibleSolutions generateSolutionCombinations(PossibleSolutions oldPossibleSolutions, CandidateRelationships candidateRelationships, boolean startNodeVisited, int[] combinationIndices) throws IOException { 24 | int oldPossibleSolutionCount = (int) oldPossibleSolutions.getSolutionElements().getElementCount() / this.queryContext.queryNodeCount; 25 | 26 | int totalCombinationCount = combinationIndices[combinationIndices.length - 1]; 27 | 28 | int[] globalSizes = new int[]{oldPossibleSolutionCount}; 29 | 30 | CLBuffer startNodeVisitedBuffer = this.queryKernels.context.createBuffer( 31 | CLMem.Usage.Input, 32 | Pointer.pointerToBooleans(startNodeVisited), true); 33 | 34 | CLBuffer 35 | combinationIndicesBuffer = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, IntBuffer.wrap(combinationIndices), true), 36 | newPossibleSolutionElements = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, totalCombinationCount * this.queryContext.queryNodeCount), 37 | newPossibleSolutionRelationships = this.queryKernels.context.createIntBuffer(CLMem.Usage.Output, totalCombinationCount * this.queryContext.queryRelationshipCount); 38 | 39 | int relationshipId = this.queryContext.gpuQuery.getRelationshipIdDictionary() 40 | .getQueryId(candidateRelationships.getRelationship().getId()); 41 | 42 | CLEvent generateSolutionCombinationsEvent = this.queryKernels.generateSolutionCombinationsKernel.generate_solution_combinations( 43 | this.queryKernels.queue, 44 | candidateRelationships.getQueryStartNodeId(), 45 | candidateRelationships.getQueryEndNodeId(), 46 | relationshipId, 47 | this.queryContext.queryNodeCount, 48 | this.queryContext.queryRelationshipCount, 49 | oldPossibleSolutions.getSolutionElements(), 50 | oldPossibleSolutions.getSolutionRelationships(), 51 | combinationIndicesBuffer, 52 | candidateRelationships.getCandidateStartNodes(), 53 | candidateRelationships.getCandidateEndNodeIndices(), 54 | candidateRelationships.getCandidateEndNodes(), 55 | candidateRelationships.getRelationshipIndices(), 56 | startNodeVisitedBuffer, 57 | candidateRelationships.getStartNodeCount(), 58 | newPossibleSolutionElements, 59 | newPossibleSolutionRelationships, 60 | globalSizes, 61 | null 62 | ); 63 | generateSolutionCombinationsEvent.waitFor(); 64 | 65 | PossibleSolutions newPossibleSolutions = new PossibleSolutions(newPossibleSolutionElements, newPossibleSolutionRelationships, this.queryContext, this.queryKernels.queue); 66 | 67 | return newPossibleSolutions; 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/buffer/BufferContainerGenerator.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.buffer; 2 | 3 | import com.nativelibs4java.opencl.CLMem; 4 | import org.bridj.Pointer; 5 | import se.simonevertsson.gpu.graph.GpuGraph; 6 | import se.simonevertsson.gpu.kernel.QueryKernels; 7 | import se.simonevertsson.gpu.query.QueryContext; 8 | 9 | import java.nio.IntBuffer; 10 | 11 | public class BufferContainerGenerator { 12 | 13 | public static BufferContainer generateBufferContainer(QueryContext queryContext, QueryKernels queryKernels) { 14 | DataBuffers dataBuffers = createDataBuffers(queryKernels, queryContext); 15 | QueryBuffers queryBuffers = createQueryBuffers(queryKernels, queryContext); 16 | return new BufferContainer(dataBuffers, queryBuffers); 17 | } 18 | 19 | private static DataBuffers createDataBuffers(QueryKernels queryKernels, QueryContext queryContext) { 20 | GpuGraph data = queryContext.gpuData; 21 | IntBuffer dataRelationshipsBuffer = IntBuffer.wrap(data.getNodeRelationships()); 22 | IntBuffer dataRelationshipTypesBuffer = IntBuffer.wrap(data.getRelationshipTypes()); 23 | IntBuffer dataRelationshipIndicesBuffer = IntBuffer.wrap(data.getRelationshipIndices()); 24 | IntBuffer dataLabelsBuffer = IntBuffer.wrap(data.getNodeLabels()); 25 | IntBuffer dataLabelIndexBuffer = IntBuffer.wrap(data.getLabelIndices()); 26 | 27 | DataBuffers dataBuffers = new DataBuffers(); 28 | 29 | dataBuffers.dataNodeRelationshipsBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, dataRelationshipsBuffer, true); 30 | dataBuffers.dataRelationshipTypesBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, dataRelationshipTypesBuffer, true); 31 | dataBuffers.dataRelationshipIndicesBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, dataRelationshipIndicesBuffer, true); 32 | dataBuffers.dataLabelsBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, dataLabelsBuffer, true); 33 | dataBuffers.dataLabelIndicesBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, dataLabelIndexBuffer, true); 34 | 35 | 36 | return dataBuffers; 37 | } 38 | 39 | 40 | private static QueryBuffers createQueryBuffers(QueryKernels queryKernels, QueryContext queryContext) { 41 | GpuGraph query = queryContext.gpuQuery; 42 | 43 | IntBuffer queryNodeRelationshipsBuffer = IntBuffer.wrap(query.getNodeRelationships()); 44 | IntBuffer queryRelationshipTypesBuffer = IntBuffer.wrap(query.getRelationshipTypes()); 45 | IntBuffer queryRelationshipIndicesBuffer = IntBuffer.wrap(query.getRelationshipIndices()); 46 | IntBuffer queryNodeLabelsBuffer = IntBuffer.wrap(query.getNodeLabels()); 47 | IntBuffer queryNodeLabelIndicesBuffer = IntBuffer.wrap(query.getLabelIndices()); 48 | 49 | boolean candidateIndicators[] = new boolean[queryContext.dataNodeCount * queryContext.queryNodeCount]; 50 | 51 | QueryBuffers queryBuffers = new QueryBuffers(); 52 | 53 | queryBuffers.queryNodeLabelsBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, queryNodeLabelsBuffer, true); 54 | queryBuffers.queryNodeLabelIndicesBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, queryNodeLabelIndicesBuffer, true); 55 | queryBuffers.queryNodeRelationshipsBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, queryNodeRelationshipsBuffer, true); 56 | queryBuffers.queryRelationshipTypesBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, queryRelationshipTypesBuffer, true); 57 | queryBuffers.queryRelationshipIndicesBuffer = queryKernels.context.createIntBuffer(CLMem.Usage.Input, queryRelationshipIndicesBuffer, true); 58 | 59 | queryBuffers.candidateIndicatorsBuffer = queryKernels.context.createBuffer( 60 | CLMem.Usage.Output, 61 | Pointer.pointerToBooleans(candidateIndicators), true); 62 | 63 | return queryBuffers; 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/GpuQuery.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import se.simonevertsson.gpu.buffer.BufferContainer; 5 | import se.simonevertsson.gpu.buffer.BufferContainerGenerator; 6 | import se.simonevertsson.gpu.kernel.QueryKernels; 7 | import se.simonevertsson.gpu.query.candidate.initialization.CandidateInitializer; 8 | import se.simonevertsson.gpu.query.relationship.join.CandidateRelationshipJoiner; 9 | import se.simonevertsson.gpu.query.relationship.join.PossibleSolutions; 10 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationshipSearcher; 11 | import se.simonevertsson.gpu.query.relationship.search.CandidateRelationships; 12 | 13 | import java.io.IOException; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | 17 | /** 18 | * This class excutes graph queries on the GPU using the given {@link QueryContext}. 19 | */ 20 | 21 | public class GpuQuery { 22 | private QueryContext queryContext; 23 | private final QueryKernels queryKernels; 24 | private BufferContainer bufferContainer; 25 | 26 | public GpuQuery(QueryContext queryContext) throws IOException { 27 | this.queryContext = queryContext; 28 | this.queryKernels = new QueryKernels(); 29 | this.bufferContainer = BufferContainerGenerator.generateBufferContainer(this.queryContext, this.queryKernels); 30 | } 31 | 32 | public GpuQuery(QueryContext queryContext, QueryKernels queryKernels) throws IOException { 33 | this.queryContext = queryContext; 34 | this.queryKernels = queryKernels; 35 | this.bufferContainer = BufferContainerGenerator.generateBufferContainer(this.queryContext, this.queryKernels); 36 | } 37 | 38 | public List executeQuery(List visitOrder) throws IOException { 39 | /****** Candidate initialization step ******/ 40 | CandidateInitializer candidateInitializer = 41 | new CandidateInitializer(this.queryContext, this.queryKernels, this.bufferContainer); 42 | candidateInitializer.candidateInitialization(visitOrder); 43 | 44 | /****** Candidate refinement step (Possibly not necessary) ******/ 45 | // CandidateRefinement candidateRefinement = 46 | // new CandidateRefinement(this.queryContext, this.queryKernels, this.bufferContainer); 47 | // candidateRefinement.refine(visitOrder); 48 | 49 | /****** Candidate relationship searching step ******/ 50 | CandidateRelationshipSearcher candidateRelationshipSearcher = 51 | new CandidateRelationshipSearcher(this.queryContext, this.queryKernels, this.bufferContainer); 52 | HashMap relationshipCandidatesHashMap = candidateRelationshipSearcher.searchCandidateRelationships(); 53 | 54 | // Release buffers which we don't need anymore 55 | this.bufferContainer.queryBuffers.candidateIndicatorsBuffer.release(); 56 | this.bufferContainer.queryBuffers.candidateIndicatorsPointer.release(); 57 | this.bufferContainer.dataBuffers.dataLabelIndicesBuffer.release(); 58 | this.bufferContainer.dataBuffers.dataLabelsBuffer.release(); 59 | this.bufferContainer.dataBuffers.dataRelationshipIndicesBuffer.release(); 60 | this.bufferContainer.dataBuffers.dataNodeRelationshipsBuffer.release(); 61 | this.bufferContainer.dataBuffers.dataRelationshipTypesBuffer.release(); 62 | 63 | /****** Candidate relationship joining step ******/ 64 | CandidateRelationshipJoiner candidateRelationshipJoiner = 65 | new CandidateRelationshipJoiner(this.queryContext, this.queryKernels, this.bufferContainer); 66 | PossibleSolutions solutions = candidateRelationshipJoiner.joinCandidateRelationships(relationshipCandidatesHashMap); 67 | 68 | for (int key : relationshipCandidatesHashMap.keySet()) { 69 | relationshipCandidatesHashMap.get(key).release(); 70 | } 71 | 72 | return QueryUtils.generateQuerySolutions(this.queryKernels, this.queryContext, solutions); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/runner/GpuQueryRunner.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.runner; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import se.simonevertsson.db.DatabaseService; 5 | import se.simonevertsson.gpu.graph.GpuGraph; 6 | import se.simonevertsson.gpu.graph.GpuGraphConverter; 7 | import se.simonevertsson.gpu.query.*; 8 | import se.simonevertsson.gpu.kernel.QueryKernels; 9 | import se.simonevertsson.gpu.dictionary.LabelDictionary; 10 | import se.simonevertsson.gpu.dictionary.TypeDictionary; 11 | 12 | import java.io.IOException; 13 | import java.util.List; 14 | 15 | public class GpuQueryRunner { 16 | 17 | public QueryResult runGpuQuery(DatabaseService databaseService, QueryGraph queryGraph, QueryKernels queryKernels) throws IOException { 18 | long tick, tock; 19 | 20 | 21 | tick = System.currentTimeMillis(); 22 | 23 | // Convert database data and runner data to fit the GPU 24 | LabelDictionary labelDictionary = new LabelDictionary(); 25 | TypeDictionary typeDictionary = new TypeDictionary(); 26 | GpuGraph gpuData = convertData(databaseService, labelDictionary, typeDictionary); 27 | GpuGraph gpuQuery = convertQuery(queryGraph, labelDictionary, typeDictionary); 28 | tock = System.currentTimeMillis(); 29 | long conversionExecutionTime = tock - tick; 30 | 31 | tick = System.currentTimeMillis(); 32 | 33 | // Execute the runner 34 | QueryContext queryContext = new QueryContext(gpuData, gpuQuery, queryGraph, labelDictionary, typeDictionary); 35 | GpuQuery gpuGraphQuery = new GpuQuery(queryContext, queryKernels); 36 | List solutions = gpuGraphQuery.executeQuery(queryGraph.getSpanningTree().getVisitOrder()); 37 | 38 | tock = System.currentTimeMillis(); 39 | long queryExecutionTime = tock - tick; 40 | 41 | return new QueryResult(solutions, conversionExecutionTime, queryExecutionTime); 42 | } 43 | 44 | public List runGpuQuery(QueryGraph dataGraph, QueryGraph queryGraph) throws IOException { 45 | long tick, tock; 46 | 47 | 48 | tick = System.currentTimeMillis(); 49 | 50 | // Convert database data and runner data to fit the GPU 51 | LabelDictionary labelDictionary = new LabelDictionary(); 52 | TypeDictionary typeDictionary = new TypeDictionary(); 53 | GpuGraph gpuData = convertData(dataGraph, labelDictionary, typeDictionary); 54 | GpuGraph gpuQuery = convertQuery(queryGraph, labelDictionary, typeDictionary); 55 | 56 | tock = System.currentTimeMillis(); 57 | System.out.println("GPU Data conversion runtime: " + (tock - tick) + "ms"); 58 | 59 | tick = System.currentTimeMillis(); 60 | 61 | // Execute the runner 62 | QueryContext queryContext = new QueryContext(gpuData, gpuQuery, queryGraph, labelDictionary, typeDictionary); 63 | GpuQuery gpuGraphQuery = new GpuQuery(queryContext); 64 | List results = gpuGraphQuery.executeQuery(queryGraph.getSpanningTree().getVisitOrder()); 65 | 66 | tock = System.currentTimeMillis(); 67 | System.out.println("GPU Query runtime: " + (tock - tick) + "ms"); 68 | 69 | return results; 70 | } 71 | 72 | private GpuGraph convertQuery(QueryGraph queryGraph, LabelDictionary labelDictionary, TypeDictionary typeDictionary) { 73 | SpanningTreeGenerator spanningTreeGenerator = new SpanningTreeGenerator(); 74 | SpanningTree spanningTree = spanningTreeGenerator.generate(queryGraph); 75 | queryGraph.setSpanningTree(spanningTree); 76 | 77 | GpuGraphConverter gpuGraphConverter = new GpuGraphConverter(queryGraph.nodes, labelDictionary, typeDictionary); 78 | return gpuGraphConverter.convert(); 79 | } 80 | 81 | private GpuGraph convertData(DatabaseService databaseService, LabelDictionary labelDictionary, TypeDictionary typeDictionary) { 82 | List allNodes = databaseService.getAllNodes(); 83 | GpuGraphConverter gpuGraphConverter = new GpuGraphConverter(allNodes, labelDictionary, typeDictionary); 84 | return gpuGraphConverter.convert(); 85 | } 86 | 87 | private GpuGraph convertData(QueryGraph dataGraph, LabelDictionary labelDictionary, TypeDictionary typeDictionary) { 88 | List allNodes = dataGraph.nodes; 89 | GpuGraphConverter gpuGraphConverter = new GpuGraphConverter(allNodes, labelDictionary, typeDictionary); 90 | return gpuGraphConverter.convert(); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/opencl/se/simonevertsson/RefineCandidates.cl: -------------------------------------------------------------------------------- 1 | bool is_valid_related_node( 2 | int d_related_node_degree, 3 | __global int* d_labels, 4 | int d_label_index_start, 5 | int d_label_index_end, 6 | 7 | __global int* q_labels, 8 | int q_label_index_start, 9 | int q_label_index_end, 10 | int q_related_node_degree) { 11 | 12 | 13 | if(q_labels[q_label_index_start] != -1) { 14 | 15 | for(int i = q_label_index_start; i < q_label_index_end; i++) { 16 | 17 | /* Verify that the related node has the current query label */ 18 | int query_label = q_labels[i]; 19 | bool label_exists = false; 20 | for(int m = d_label_index_start; m < d_label_index_end; m++) { 21 | if(d_labels[m] == query_label) { 22 | label_exists = true; 23 | break; 24 | } 25 | } 26 | 27 | if(!label_exists) { 28 | /* No match for given query label was found */ 29 | return false; 30 | } 31 | } 32 | 33 | } 34 | 35 | return q_related_node_degree <= d_related_node_degree; 36 | } 37 | 38 | __kernel void refine_candidates( 39 | int q_node, 40 | __global int* q_relationships, 41 | __global int* q_relationship_types, 42 | __global int* q_relationship_indices, 43 | __global int* q_labels, 44 | __global int* q_label_indices, 45 | int q_relationship_start_index, 46 | int q_relationship_end_index, 47 | 48 | __global int* d_relationships, 49 | __global int* d_relationship_types, 50 | __global int* d_relationship_indices, 51 | __global int* d_labels, 52 | __global int* d_label_indices, 53 | 54 | __global int* candidates_array, 55 | __global bool* candidate_indicators, 56 | int d_node_count) 57 | { 58 | 59 | int candidate_node = candidates_array[get_global_id(0)]; 60 | 61 | int c_relationship_start_index = d_relationship_indices[candidate_node]; 62 | int c_relationship_end_index = d_relationship_indices[candidate_node+1]; 63 | 64 | if(q_relationships[q_relationship_start_index] != -1) { 65 | /* The query node has relationships */ 66 | for(int i = q_relationship_start_index; i < q_relationship_end_index; i++) { 67 | int u = q_relationships[i]; 68 | int q_relationship_type = q_relationship_types[i]; 69 | 70 | int q_node_degree = q_relationships[ q_relationship_indices[u]] != -1 ? q_relationship_indices[u+1] - q_relationship_indices[u] : 0; 71 | int q_label_index_start = q_label_indices[u]; 72 | int q_label_index_end = q_label_indices[u+1]; 73 | 74 | bool match_found = false; 75 | 76 | for(int j = c_relationship_start_index; j < c_relationship_end_index; j++) { 77 | int v = d_relationships[j]; 78 | if(v != -1) { 79 | /* The related data node has relationships */ 80 | if(q_relationships[q_relationship_start_index] != -1) { 81 | int d_relationship_type = d_relationship_types[j]; 82 | if(q_relationship_type == -1 || d_relationship_type == q_relationship_type ) { 83 | 84 | 85 | int d_node_degree = d_relationships[d_relationship_indices[v]] != -1 ? (d_relationship_indices[v+1] - d_relationship_indices[v]) : 0; 86 | int d_label_index_start = d_label_indices[v]; 87 | int d_label_index_end = d_label_indices[v+1]; 88 | 89 | if(is_valid_related_node(d_node_degree, d_labels, d_label_index_start, d_label_index_end, q_labels, q_label_index_start, q_label_index_end, q_node_degree)) { 90 | match_found = true; 91 | break; 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | if(!match_found) { 99 | candidate_indicators[ q_node*d_node_count + candidate_node ] = false; 100 | return; 101 | } 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/db/DatabaseService.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.db; 2 | 3 | import org.neo4j.graphdb.*; 4 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 5 | import org.neo4j.tooling.GlobalGraphOperations; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Service class which handles database connections and queries 12 | */ 13 | public class DatabaseService { 14 | 15 | private final String databasePath; 16 | private GlobalGraphOperations graphOperations; 17 | private GraphDatabaseService graphDatabase; 18 | 19 | /** 20 | * Creates a new connection to a Neo4j database 21 | * 22 | * @param databasePath The absolute path to the the database directory. 23 | * @param configPath The absolute path to the database configuration file. 24 | */ 25 | public DatabaseService(String databasePath, String configPath) { 26 | this.databasePath = databasePath; 27 | this.graphDatabase = new GraphDatabaseFactory() 28 | .newEmbeddedDatabaseBuilder(databasePath) 29 | .loadPropertiesFromFile(configPath) 30 | .newGraphDatabase(); 31 | this.graphOperations = GlobalGraphOperations.at(this.graphDatabase); 32 | registerShutdownHook(this.graphDatabase); 33 | } 34 | 35 | private void registerShutdownHook(final GraphDatabaseService graphDb) { 36 | // Registers a shutdown hook for the Neo4j instance so that it 37 | // shuts down nicely when the VM exits (even if you "Ctrl-C" the 38 | // running application). 39 | Runtime.getRuntime().addShutdownHook(new Thread() { 40 | @Override 41 | public void run() { 42 | graphDb.shutdown(); 43 | } 44 | }); 45 | } 46 | 47 | private boolean isConnected() { 48 | return graphDatabase != null; 49 | } 50 | 51 | /** 52 | * Retrieves all nodes in the currently connected database 53 | * 54 | * @return A list of all nodes in the database 55 | */ 56 | public List getAllNodes() { 57 | ResourceIterable result = null; 58 | ArrayList allNodes = new ArrayList<>(); 59 | if (isConnected()) { 60 | try { 61 | Transaction tx = this.graphDatabase.beginTx(); 62 | result = this.graphOperations.getAllNodes(); 63 | tx.success(); 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | } 67 | for (Node node : result) { 68 | allNodes.add(node); 69 | } 70 | } 71 | 72 | return allNodes; 73 | } 74 | 75 | /** 76 | * Retrieves all relationships in the currently connected database 77 | * 78 | * @return A list of all relationships in the database 79 | */ 80 | public ArrayList getAllRelationships() { 81 | Iterable result = null; 82 | ArrayList allRelationships = new ArrayList<>(); 83 | if (isConnected()) { 84 | try { 85 | Transaction tx = this.graphDatabase.beginTx(); 86 | result = this.graphOperations.getAllRelationships(); 87 | tx.success(); 88 | } catch (Exception e) { 89 | e.printStackTrace(); 90 | } 91 | for (Relationship relationship : result) { 92 | allRelationships.add(relationship); 93 | } 94 | } 95 | 96 | return allRelationships; 97 | } 98 | 99 | public Transaction beginTx() { 100 | return this.graphDatabase.beginTx(); 101 | } 102 | 103 | /** 104 | * Executes the supplied Cypher runner on the currently conected database. Remember to call {@link DatabaseService#beginTx()} 105 | * before executing queries, or Neo4j will throw an exception. 106 | * 107 | * @param query The Cypher runner which will be executed. 108 | * @return The result of the runner 109 | */ 110 | public Result excuteCypherQuery(String query) { 111 | Result result = null; 112 | if (isConnected()) { 113 | result = this.graphDatabase.execute(query); 114 | } 115 | 116 | return result; 117 | } 118 | 119 | /** 120 | * Terminates the current database connection 121 | */ 122 | public void shutdown() { 123 | this.graphDatabase.shutdown(); 124 | this.graphDatabase = null; 125 | } 126 | 127 | public String getDatabaseName() { 128 | String databaseName = null; 129 | if(databasePath != null && (databasePath.lastIndexOf("/") >= 0 || databasePath.lastIndexOf("\\") >= 0)) { 130 | String separator = databasePath.lastIndexOf("/") > databasePath.lastIndexOf("\\") ? "/" : "\\"; 131 | databaseName = databasePath.substring(databasePath.lastIndexOf(separator) + 1); 132 | } 133 | 134 | return databaseName; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/SpanningTreeGenerator.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import org.neo4j.graphdb.Direction; 4 | import org.neo4j.graphdb.Label; 5 | import org.neo4j.graphdb.Node; 6 | import org.neo4j.graphdb.Relationship; 7 | import se.simonevertsson.runner.QueryGraph; 8 | 9 | import java.util.*; 10 | 11 | public class SpanningTreeGenerator { 12 | 13 | private HashMap visitedNodes; 14 | private List spanningTreeRelationships; 15 | private List spanningTreeVisitOrder; 16 | 17 | 18 | public SpanningTree generate(QueryGraph queryGraph) { 19 | this.spanningTreeRelationships = new ArrayList<>(); 20 | this.spanningTreeVisitOrder = new ArrayList<>(); 21 | this.visitedNodes = new HashMap<>(); 22 | 23 | Relationship initialRelationShip = findInitialRelationship(queryGraph.relationships); 24 | this.spanningTreeVisitOrder.add(initialRelationShip.getStartNode()); 25 | visitedNodes.put(initialRelationShip.getStartNode().getId(), true); 26 | 27 | while (this.spanningTreeVisitOrder.size() < queryGraph.nodes.size()) { 28 | addNextLevelOfNodes(queryGraph); 29 | } 30 | 31 | return new SpanningTree(this.spanningTreeRelationships, this.spanningTreeVisitOrder); 32 | } 33 | 34 | private void addNextLevelOfNodes(QueryGraph queryGraph) { 35 | for (Relationship relationship : queryGraph.relationships) { 36 | long currentStartId = relationship.getStartNode().getId(); 37 | long currentEndId = relationship.getEndNode().getId(); 38 | if (visitedNodes.containsKey(currentStartId) && !visitedNodes.containsKey(currentEndId)) { 39 | addNodeToSpanningTree(relationship, relationship.getEndNode()); 40 | } else if (!visitedNodes.containsKey(currentStartId) && visitedNodes.containsKey(currentEndId)) { 41 | addNodeToSpanningTree(relationship, relationship.getStartNode()); 42 | } 43 | } 44 | } 45 | 46 | private void addNodeToSpanningTree(Relationship relationship, Node node) { 47 | this.spanningTreeRelationships.add(relationship); 48 | this.spanningTreeVisitOrder.add(node); 49 | this.visitedNodes.put(node.getId(), true); 50 | } 51 | 52 | private static Relationship findInitialRelationship(ArrayList queryGraphRelationships) { 53 | HashMap labelCounter = countLabels(queryGraphRelationships); 54 | float maxEstimateSum = 0; 55 | Relationship initialRelationship = null; 56 | 57 | for (Relationship relationship : queryGraphRelationships) { 58 | float startEstimate = calculateNodeRankEstimate(labelCounter, relationship.getStartNode()); 59 | float endEstimate = calculateNodeRankEstimate(labelCounter, relationship.getEndNode()); 60 | if (startEstimate >= endEstimate && (startEstimate + endEstimate) >= maxEstimateSum) { 61 | initialRelationship = relationship; 62 | maxEstimateSum = (startEstimate + endEstimate); 63 | } 64 | } 65 | 66 | return initialRelationship; 67 | } 68 | 69 | private static HashMap countLabels(ArrayList queryGraphRelationships) { 70 | HashMap labelCounter = new HashMap(); 71 | HashSet visitedNodes = new HashSet(); 72 | for (Relationship relationship : queryGraphRelationships) { 73 | Node startNode = relationship.getStartNode(); 74 | Node endNode = relationship.getEndNode(); 75 | addNodeLabelsToCount(labelCounter, visitedNodes, startNode); 76 | addNodeLabelsToCount(labelCounter, visitedNodes, endNode); 77 | } 78 | 79 | return labelCounter; 80 | } 81 | 82 | private static void addNodeLabelsToCount(HashMap labelCounter, HashSet visitedNodes, Node node) { 83 | if (!visitedNodes.contains(node.getId())) { 84 | for (Label label : node.getLabels()) { 85 | Integer currentLabelCountObject = labelCounter.get(label.name()); 86 | int currentLabelCount = currentLabelCountObject == null ? 0 : currentLabelCountObject; 87 | labelCounter.put(label.name(), currentLabelCount + 1); 88 | } 89 | visitedNodes.add(node.getId()); 90 | } 91 | } 92 | 93 | 94 | private static float calculateNodeRankEstimate(HashMap labelCounter, Node node) { 95 | int labelFrequency = 0; 96 | for (Label label : node.getLabels()) { 97 | labelFrequency += labelCounter.get(label.name()); 98 | } 99 | int nodeDegree = node.getDegree(Direction.OUTGOING); 100 | if (labelFrequency > 0) { 101 | return nodeDegree / (float) labelFrequency; 102 | } else { 103 | return nodeDegree; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/QueryUtils.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query; 2 | 3 | import com.nativelibs4java.opencl.CLEvent; 4 | import org.bridj.Pointer; 5 | import se.simonevertsson.gpu.dictionary.QueryIdDictionary; 6 | import se.simonevertsson.gpu.kernel.QueryKernels; 7 | import se.simonevertsson.gpu.query.relationship.join.PossibleSolutions; 8 | import se.simonevertsson.runner.AliasDictionary; 9 | 10 | import java.util.AbstractMap; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class QueryUtils { 16 | 17 | public static int[] gatherCandidateArray(Pointer candidateIndicatorsPointer, int dataNodeCount, int nodeId) { 18 | int[] prefixScanArray = new int[dataNodeCount]; 19 | int candidateCount = 0; 20 | int offset = dataNodeCount * nodeId; 21 | if (candidateIndicatorsPointer.get(offset + 0)) { 22 | candidateCount++; 23 | } 24 | 25 | for (int i = 1; i < dataNodeCount; i++) { 26 | int nextElement = candidateIndicatorsPointer.get(offset + i - 1) ? 1 : 0; 27 | prefixScanArray[i] = prefixScanArray[i - 1] + nextElement; 28 | if (candidateIndicatorsPointer.get(offset + i)) { 29 | candidateCount++; 30 | } 31 | } 32 | 33 | int[] candidateArray = new int[candidateCount]; 34 | 35 | for (int i = 0; i < dataNodeCount; i++) { 36 | if (candidateIndicatorsPointer.get(offset + i)) { 37 | candidateArray[prefixScanArray[i]] = i; 38 | } 39 | } 40 | return candidateArray; 41 | } 42 | 43 | 44 | public static boolean[] pointerBooleanToArray(Pointer pointer, int size) { 45 | boolean[] result = new boolean[size]; 46 | int i = 0; 47 | for (boolean element : pointer) { 48 | result[i] = element; 49 | i++; 50 | } 51 | return result; 52 | } 53 | 54 | public static int[] pointerIntegerToArray(Pointer pointer, int size) { 55 | int[] result = new int[size]; 56 | int i = 0; 57 | for (int element : pointer) { 58 | result[i] = element; 59 | i++; 60 | } 61 | return result; 62 | } 63 | 64 | public static int[] generatePrefixScanArray(Pointer bufferPointer, int bufferSize) { 65 | int totalElementCount = 0; 66 | int[] prefixScanArray = new int[bufferSize + 1]; 67 | for (int i = 0; i < bufferSize; i++) { 68 | prefixScanArray[i] = totalElementCount; 69 | totalElementCount += bufferPointer.get(i); 70 | } 71 | prefixScanArray[bufferSize] = totalElementCount; 72 | return prefixScanArray; 73 | } 74 | 75 | public static int[] generatePrefixScanArrayFromBooleans(Pointer bufferPointer, int bufferSize) { 76 | int totalElementCount = 0; 77 | int[] prefixScanArray = new int[bufferSize + 1]; 78 | for (int i = 0; i < bufferSize; i++) { 79 | prefixScanArray[i] = totalElementCount; 80 | totalElementCount += bufferPointer.get(i) ? 1 : 0; 81 | } 82 | prefixScanArray[bufferSize] = totalElementCount; 83 | return prefixScanArray; 84 | } 85 | 86 | 87 | public static List generateQuerySolutions(QueryKernels queryKernels, QueryContext queryContext, PossibleSolutions solution) { 88 | ArrayList results = new ArrayList(); 89 | List> solutionElements = null; 90 | 91 | if (solution != null) { 92 | AliasDictionary aliasDictionary = queryContext.queryGraph.aliasDictionary; 93 | QueryIdDictionary queryGraphQueryIdDictionary = queryContext.gpuQuery.getNodeIdDictionary(); 94 | QueryIdDictionary dataGraphQueryIdDictionary = queryContext.gpuData.getNodeIdDictionary(); 95 | Pointer solutionsPointer = solution.getSolutionElements().read(queryKernels.queue); 96 | int solutionCount = (int) (solution.getSolutionElements().getElementCount() / queryContext.queryNodeCount); 97 | 98 | 99 | for (int i = 0; i < solutionCount * queryContext.queryNodeCount; i++) { 100 | if (i % queryContext.queryNodeCount == 0) { 101 | solutionElements = new ArrayList<>(); 102 | } 103 | int queryGraphQueryId = i % queryContext.queryNodeCount; 104 | int queryGraphId = (int) queryGraphQueryIdDictionary.getId(queryGraphQueryId); 105 | int solutionElementQueryId = solutionsPointer.get(i); 106 | int solutionElementId = (int) dataGraphQueryIdDictionary.getId(solutionElementQueryId); 107 | 108 | String alias = aliasDictionary.getAliasForId(queryGraphId); 109 | 110 | solutionElements.add(new AbstractMap.SimpleEntry(alias, solutionElementId)); 111 | if (i % queryContext.queryNodeCount == queryContext.queryNodeCount - 1) { 112 | results.add(new QuerySolution(queryContext.queryGraph, solutionElements)); 113 | } 114 | } 115 | } 116 | 117 | return results; 118 | } 119 | 120 | public static double getEventRunTime(CLEvent event) { 121 | return (double) (event.getProfilingCommandEnd() - event.getProfilingCommandStart()) / 10e6; 122 | } 123 | } -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/query/relationship/search/CandidateRelationships.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.query.relationship.search; 2 | 3 | import com.nativelibs4java.opencl.CLBuffer; 4 | import org.neo4j.graphdb.Relationship; 5 | import se.simonevertsson.gpu.query.QueryContext; 6 | import se.simonevertsson.gpu.dictionary.QueryIdDictionary; 7 | import se.simonevertsson.gpu.query.QueryUtils; 8 | import se.simonevertsson.gpu.dictionary.TypeDictionary; 9 | import se.simonevertsson.gpu.kernel.QueryKernels; 10 | 11 | import java.util.Arrays; 12 | 13 | public class CandidateRelationships { 14 | 15 | 16 | private final Relationship relationship; 17 | private final QueryKernels queryKernels; 18 | private final QueryIdDictionary nodeIdDictionary; 19 | private final TypeDictionary typeDictionary; 20 | private CLBuffer candidateEndNodeIndices; 21 | private CLBuffer candidateEndNodes; 22 | private CLBuffer candidateStartNodes; 23 | private int endNodeCount; 24 | private int startNodeCount; 25 | private CLBuffer candidateRelationshipIndices; 26 | 27 | 28 | public CandidateRelationships(Relationship relationship, QueryContext queryContext, QueryKernels queryKernels) { 29 | this.relationship = relationship; 30 | this.nodeIdDictionary = queryContext.gpuQuery.getNodeIdDictionary(); 31 | this.typeDictionary = queryContext.typeDictionary; 32 | this.queryKernels = queryKernels; 33 | } 34 | 35 | 36 | public int getRelationshipType() { 37 | 38 | return this.relationship.getType() != null ? this.typeDictionary.getIdForType(this.relationship.getType().name()) : -1; 39 | } 40 | 41 | public CLBuffer getCandidateEndNodeIndices() { 42 | return candidateEndNodeIndices; 43 | } 44 | 45 | public CLBuffer getCandidateEndNodes() { 46 | return candidateEndNodes; 47 | } 48 | 49 | public CLBuffer getCandidateStartNodes() { 50 | return candidateStartNodes; 51 | } 52 | 53 | public void setCandidateStartNodes(CLBuffer candidateStartNodes) { 54 | this.candidateStartNodes = candidateStartNodes; 55 | this.startNodeCount = (int) this.candidateStartNodes.getElementCount(); 56 | } 57 | 58 | public void setCandidateEndNodeIndices(CLBuffer candidateEndNodeIndices) { 59 | this.candidateEndNodeIndices = candidateEndNodeIndices; 60 | } 61 | 62 | public void setCandidateEndNodes(CLBuffer candidateEndNodes) { 63 | this.candidateEndNodes = candidateEndNodes; 64 | } 65 | 66 | public int getQueryStartNodeId() { 67 | return this.nodeIdDictionary.getQueryId(this.relationship.getStartNode().getId()); 68 | } 69 | 70 | public int getQueryEndNodeId() { 71 | return this.nodeIdDictionary.getQueryId(this.relationship.getEndNode().getId()); 72 | } 73 | 74 | public int getEndNodeCount() { 75 | return endNodeCount; 76 | } 77 | 78 | public int getStartNodeCount() { 79 | return startNodeCount; 80 | } 81 | 82 | public Relationship getRelationship() { 83 | return this.relationship; 84 | } 85 | 86 | public void setEndNodeCount(int endNodeCount) { 87 | this.endNodeCount = endNodeCount; 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | StringBuilder builder = new StringBuilder(); 93 | builder.append("Candidate relationships for runner relationship "); 94 | builder.append(this.relationship.getId()); 95 | builder.append(" ( "); 96 | builder.append(this.nodeIdDictionary.getQueryId(this.relationship.getStartNode().getId())); 97 | builder.append(" --> "); 98 | builder.append(this.nodeIdDictionary.getQueryId(this.relationship.getEndNode().getId())); 99 | builder.append(" )\n"); 100 | 101 | builder.append("Start nodes: "); 102 | builder.append(Arrays.toString(QueryUtils.pointerIntegerToArray(this.candidateStartNodes.read(this.queryKernels.queue), startNodeCount))); 103 | builder.append('\n'); 104 | 105 | builder.append("End node indices: "); 106 | builder.append(Arrays.toString(QueryUtils.pointerIntegerToArray(this.candidateEndNodeIndices.read(this.queryKernels.queue), this.startNodeCount + 1))); 107 | builder.append('\n'); 108 | 109 | builder.append("End nodes: "); 110 | builder.append(Arrays.toString(QueryUtils.pointerIntegerToArray(this.candidateEndNodes.read(this.queryKernels.queue), this.endNodeCount))); 111 | builder.append('\n'); 112 | 113 | builder.append("Relationship indices: "); 114 | builder.append(Arrays.toString(QueryUtils.pointerIntegerToArray(this.candidateRelationshipIndices.read(this.queryKernels.queue), this.endNodeCount))); 115 | builder.append('\n'); 116 | 117 | return builder.toString(); 118 | } 119 | 120 | 121 | public void setCandidateRelationshipIndices(CLBuffer candidateRelationshipIndices) { 122 | this.candidateRelationshipIndices = candidateRelationshipIndices; 123 | } 124 | 125 | public CLBuffer getRelationshipIndices() { 126 | return this.candidateRelationshipIndices; 127 | } 128 | 129 | public void release() { 130 | this.candidateStartNodes.release(); 131 | this.candidateEndNodeIndices.release(); 132 | this.candidateEndNodes.release(); 133 | this.candidateRelationshipIndices.release(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/se/simonevertsson/gpu/graph/GpuGraphConverter.java: -------------------------------------------------------------------------------- 1 | package se.simonevertsson.gpu.graph; 2 | 3 | import org.neo4j.graphdb.*; 4 | import se.simonevertsson.gpu.dictionary.LabelDictionary; 5 | import se.simonevertsson.gpu.dictionary.QueryIdDictionary; 6 | import se.simonevertsson.gpu.dictionary.TypeDictionary; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * This class converts graph data data types, such as Node and Relationship etc. to a more GPU-firendly representation 13 | * such as arrays and primitive types. 14 | */ 15 | public class GpuGraphConverter { 16 | 17 | private List nodes; 18 | private LabelDictionary labelDictionary; 19 | private TypeDictionary typeDictionary; 20 | private QueryIdDictionary nodeIdDictionary; 21 | 22 | private ArrayList labelIndices; 23 | private ArrayList nodeLabels; 24 | private ArrayList relationshipIndices; 25 | private ArrayList nodeRelationships; 26 | private ArrayList relationshipTypes; 27 | private QueryIdDictionary relationshipIdDictionary; 28 | private int currentLabelIndex; 29 | private int currentRelationshipIndex; 30 | 31 | /** 32 | * Creates a new converter. 33 | * 34 | * @param nodes A list of all nodes in the database which should be queried. 35 | * @param labelDictionary A dictionary where the node labels and their generated GPU-representation will be stored. 36 | * @param typeDictionary A dictionary where the relationship types and their generated GPU-representation will be stored. 37 | */ 38 | public GpuGraphConverter(List nodes, LabelDictionary labelDictionary, TypeDictionary typeDictionary) { 39 | this.nodes = nodes; 40 | this.labelDictionary = labelDictionary; 41 | this.typeDictionary = typeDictionary; 42 | } 43 | 44 | /** 45 | * Converts the supplied database (node list) to a more GPU friendly representation 46 | * 47 | * @return 48 | */ 49 | public GpuGraph convert() { 50 | initializeLists(); 51 | for (Node node : this.nodes) { 52 | nodeIdDictionary.add(node.getId()); 53 | } 54 | 55 | currentLabelIndex = 0; 56 | currentRelationshipIndex = 0; 57 | for (Node startNode : this.nodes) { 58 | addLabels(startNode); 59 | convertAndAddRelationships(startNode); 60 | } 61 | 62 | appendLastIndexIfNotEmpty(labelIndices, currentLabelIndex); 63 | appendLastIndexIfNotEmpty(relationshipIndices, currentRelationshipIndex); 64 | 65 | 66 | // If no node has labels the label array will be empty. Empty arrays are not supported by JavaCL hence a 67 | // dummy element is added in this case. 68 | if (nodeLabels.isEmpty()) { 69 | nodeLabels.add(-1); 70 | } 71 | 72 | return new GpuGraph(labelIndices, nodeLabels, relationshipIndices, nodeRelationships, relationshipTypes, nodeIdDictionary, relationshipIdDictionary); 73 | } 74 | 75 | private void convertAndAddRelationships(Node sourceNode) { 76 | Iterable nodeRelationships = sourceNode.getRelationships(Direction.OUTGOING); 77 | int relationshipCount = convertAndAddRelationships(nodeRelationships); 78 | 79 | relationshipIndices.add(currentRelationshipIndex); 80 | currentRelationshipIndex += relationshipCount; 81 | } 82 | 83 | private void addLabels(Node sourceNode) { 84 | Iterable