Skip to content

Commit a558ef2

Browse files
author
Peter
committed
introduced edge segments which is slightly slower 3% but more robust memory allocation for big graphs
1 parent 5258d23 commit a558ef2

File tree

5 files changed

+126
-91
lines changed

5 files changed

+126
-91
lines changed

core/nbactions.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</goals>
1919
<properties>
2020
<exec.classpathScope>runtime</exec.classpathScope>
21-
<exec.args>-Xms1000m -Xmx1000m -XX:PermSize=20m -XX:MaxPermSize=20m -classpath %classpath de.jetsli.graph.reader.OSMReader test=false debug=true shortestpath=true graph=graph-unterfranken.osm osm=/media/SAMSUNG/unterfranken.osm</exec.args>
21+
<exec.args>-Xms1000m -Xmx1000m -XX:PermSize=20m -XX:MaxPermSize=20m -classpath %classpath de.jetsli.graph.reader.OSMReader test=true debug=true shortestpath=true graph=graph-unterfranken.osm osm=/media/SAMSUNG/unterfranken.osm</exec.args>
2222
<exec.executable>java</exec.executable>
2323
</properties>
2424
</action>
@@ -30,7 +30,7 @@
3030
</goals>
3131
<properties>
3232
<exec.classpathScope>runtime</exec.classpathScope>
33-
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -Xms1000m -Xmx1000m -XX:PermSize=20m -XX:MaxPermSize=20m -classpath %classpath de.jetsli.graph.reader.OSMReader test=false debug=true shortestpath=true graph=graph-unterfranken.osm osm=/media/SAMSUNG/unterfranken.osm</exec.args>
33+
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -Xms1000m -Xmx1000m -XX:PermSize=20m -XX:MaxPermSize=20m -classpath %classpath de.jetsli.graph.reader.OSMReader test=true debug=true shortestpath=true graph=graph-unterfranken.osm osm=/media/SAMSUNG/unterfranken.osm</exec.args>
3434
<jpda.listen>true</jpda.listen>
3535
<exec.executable>java</exec.executable>
3636
</properties>
@@ -42,7 +42,7 @@
4242
<goal>org.codehaus.mojo:exec-maven-plugin:1.1.1:exec</goal>
4343
</goals>
4444
<properties>
45-
<exec.args>${profiler.args} -Xms1000m -Xmx1000m -XX:PermSize=20m -XX:MaxPermSize=20m -classpath %classpath de.jetsli.graph.reader.OSMReader test=false debug=true shortestpath=true graph=graph-unterfranken.osm osm=/media/SAMSUNG/unterfranken.osm</exec.args>
45+
<exec.args>${profiler.args} -Xms1000m -Xmx1000m -XX:PermSize=20m -XX:MaxPermSize=20m -classpath %classpath de.jetsli.graph.reader.OSMReader test=true debug=true shortestpath=true graph=graph-unterfranken.osm osm=/media/SAMSUNG/unterfranken.osm</exec.args>
4646
<profiler.action>profile</profiler.action>
4747
<exec.executable>${profiler.java}</exec.executable>
4848
</properties>

core/pom.xml

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,13 @@
3535
<groupId>org.slf4j</groupId>
3636
<artifactId>slf4j-api</artifactId>
3737
<version>${slf4j.version}</version>
38-
</dependency>
38+
</dependency>
3939
<dependency>
4040
<groupId>org.slf4j</groupId>
41-
<artifactId>slf4j-log4j12</artifactId>
41+
<artifactId>slf4j-simple</artifactId>
4242
<version>${slf4j.version}</version>
4343
<scope>runtime</scope>
44-
</dependency>
45-
46-
<!-- use 1.2.14 instead of 1.2.16 because of
47-
java.lang.NullPointerException at org.apache.log4j.helpers.ISO8601DateFormat.format(ISO8601DateFormat.java:70)
48-
-->
49-
<dependency>
50-
<groupId>log4j</groupId>
51-
<artifactId>log4j</artifactId>
52-
<version>1.2.14</version>
53-
<scope>compile</scope>
54-
</dependency>
44+
</dependency>
5545

