diff --git a/java/server/src/main/java/org/softwareheritage/graph/Graph.java b/java/server/src/main/java/org/softwareheritage/graph/Graph.java
--- a/java/server/src/main/java/org/softwareheritage/graph/Graph.java
+++ b/java/server/src/main/java/org/softwareheritage/graph/Graph.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import it.unimi.dsi.big.webgraph.BVGraph;
+import it.unimi.dsi.big.webgraph.LazyLongIterator;
import org.softwareheritage.graph.Node;
import org.softwareheritage.graph.SwhId;
@@ -140,6 +141,17 @@
}
/**
+ * Returns lazy iterator of successors of a node.
+ *
+ * @param nodeId node specified as a long id
+ * @return lazy iterator of successors of the node, specified as a WebGraph LazyLongIterator
+ */
+ public LazyLongIterator lazySuccessors(long nodeId) {
+ return graph.successors(nodeId);
+ }
+
+ /**
* Returns the outdegree of a node.
*
* @param nodeId node specified as a long id
@@ -153,7 +165,7 @@
* Returns list of predecessors of a node.
*
* @param nodeId node specified as a long id
- * @return list of successors of the node, specified as a fastutil LongBigArrays
*/
public long[][] predecessors(long nodeId) {
@@ -161,6 +173,17 @@
}
/**
+ * Returns lazy iterator of predecessors of a node.
+ *
+ * @param nodeId node specified as a long id
+ * @return lazy iterator of predecessors of the node, specified as a WebGraph LazyLongIterator
+ */
+ public LazyLongIterator lazyPredecessors(long nodeId) {
+ return graphTransposed.successors(nodeId);
+ }
+
+ /**
* Returns the indegree of a node.
*
* @param nodeId node specified as a long id
@@ -182,14 +205,26 @@
}
/**
- * Returns the neighbors of a node, depending on graph orientation.
+ * Returns the neighbors of a node (as a list), depending on graph orientation.
*
* @param nodeId node specified as a long id
* @param useTransposed boolean value to use transposed graph
- * @return list of successors of the node, specified as a fastutil LongBigArrays
*/
public long[][] neighbors(long nodeId, boolean useTransposed) {
return (useTransposed) ? predecessors(nodeId) : successors(nodeId);
}
+
+ /**
+ * Returns the neighbors of a node (as a lazy iterator), depending on graph orientation.
+ *
+ * @param nodeId node specified as a long id
+ * @param useTransposed boolean value to use transposed graph
+ * @return lazy iterator of neighbors of the node, specified as a WebGraph LazyLongIterator
+ */
+ public LazyLongIterator lazyNeighbors(long nodeId, boolean useTransposed) {
+ return (useTransposed) ? lazyPredecessors(nodeId) : lazySuccessors(nodeId);
+ }
}
diff --git a/java/server/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java b/java/server/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java
new file mode 100644
--- /dev/null
+++ b/java/server/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java
@@ -0,0 +1,49 @@
+package org.softwareheritage.graph.benchmark;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import it.unimi.dsi.big.webgraph.LazyLongIterator;
+
+import org.softwareheritage.graph.Graph;
+import org.softwareheritage.graph.utils.Random;
+import org.softwareheritage.graph.utils.Statistics;
+import org.softwareheritage.graph.utils.Timing;
+
+/**
+ * Benchmark to time edge access time.
+ *
+ * @author Thibault Allançon
+ * @version 0.0.1
+ * @since 0.0.1
+ */
+
+public class AccessEdge {
+ /**
+ * Main entrypoint.
+ *
+ * @param args command line arguments
+ */
+ public static void main(String[] args) throws IOException {
+ String path = args[0];
+ Graph graph = new Graph(path);
+
+ final long seed = 42;
+ final long nbNodes = 1_000_000;
+ Random random = new Random(seed);
+ long[] nodeIds = random.generateNodeIds(graph, nbNodes);
+
+ ArrayList timings = new ArrayList<>();
+ for (long nodeId : nodeIds) {
+ long startTime = Timing.start();
+ LazyLongIterator neighbors = graph.lazySuccessors(nodeId);
+ long firstNeighbor = neighbors.nextLong();
+ double duration = (double) Timing.stop(startTime);
+ timings.add(duration);
+ }
+
+ System.out.println("Used " + nbNodes + " random edges (results are in seconds):");
+ Statistics stats = new Statistics(timings);
+ stats.printAll();
+ }
+}
diff --git a/java/server/src/main/java/org/softwareheritage/graph/utils/Random.java b/java/server/src/main/java/org/softwareheritage/graph/utils/Random.java
new file mode 100644
--- /dev/null
+++ b/java/server/src/main/java/org/softwareheritage/graph/utils/Random.java
@@ -0,0 +1,43 @@
+package org.softwareheritage.graph.utils;
+
+import org.softwareheritage.graph.Graph;
+
+/**
+ * Random related utility class.
+ *
+ * @author Thibault Allançon
+ * @version 0.0.1
+ * @since 0.0.1
+ */
+
+public class Random {
+ /** Internal pseudorandom generator */
+ java.util.Random random;
+
+ /**
+ * Constructor.
+ */
+ public Random() {
+ this.random = new java.util.Random();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param seed random generator seed
+ */
+ public Random(long seed) {
+ this.random = new java.util.Random(seed);
+ }
+
+ /**
+ * Generates random node ids.
+ *
+ * @param graph graph from which node ids will be picked
+ * @param nbNodes number of node ids to generate
+ * @return an array of random node ids
+ */
+ public long[] generateNodeIds(Graph graph, long nbNodes) {
+ return random.longs(nbNodes, 0, graph.getNbNodes()).toArray();
+ }
+}
diff --git a/java/server/src/main/java/org/softwareheritage/graph/utils/Statistics.java b/java/server/src/main/java/org/softwareheritage/graph/utils/Statistics.java
new file mode 100644
--- /dev/null
+++ b/java/server/src/main/java/org/softwareheritage/graph/utils/Statistics.java
@@ -0,0 +1,89 @@
+package org.softwareheritage.graph.utils;
+
+import java.util.ArrayList;
+
+/**
+ * Compute various statistics on a list of values.
+ *
+ * @author Thibault Allançon
+ * @version 0.0.1
+ * @since 0.0.1
+ */
+
+public class Statistics {
+ /** Input values */
+ ArrayList values;
+
+ /**
+ * Constructor.
+ *
+ * @param values input values
+ */
+ public Statistics(ArrayList values) {
+ this.values = values;
+ }
+
+ /**
+ * Returns the minimum value.
+ *
+ * @return minimum value
+ */
+ public double getMin() {
+ double min = Double.POSITIVE_INFINITY;
+ for (double v : values) {
+ min = Math.min(min, v);
+ }
+ return min;
+ }
+
+ /**
+ * Returns the maximum value.
+ *
+ * @return maximum value
+ */
+ public double getMax() {
+ double max = Double.NEGATIVE_INFINITY;
+ for (double v : values) {
+ max = Math.max(max, v);
+ }
+ return max;
+ }
+
+ /**
+ * Computes the average.
+ *
+ * @return average value
+ */
+ public double getAverage() {
+ double sum = 0;
+ for (double v : values) {
+ sum += v;
+ }
+ return sum / (double) values.size();
+ }
+
+ /**
+ * Computes the standard deviation.
+ *
+ * @return standard deviation value
+ */
+ public double getStandardDeviation() {
+ double average = getAverage();
+ double variance = 0;
+ for (double v : values) {
+ variance += (v - average) * (v - average);
+ }
+ variance /= (double) values.size();
+ return Math.sqrt(variance);
+ }
+
+ /**
+ * Computes and prints all statistical values.
+ */
+ public void printAll() {
+ System.out.println("min value: " + getMin());
+ System.out.println("max value: " + getMax());
+ System.out.println("average: " + getAverage());
+ System.out.println("standard deviation: " + getStandardDeviation());
+ }
+}