5646
<dependency>
5747
<groupId>junit</groupId>
@@ -60,20 +50,7 @@
6050
<version>4.10</version>
6151
<scope>test</scope>
6252
</dependency>
63-
<!--
64-
<dependency>
65-
<groupId>org.hamcrest</groupId>
66-
<artifactId>hamcrest-core</artifactId>
67-
<version>1.3.RC2</version>
68-
<scope>test</scope>
69-
</dependency>
70-
<dependency>
71-
<groupId>org.hamcrest</groupId>
72-
<artifactId>hamcrest-library</artifactId>
73-
<version>1.3.RC2</version>
74-
<scope>test</scope>
75-
</dependency>
76-
-->
53+
7754
</dependencies>
7855

7956
<build>

core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafe.java

Lines changed: 96 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -41,32 +41,27 @@ public class MemoryGraphSafe implements SaveableGraph {
4141
private static final int EMPTY_LINK = 0;
4242
private static final float DIST_UNIT = 10000f;
4343
private Logger logger = LoggerFactory.getLogger(getClass());
44+
// number of integers not edges
45+
private static final int MIN_SEGMENT_SIZE = 1 << 13;
4446
private static final float FACTOR = 1.5f;
4547
// keep in mind that we address integers here - not bytes!
4648
private static final int LEN_DIST = 1;
4749
private static final int LEN_NODEID = 1;
4850
private static final int LEN_FLAGS = 1;
4951
private static final int LEN_LINK = 1;
5052
private static final int LEN_EDGE = LEN_FLAGS + LEN_DIST + LEN_NODEID + LEN_LINK;
51-
// or should we use int and a factor? private static double MICRO = 1000000;
5253
private float[] lats;
5354
private float[] lons;
54-
// private int[] priorities;
5555
private long creationTime = System.currentTimeMillis();
5656
private int size;
5757
private int[] refToEdges;
58-
// TODO use a bitset to memorize fragmented edges-area!
59-
private int[] edgesArea;
60-
private int nextEdgePointer;
61-
// private final ReadWriteLock lock;
62-
// private final Lock writeLock;
63-
// private final Lock readLock;
58+
private int[][] edgesSegments;
59+
private int edgesSegmentSize;
60+
private int edgeCurrentSegment;
61+
private int edgeNextGlobalPointer;
6462
private String storageLocation;
6563
private MyBitSet deletedNodes;
6664

67-
// public MemoryGraphSafe() {
68-
// this(10);
69-
// }
7065
public MemoryGraphSafe(int cap) {
7166
this(null, cap);
7267
}
@@ -83,13 +78,21 @@ public MemoryGraphSafe(String storageDir, int cap, int capEdge) {
8378
}
8479
}
8580

81+
int getSegmentSize() {
82+
return edgesSegmentSize;
83+
}
84+
85+
int getSegments() {
86+
return edgesSegments.length;
87+
}
88+
8689
@Override public int getNodes() {
8790
// readLock.lock();
8891
return size;
8992
}
9093

91-
public int getMaxEdges() {
92-
return edgesArea.length / LEN_EDGE;
94+
private int getMaxEdges() {
95+
return edgesSegments.length * edgesSegmentSize / LEN_EDGE;
9396
}
9497

9598
@Override
@@ -101,27 +104,25 @@ public void setNode(int index, double lat, double lon) {
101104
}
102105

103106
private void initEdges(int cap) {
104-
cap *= LEN_EDGE;
105-
edgesArea = new int[cap];
106-
}
107-
108-
// Use ONLY within a writer lock area
109-
private void ensureEdgesCapacity(int cap) {
110-
ensureEdgePointer(cap * LEN_EDGE);
107+
int tmp = (int) (Math.log(cap) / Math.log(2));
108+
edgesSegmentSize = Math.max((int) Math.pow(2, tmp), MIN_SEGMENT_SIZE);
109+
edgesSegments = new int[1][edgesSegmentSize];
111110
}
112111

113112
// Use ONLY within a writer lock area
114113
private void ensureEdgePointer(int pointer) {
115-
if (pointer + LEN_EDGE < edgesArea.length)
114+
if (pointer + LEN_EDGE < edgesSegmentSize * getSegments())
116115
return;
117116

118-
// TODO instead of reallocation (memory waste, time to copy), create multiple segments for
119-
// edges and use the highest bits (any number 2^x) as the segment number.
120-
// Choose 2^x as segment number so we can use bit operations and avoid modulo for edgeId lookup!
121-
pointer = Math.max(10 * LEN_EDGE, Math.round(pointer * FACTOR));
122-
logger.info("ensure space to " + (int) (pointer / LEN_EDGE) + " edges (" + (float) 4 * pointer / (1 << 20) + " MB)");
123-
int newLen = pointer;
124-
edgesArea = Arrays.copyOf(edgesArea, newLen);
117+
// create a new segment
118+
edgeCurrentSegment++;
119+
int[][] tmp = new int[edgeCurrentSegment + 1][];
120+
for (int i = 0; i < edgesSegments.length; i++) {
121+
tmp[i] = edgesSegments[i];
122+
}
123+
tmp[edgeCurrentSegment] = new int[edgesSegmentSize];
124+
edgesSegments = tmp;
125+
// nextEdgePointer = 0;
125126
}
126127

127128
// Use ONLY within a writer lock area
@@ -181,6 +182,20 @@ public void edge(int a, int b, double distance, boolean bothDirections) {
181182
internalAdd(b, a, distance, dirFlag);
182183
}
183184

185+
private void saveToEdgeArea(int pointer, int data) {
186+
// TODO improve speed via bit operations!
187+
int segNumber = pointer / edgesSegmentSize;
188+
int segPointer = pointer % edgesSegmentSize;
189+
edgesSegments[segNumber][segPointer] = data;
190+
}
191+
192+
private int getFromEdgeArea(int pointer) {
193+
// TODO improve speed via bit operations!
194+
int segNumber = pointer / edgesSegmentSize;
195+
int segPointer = pointer % edgesSegmentSize;
196+
return edgesSegments[segNumber][segPointer];
197+
}
198+
184199
private void internalAdd(int fromNodeId, int toNodeId, double dist, int flags) {
185200
int edgePointer = refToEdges[fromNodeId];
186201
int newPos = nextEdgePointer();
@@ -197,7 +212,7 @@ private void internalAdd(int fromNodeId, int toNodeId, double dist, int flags) {
197212
throw new IllegalStateException("list cannot be empty for positive edgePointer " + edgePointer + " node:" + fromNodeId);
198213

199214
int linkPointer = getLink(list.get(list.size() - 1));
200-
edgesArea[linkPointer] = newPos;
215+
saveToEdgeArea(linkPointer, newPos);
201216
} else
202217
refToEdges[fromNodeId] = newPos;
203218

@@ -211,30 +226,30 @@ private int getLink(int edgePointer) {
211226
private void writeEdge(int edgePointer, int flags, double dist, int toNodeId, int nextEdgePointer) {
212227
ensureEdgePointer(edgePointer);
213228

214-
edgesArea[edgePointer] = flags;
229+
saveToEdgeArea(edgePointer, flags);
215230
edgePointer += LEN_FLAGS;
216231

217-
edgesArea[edgePointer] = (int) (dist * DIST_UNIT);
232+
saveToEdgeArea(edgePointer, (int) (dist * DIST_UNIT));
218233
edgePointer += LEN_DIST;
219234

220-
edgesArea[edgePointer] = toNodeId;
235+
saveToEdgeArea(edgePointer, toNodeId);
221236
edgePointer += LEN_NODEID;
222237

223-
edgesArea[edgePointer] = nextEdgePointer;
238+
saveToEdgeArea(edgePointer, nextEdgePointer);
224239
// edgePointer += LEN_LINK;
225240
}
226241

227242
private int nextEdgePointer() {
228-
nextEdgePointer += LEN_EDGE;
229-
return nextEdgePointer;
243+
edgeNextGlobalPointer += LEN_EDGE;
244+
return edgeNextGlobalPointer;
230245
}
231246

232247
private TIntArrayList readAllEdges(int edgePointer) {
233248
TIntArrayList list = new TIntArrayList(5);
234249
int i = 0;
235250
for (; i < 1000; i++) {
236251
list.add(edgePointer);
237-
edgePointer = edgesArea[getLink(edgePointer)];
252+
edgePointer = getFromEdgeArea(getLink(edgePointer));
238253
if (edgePointer == EMPTY_LINK)
239254
break;
240255
}
@@ -282,20 +297,20 @@ public EdgeIterable(int node, boolean in, boolean out) {
282297

283298
void readNext() {
284299
// readLock.lock();
285-
flags = edgesArea[pointer];
300+
flags = getFromEdgeArea(pointer);
286301
if (!in && (flags & 1) == 0 || !out && (flags & 2) == 0) {
287-
pointer = edgesArea[getLink(pointer)];
302+
pointer = getFromEdgeArea(getLink(pointer));
288303
return;
289304
}
290305
pointer += LEN_FLAGS;
291306

292-
dist = edgesArea[pointer] / DIST_UNIT;
307+
dist = getFromEdgeArea(pointer) / DIST_UNIT;
293308
pointer += LEN_DIST;
294309

295-
nodeId = edgesArea[pointer];
310+
nodeId = getFromEdgeArea(pointer);
296311
pointer += LEN_NODEID;
297312
// next edge
298-
pointer = edgesArea[pointer];
313+
pointer = getFromEdgeArea(pointer);
299314
foundNext = true;
300315
}
301316

@@ -334,14 +349,25 @@ public int flags() {
334349
@Override
335350
public Graph clone() {
336351
// readLock.lock();
337-
MemoryGraphSafe g = new MemoryGraphSafe(null, refToEdges.length, getMaxEdges());
338-
System.arraycopy(lats, 0, g.lats, 0, lats.length);
339-
System.arraycopy(lons, 0, g.lons, 0, lons.length);
340-
System.arraycopy(refToEdges, 0, g.refToEdges, 0, refToEdges.length);
341-
System.arraycopy(edgesArea, 0, g.edgesArea, 0, edgesArea.length);
342-
g.nextEdgePointer = nextEdgePointer;
343-
g.size = size;
344-
return g;
352+
MemoryGraphSafe clonedGraph = new MemoryGraphSafe(null, refToEdges.length, getMaxEdges());
353+
354+
System.arraycopy(lats, 0, clonedGraph.lats, 0, lats.length);
355+
System.arraycopy(lons, 0, clonedGraph.lons, 0, lons.length);
356+
System.arraycopy(refToEdges, 0, clonedGraph.refToEdges, 0, refToEdges.length);
357+
358+
clonedGraph.edgesSegments = new int[edgeCurrentSegment + 1][];
359+
for (int i = 0; i < clonedGraph.edgesSegments.length; i++) {
360+
clonedGraph.edgesSegments[i] = new int[edgesSegmentSize];
361+
}
362+
363+
for (int i = 0; i < edgesSegments.length; i++) {
364+
System.arraycopy(edgesSegments[i], 0, clonedGraph.edgesSegments[i], 0, edgesSegmentSize);
365+
}
366+
clonedGraph.edgeCurrentSegment = edgeCurrentSegment;
367+
clonedGraph.edgesSegmentSize = edgesSegmentSize;
368+
clonedGraph.edgeNextGlobalPointer = edgeNextGlobalPointer;
369+
clonedGraph.size = size;
370+
return clonedGraph;
345371
}
346372

347373
private MyBitSet getDeletedNodes() {
@@ -380,7 +406,7 @@ public void optimize() {
380406
if (deleted == 0)
381407
return;
382408

383-
// if (deleted < size / 5) {
409+
// if (deleted < size / 4) {
384410
inPlaceDelete(deleted);
385411
// } else
386412
// optimizeIfLotsOfDeletes(deleted);
@@ -524,7 +550,10 @@ void replacingDelete(int deleted) {
524550
lats = inMemGraph.lats;
525551
lons = inMemGraph.lons;
526552
refToEdges = inMemGraph.refToEdges;
527-
edgesArea = inMemGraph.edgesArea;
553+
for (int i = 0; i < edgesSegments.length; i++) {
554+
edgesSegments[i] = inMemGraph.edgesSegments[i];
555+
}
556+
528557
size = inMemGraph.size;
529558
deletedNodes = null;
530559
}
@@ -541,9 +570,10 @@ public void save() {
541570
Helper.writeFloats(storageLocation + "/lats", lats);
542571
Helper.writeFloats(storageLocation + "/lons", lons);
543572
Helper.writeInts(storageLocation + "/refs", refToEdges);
544-
Helper.writeInts(storageLocation + "/edges", edgesArea);
545-
// Helper.writeInts(storageLocation + "/priorities", priorities);
546-
Helper.writeSettings(storageLocation + "/settings", size, creationTime, nextEdgePointer);
573+
for (int i = 0; i < edgesSegments.length; i++) {
574+
Helper.writeInts(storageLocation + "/edges" + i, edgesSegments[i]);
575+
}
576+
Helper.writeSettings(storageLocation + "/settings", size, creationTime, edgeNextGlobalPointer, edgeCurrentSegment, edgesSegmentSize);
547577
} catch (IOException ex) {
548578
throw new RuntimeException("Couldn't write data to storage. location was " + storageLocation, ex);
549579
}
@@ -561,15 +591,22 @@ public boolean loadExisting(String storageDir) {
561591

562592
size = (Integer) ob[0];
563593
creationTime = (Long) ob[1];
564-
nextEdgePointer = (Integer) ob[2];
565-
logger.info("found graph " + storageLocation + " with size:" + size
566-
+ ", edges:" + nextEdgePointer / LEN_EDGE + ", created-at:" + new Date(creationTime));
594+
edgeNextGlobalPointer = (Integer) ob[2];
595+
edgeCurrentSegment = (Integer) ob[3];
596+
edgesSegmentSize = (Integer) ob[4];
597+
logger.info("found graph " + storageLocation + " with nodes:" + size
598+
+ ", edges:" + edgeNextGlobalPointer / LEN_EDGE
599+
+ ", edges segments:" + (edgeCurrentSegment + 1)
600+
+ ", edges segmentSize:" + edgesSegmentSize
601+
+ ", created-at:" + new Date(creationTime));
567602

568603
lats = Helper.readFloats(storageLocation + "/lats");
569604
lons = Helper.readFloats(storageLocation + "/lons");
570605
refToEdges = Helper.readInts(storageLocation + "/refs");
571-
edgesArea = Helper.readInts(storageLocation + "/edges");
572-
// priorities = Helper.readInts(storageLocation + "/priorities");
606+
edgesSegments = new int[edgeCurrentSegment + 1][];
607+
for (int i = 0; i <= edgeCurrentSegment; i++) {
608+
edgesSegments[i] = Helper.readInts(storageLocation + "/edges" + i);
609+
}
573610
deletedNodes = new MyOpenBitSet(lats.length);
574611
return true;
575612
} catch (IOException ex) {

core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafeStorage.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ public MemoryGraphSafeStorage(String file, int expectedNodes) {
3333
@Override
3434
public void createNew() {
3535
Helper.deleteDir(new File(folder));
36-
// in order to avoid reallocation, allocate enough memory for the edges
37-
g = new MemoryGraphSafe(folder, osmIdToIndexMap.size(), Math.round(2.2f * osmIdToIndexMap.size()));
36+
// in order to avoid reallocation allocate enough memory.
37+
// edges will be incrementally allocated so it is not that important to match the size up front
38+
g = new MemoryGraphSafe(folder, osmIdToIndexMap.size(), Math.round(0.8f * osmIdToIndexMap.size()));
3839
}
3940

4041
@Override

0 commit comments

Comments
 (0)