Java tutorial
/** * @author Aapo Kyrola <akyrola@cs.cmu.edu> * @version 1.0 * * @section LICENSE * * Copyright [2014] [Aapo Kyrola / Carnegie Mellon University] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Publication to cite: http://arxiv.org/abs/1403.0701 */ package edu.cmu.graphchi.shards; import static com.codahale.metrics.MetricRegistry.name; import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; import com.typesafe.config.Config; import edu.cmu.graphchi.ChiFilenames; import edu.cmu.graphchi.GraphChiEnvironment; import edu.cmu.graphchi.VertexInterval; import edu.cmu.graphchi.bits.IncreasingEliasGammaSeq; import edu.cmu.graphchi.preprocessing.VertexIdTranslate; import edu.cmu.graphchi.queries.FinishQueryException; import edu.cmu.graphchi.queries.QueryCallback; import org.apache.commons.collections.map.LRUMap; import scala.actors.threadpool.locks.Lock; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.channels.FileChannel; import java.util.*; /** * Shard query support * @author Aapo Kyrola */ public class QueryShard { private ShardIndex index; private int shardNum; private File adjFile; private LongBuffer adjBuffer; private LongBuffer pointerIdxBuffer; private IntBuffer inEdgeStartBuffer; private VertexInterval interval; private int deletedCount = 0; private static final Timer inEdgeIndexLookupTimer = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "inedge-indexlookup")); private final Timer outEdgePhase1Timer = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "outedge-phase1")); private final Timer outEdgePhase2Timer = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "outedge-phase2")); private final Timer outEdgeLookupTimer = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "outedge-lookups")); private final Timer inEdgePhase1Timer = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "inedge-phase1")); private final Timer inEdgePhase2Timer = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "inedge-phase2")); private final Timer findEdgeByOffTimerScan = GraphChiEnvironment.metrics .timer(name(QueryShard.class, "findEdgeByOffTimerScan")); private final Counter cacheMissCounter = GraphChiEnvironment.metrics.counter("querycache-misses"); private final Counter cacheHitCounter = GraphChiEnvironment.metrics.counter("querycache-hits"); public boolean pinIndexToMemory = true; /* Pinned compressed indices */ private IncreasingEliasGammaSeq gammaSeqVertices; private IncreasingEliasGammaSeq gammaSeqOffs; private int numEdges; public int queryCacheSize; public static boolean freezeCache = false; private Map queryCache = (queryCacheSize == 0 ? null : Collections.synchronizedMap(new HashMap(queryCacheSize))); public final static int BYTES_PER_EDGE = 8; public QueryShard(String filename, int shardNum, int numShards, Config dbConfig) throws IOException { this(filename, shardNum, numShards, ChiFilenames.loadIntervals(filename, numShards).get(shardNum), dbConfig); } public QueryShard(String fileName, int shardNum, int numShards, VertexInterval interval, Config dbConfig) throws IOException { this.shardNum = shardNum; this.interval = interval; pinIndexToMemory = dbConfig.getBoolean("queryshard.pinindex"); queryCacheSize = dbConfig.getInt("queryshard.cachesize"); adjFile = new File(ChiFilenames.getFilenameShardsAdj(fileName, shardNum, numShards)); numEdges = (int) (adjFile.length() / BYTES_PER_EDGE); FileChannel channel = new java.io.RandomAccessFile(adjFile, "rw").getChannel(); adjBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, adjFile.length()).asLongBuffer(); channel.close(); index = (pinIndexToMemory ? null : createSparseIndex()); loadPointers(); loadInEdgeStartBuffer(); } public ShardIndex createSparseIndex() throws IOException { return new ShardIndex(adjFile); } public boolean isEmpty() { return numEdges == 0; } public long getNumEdges() { return numEdges; } private Long incacheKey(long vertexId, byte edgeType) { return -(vertexId * 16 + edgeType); } private Long outcachekey(long vertexId, byte edgeType) { return (vertexId * 16 + edgeType); } private void loadInEdgeStartBuffer() throws IOException { File inEdgeStartBufferFile = new File( ChiFilenames.getFilenameShardsAdjStartIndices(adjFile.getAbsolutePath())); FileChannel inEdgeStartChannel = new java.io.RandomAccessFile(inEdgeStartBufferFile, "r").getChannel(); inEdgeStartBuffer = inEdgeStartChannel.map(FileChannel.MapMode.READ_ONLY, 0, inEdgeStartBufferFile.length()) .asIntBuffer(); inEdgeStartChannel.close(); } public static long totalPinnedSize = 0, totalOrigSize = 0; void loadPointers() throws IOException { File pointerFile = new File(ChiFilenames.getFilenameShardsAdjPointers(adjFile.getAbsolutePath())); if (!pinIndexToMemory) { FileChannel ptrFileChannel = new java.io.RandomAccessFile(pointerFile, "r").getChannel(); pointerIdxBuffer = ptrFileChannel.map(FileChannel.MapMode.READ_ONLY, 0, pointerFile.length()) .asLongBuffer(); ptrFileChannel.close(); } else { byte[] data = new byte[(int) pointerFile.length()]; if (data.length == 0) return; totalOrigSize += data.length; FileInputStream fis = new FileInputStream(pointerFile); int i = 0; while (i < data.length) { i += fis.read(data, i, data.length - i); } fis.close(); pointerIdxBuffer = ByteBuffer.wrap(data).asLongBuffer(); long[] vertices = new long[pointerIdxBuffer.capacity() - 1]; long[] offs = new long[vertices.length]; for (int j = 0; j < vertices.length; j++) { long x = pointerIdxBuffer.get(j); vertices[j] = VertexIdTranslate.getVertexId(x); offs[j] = VertexIdTranslate.getAux(x); } boolean extraZero = (offs.length > 1 && offs[1] == offs[0]); if (extraZero) { vertices = Arrays.copyOfRange(vertices, 1, vertices.length); offs = Arrays.copyOfRange(offs, 1, offs.length); } gammaSeqVertices = new IncreasingEliasGammaSeq(vertices); gammaSeqOffs = new IncreasingEliasGammaSeq(offs); totalPinnedSize += gammaSeqVertices.sizeInBytes(); totalPinnedSize += gammaSeqOffs.sizeInBytes(); pointerIdxBuffer = null; } } // TODO: do not synchronize but make mirror of the buffer public Long find(byte edgeType, long src, long dst) { final LongBuffer tmpAdjBuffer = adjBuffer.duplicate(); final LongBuffer tmpPointerBuffer = (pinIndexToMemory ? null : pointerIdxBuffer.duplicate()); ShardIndex.IndexEntry indexEntry = (pinIndexToMemory ? null : index.lookup(src)); PointerPair ptr = findIdxAndPos(src, indexEntry, tmpPointerBuffer, new long[2]); if (ptr.cur != (-1L)) { long nextPtr = ptr.next; int n = (int) (nextPtr - ptr.cur); long adjOffset = ptr.cur; if (n < 32) { // linear search tmpAdjBuffer.position((int) adjOffset); for (int i = 0; i < n; i++) { long e = tmpAdjBuffer.get(); long v = VertexIdTranslate.getVertexId(e); if (v == dst && VertexIdTranslate.getType(e) == edgeType) { return PointerUtil.encodePointer(shardNum, (int) adjOffset + i); } else if (v > dst) { break; } } } else { // Binary search int low = (int) adjOffset; int high = low + n; while (low <= high) { int idx = ((high + low) / 2); long e = tmpAdjBuffer.get(idx); long v = VertexIdTranslate.getVertexId(e); byte type = VertexIdTranslate.getType(e); if (v == dst && type == edgeType) { // Note: edgeType sorting is not accurate as edge type can be changed (by deleting)! return PointerUtil.encodePointer(shardNum, idx); } if (v < dst || (dst == v && type < edgeType)) { low = idx + 1; } else { high = idx - 1; } } } } return null; } public boolean deleteEdge(byte edgeType, long src, long dst) { Long ptr = find(edgeType, src, dst); if (ptr != null) { deleteEdgeAtPtr(ptr); return true; } else { return false; } } public int getDeletedCount() { return deletedCount; } private void deleteEdgeAtPtr(Long ptr) { int idx = PointerUtil.decodeShardPos(ptr); long edge = adjBuffer.get(idx); if (VertexIdTranslate.getType(edge) == VertexIdTranslate.DELETED_TYPE) deletedCount++; adjBuffer.put(idx, VertexIdTranslate.encodeAsDeleted(VertexIdTranslate.getVertexId(edge), VertexIdTranslate.getAux(edge))); // TODO: delete columns (vardata) --- maybe delete listeners? } class DeleteCallBack implements QueryCallback { @Override public boolean immediateReceive() { return false; } @Override public void receiveEdge(long src, long dst, byte edgeType, long dataPtr) { throw new IllegalStateException(); } @Override public void receiveOutNeighbors(long vertexId, ArrayList<Long> neighborIds, ArrayList<Byte> edgeTypes, ArrayList<Long> dataPointers) { // A bit wasteful, but not a big deal for (long ptr : dataPointers) { deleteEdgeAtPtr(ptr); } } @Override public void receiveInNeighbors(long vertexId, ArrayList<Long> neighborIds, ArrayList<Byte> edgeTypes, ArrayList<Long> dataPointers) { // A bit wasteful, but not a big deal for (long ptr : dataPointers) { deleteEdgeAtPtr(ptr); } } } public synchronized void deleteAllEdgesFor(long vertexId, boolean hasIn, boolean hasOut) { if (interval.contains(vertexId) && hasIn) { queryIn(vertexId, new DeleteCallBack(), (byte) 0, true); } if (hasOut) queryOut(vertexId, new DeleteCallBack(), (byte) 0, true); } public void queryOut(List<Long> sortedIds, QueryCallback callback, byte edgeType) { queryOut(sortedIds, callback, edgeType, false); } // TODO: wild-card search public void queryOut(List<Long> sortedIds, QueryCallback callback, byte edgeType, boolean ignoreType) { if (isEmpty()) return; try { final Timer.Context _timer1 = outEdgePhase1Timer.time(); if (queryCacheSize > 0 && !queryCache.isEmpty()) { if (callback.immediateReceive()) { ArrayList<Long> misses = null; for (long qid : sortedIds) { Long cacheKey = outcachekey(qid, edgeType); long[] cached = (long[]) queryCache.get(cacheKey); if (cached != null) { for (int j = 0; j < cached.length; j++) { long vpacket = cached[j]; callback.receiveEdge(qid, VertexIdTranslate.getVertexId(vpacket), edgeType, PointerUtil.encodePointer(shardNum, (int) VertexIdTranslate.getAux(vpacket))); } } else { if (misses == null) misses = new ArrayList<Long>(); misses.add(qid); } } if (misses != null) cacheMissCounter.inc(misses.size()); if (misses != null) cacheHitCounter.inc(sortedIds.size() - misses.size()); else cacheHitCounter.inc(sortedIds.size()); sortedIds = misses; if (sortedIds == null) return; } else { System.err.println("Caching without immediatereceive not implemented yet"); } } ArrayList<ShardIndex.IndexEntry> indexEntries = new ArrayList<>(sortedIds.size()); if (!pinIndexToMemory) { for (Long a : sortedIds) { indexEntries.add(index.lookup(a)); } } _timer1.stop(); final Timer.Context _timer2 = outEdgePhase2Timer.time(); final LongBuffer tmpPointerIdxBuffer = (pointerIdxBuffer != null ? pointerIdxBuffer.duplicate() : null); final LongBuffer tmpAdjBuffer = adjBuffer.duplicate(); ShardIndex.IndexEntry entry = null; long[] workarr = new long[2]; for (int qIdx = 0; qIdx < sortedIds.size(); qIdx++) { entry = (pinIndexToMemory ? null : indexEntries.get(qIdx)); // ugly long vertexId = sortedIds.get(qIdx); if (qIdx > 0) { if (sortedIds.get(qIdx - 1) >= vertexId) { throw new IllegalArgumentException("Query ids have to be sorted!"); } } PointerPair ptr = findIdxAndPos(vertexId, entry, tmpPointerIdxBuffer, workarr); long curPtr = ptr.cur; if (ptr.cur != (-1L)) { long nextPtr = ptr.next; int n = (int) (nextPtr - curPtr); ArrayList<Long> res = (callback.immediateReceive() ? null : new ArrayList<Long>(n)); ArrayList<Long> resPointers = (callback.immediateReceive() ? null : new ArrayList<Long>(n)); ArrayList<Byte> resTypes = (callback.immediateReceive() ? null : new ArrayList<Byte>(n)); tmpAdjBuffer.position((int) curPtr); long[] cached = (queryCache == null || queryCache.size() >= queryCacheSize || freezeCache ? null : new long[n]); int cachek = 0; for (int i = 0; i < n && tmpAdjBuffer.position() < tmpAdjBuffer.capacity(); i++) { long e = tmpAdjBuffer.get(); byte etype = VertexIdTranslate.getType(e); if (ignoreType || etype == edgeType) { if (!callback.immediateReceive()) { res.add(VertexIdTranslate.getVertexId(e)); resPointers.add(PointerUtil.encodePointer(shardNum, (int) curPtr + i)); resTypes.add(etype); } else { callback.receiveEdge(vertexId, VertexIdTranslate.getVertexId(e), etype, PointerUtil.encodePointer(shardNum, (int) curPtr + i)); if (cached != null) { cached[cachek++] = VertexIdTranslate.encodeVertexPacket(edgeType, VertexIdTranslate.getVertexId(e), curPtr + 1); } } } } if (!callback.immediateReceive()) callback.receiveOutNeighbors(vertexId, res, resTypes, resPointers); if (cached != null) { if (cachek < n) { cached = Arrays.copyOf(cached, cachek); } queryCache.put(outcachekey(vertexId, edgeType), cached); } } else { if (!callback.immediateReceive()) callback.receiveOutNeighbors(vertexId, new ArrayList<Long>(0), new ArrayList<Byte>(0), new ArrayList<Long>(0)); if (queryCache != null && queryCacheSize > queryCache.size() && !freezeCache) { queryCache.put(outcachekey(vertexId, edgeType), new long[0]); } } } _timer2.stop(); } catch (FinishQueryException fqe) { // Used for cases when query was early fulfilled throw fqe; } catch (Exception err) { throw new RuntimeException(err); } } /** Singleton search */ public void queryOut(long vertexId, QueryCallback callback, byte edgeType) { queryOut(vertexId, callback, edgeType, false); } public void queryOut(long vertexId, QueryCallback callback, byte edgeType, boolean ignoreType) { if (isEmpty()) return; try { final LongBuffer tmpPointerIdxBuffer = (pointerIdxBuffer != null ? pointerIdxBuffer.duplicate() : null); final LongBuffer tmpAdjBuffer = adjBuffer.duplicate(); boolean immediateReceive = callback.immediateReceive(); ShardIndex.IndexEntry entry = null; entry = (pinIndexToMemory ? null : index.lookup(vertexId)); // ugly PointerPair ptr = findIdxAndPos(vertexId, entry, tmpPointerIdxBuffer, new long[2]); long curPtr = ptr.cur; if (ptr.cur != (-1L)) { long nextPtr = ptr.next; int n = (int) (nextPtr - curPtr); ArrayList<Long> res = (immediateReceive ? null : new ArrayList<Long>(n)); ArrayList<Long> resPointers = (immediateReceive ? null : new ArrayList<Long>(n)); ArrayList<Byte> resTypes = (immediateReceive ? null : new ArrayList<Byte>(n)); tmpAdjBuffer.position((int) curPtr); for (int i = 0; i < n && tmpAdjBuffer.position() < tmpAdjBuffer.capacity(); i++) { // The latter condition is to fix some bug with the last vertex? long e = tmpAdjBuffer.get(); byte etype = VertexIdTranslate.getType(e); if (etype == edgeType || ignoreType) { if (!immediateReceive) { res.add(VertexIdTranslate.getVertexId(e)); resPointers.add(PointerUtil.encodePointer(shardNum, (int) curPtr + i)); resTypes.add(etype); } else { callback.receiveEdge(vertexId, VertexIdTranslate.getVertexId(e), etype, PointerUtil.encodePointer(shardNum, (int) curPtr + i)); } } } if (!immediateReceive) callback.receiveOutNeighbors(vertexId, res, resTypes, resPointers); } else { if (!immediateReceive) callback.receiveOutNeighbors(vertexId, new ArrayList<Long>(0), new ArrayList<Byte>(0), new ArrayList<Long>(0)); } } catch (FinishQueryException fqe) { // Used for cases when query was early fulfilled throw fqe; } catch (Exception err) { err.printStackTrace(); throw new RuntimeException(err); } } class PointerPair { long cur; long next; PointerPair(long cur, long next) { this.cur = cur; this.next = next; } } /** * Returns the vertex idx for given vertex in the pointer file, or -1 if not found. * Note: moves the position of the tmpBuffer bytebuffer. */ private PointerPair findIdxAndPos(long vertexId, ShardIndex.IndexEntry sparseIndexEntry, final LongBuffer tmpPtrBuffer, long[] workarr) { return findIdxAndPos(vertexId, sparseIndexEntry, tmpPtrBuffer, workarr, false); } private PointerPair findIdxAndPos(long vertexId, ShardIndex.IndexEntry sparseIndexEntry, final LongBuffer tmpPtrBuffer, long[] workarr, boolean nextGreatest) { if (tmpPtrBuffer != null) { assert (sparseIndexEntry.vertex <= vertexId); // Not pinned if (tmpPtrBuffer.capacity() == 0) return new PointerPair(-1, -1); int vertexSeq = sparseIndexEntry.vertexSeq; long curvid = sparseIndexEntry.vertex; if (sparseIndexEntry.nextEntry == null || nextGreatest) { // Linear search tmpPtrBuffer.position(vertexSeq); long ptr = tmpPtrBuffer.get(); while (curvid <= vertexId) { try { curvid = VertexIdTranslate.getVertexId(ptr); if (curvid == vertexId || (curvid >= vertexId && nextGreatest)) { return new PointerPair(VertexIdTranslate.getAux(ptr), VertexIdTranslate.getAux(tmpPtrBuffer.get())); } ptr = tmpPtrBuffer.get(); } catch (BufferUnderflowException bufe) { return new PointerPair(-1, -1); } } } else { // Binary search int low = sparseIndexEntry.vertexSeq; int high = sparseIndexEntry.nextEntry.vertexSeq; int n = tmpPtrBuffer.capacity(); while (low <= high) { int idx = ((high + low) / 2); long ptr = tmpPtrBuffer.get(idx); curvid = VertexIdTranslate.getVertexId(ptr); if (curvid == vertexId) { return new PointerPair(VertexIdTranslate.getAux(ptr), VertexIdTranslate.getAux(tmpPtrBuffer.get(idx + 1))); } if (curvid < vertexId) { low = idx + 1; } else { high = idx - 1; } } } return new PointerPair(-1, -1); } else { if (gammaSeqVertices == null) { // Empty shard return new PointerPair(-1, -1); } // Pinned int idx = gammaSeqVertices.getIndex(vertexId); if (idx == -1) return new PointerPair(-1, -1); gammaSeqOffs.getTwo(idx, workarr); return new PointerPair(workarr[0], workarr[1]); } } private long findVertexForOff(long qoff, final LongBuffer tmpBuffer) { // Binary search to find the start of the vertex if (tmpBuffer != null) { // non-pinned int n = tmpBuffer.capacity(); int high = n - 1; int low = tmpBuffer.position(); // Check if we are close long cur = tmpBuffer.get(); long curoff = VertexIdTranslate.getAux(cur); // TODO if (qoff > curoff && qoff - curoff < 100) { long last = cur; final Timer.Context tmr = findEdgeByOffTimerScan.time(); while (curoff <= qoff) { last = cur; cur = tmpBuffer.get(); curoff = VertexIdTranslate.getAux(cur); } if (tmpBuffer.position() > 0) { tmpBuffer.position(tmpBuffer.position() - 1); // Backtrack one } tmr.stop(); return VertexIdTranslate.getVertexId(last); } if (curoff > qoff) { low = 0; } if (curoff == qoff) { return VertexIdTranslate.getVertexId(cur); } while (low <= high) { int idx = ((high + low) / 2); if (idx == n - 1) idx--; tmpBuffer.position(idx); long x = tmpBuffer.get(); long x_next = tmpBuffer.get(); long off = VertexIdTranslate.getAux(x); long off_next = VertexIdTranslate.getAux(x_next); if (off_next > qoff && off <= qoff) { tmpBuffer.position(idx); return VertexIdTranslate.getVertexId(x); } if (off < qoff) { low = idx + 1; } else { high = idx - 1; } } throw new RuntimeException("Could not find " + qoff); } else { // pinned int idx = gammaSeqOffs.getIndexOfLowerBound(qoff); if (idx == -1) { for (int i = 0; i < gammaSeqOffs.length(); i++) { long x = gammaSeqOffs.get(i); if (x > qoff) break; } throw new RuntimeException("(Gamma-version) Could not find " + qoff); } return gammaSeqVertices.get(idx); } } public void queryIn(Long queryId, QueryCallback callback, byte edgeType) { queryIn(queryId, callback, edgeType, false); } private void queryIn(Long queryId, QueryCallback callback, byte edgeType, boolean ignoreType) { if (queryCache != null && callback.immediateReceive()) { long[] cached = (long[]) queryCache.get(incacheKey(queryId, edgeType)); if (cached != null && callback.immediateReceive()) { for (int j = 0; j < cached.length; j++) { long ptr = cached[j]; callback.receiveEdge(VertexIdTranslate.getVertexId(ptr), queryId, edgeType, PointerUtil.encodePointer(shardNum, (int) VertexIdTranslate.getAux(ptr))); } // cacheHitCounter.inc(); return; } else { cacheMissCounter.inc(); } } if (queryId < interval.getFirstVertex() || queryId > interval.getLastVertex()) { throw new IllegalArgumentException("Vertex " + queryId + " not part of interval:" + interval); } final LongBuffer tmpBuffer = adjBuffer.duplicate(); try { /* Step 1: collect adj file offsets for the in-edges */ final Timer.Context _timer1 = inEdgePhase1Timer.time(); ArrayList<Integer> offsets = new ArrayList<Integer>(); int END = (1 << 26) - 1; final IntBuffer startBufferTmp = inEdgeStartBuffer.duplicate(); // Binary search to find the start of the vertex int n = inEdgeStartBuffer.capacity() / 2; int low = 0; int high = n - 1; int off = -1; int queryRelative = (int) (queryId - interval.getFirstVertex()); while (low <= high) { int idx = ((high + low) / 2); int v = startBufferTmp.get(idx * 2); if (v == queryRelative) { off = startBufferTmp.get(idx * 2 + 1); break; } if (v < queryRelative) { low = idx + 1; } else { high = idx - 1; } } if (off == (-1)) { if (queryCache != null && queryCacheSize > queryCache.size()) { queryCache.put(incacheKey(queryId, edgeType), new long[0]); } return; } while (off != END) { tmpBuffer.position(off); long edge = tmpBuffer.get(); if (VertexIdTranslate.getVertexId(edge) != queryId) { System.out.println( "Mismatch in edge linkage: " + VertexIdTranslate.getVertexId(edge) + " !=" + queryId); throw new RuntimeException( "Mismatch in edge linkage: " + VertexIdTranslate.getVertexId(edge) + " !=" + queryId); } if (ignoreType || VertexIdTranslate.getType(edge) == edgeType) { offsets.add(off); } off = (int) VertexIdTranslate.getAux(edge); if (off > END) { throw new RuntimeException("Encoding error: " + edge + " --> " + VertexIdTranslate.getVertexId(edge) + " off : " + VertexIdTranslate.getAux(edge)); } if (off != END && (off < 0 || off > tmpBuffer.capacity())) { System.err.println("Illegal off when looking for inedges: " + off + ", capacity:" + tmpBuffer.capacity() + ", shardNum=" + shardNum); } } _timer1.stop(); /* Step 2: collect the vertex ids that contain the offsets by passing over the pointer data */ /* Find beginning */ ArrayList<Long> inNeighbors = (callback.immediateReceive() ? null : new ArrayList<Long>(offsets.size())); ArrayList<Long> inNeighborsPtrs = (callback.immediateReceive() ? null : new ArrayList<Long>(offsets.size())); ArrayList<Byte> edgeTypes = (callback.immediateReceive() ? null : new ArrayList<Byte>(offsets.size())); ArrayList<Long> cached = (queryCache == null || queryCache.size() >= queryCacheSize || freezeCache ? null : new ArrayList<Long>()); final LongBuffer tmpPointerIdxBuffer = (pinIndexToMemory ? null : pointerIdxBuffer.duplicate()); Iterator<Integer> offsetIterator = offsets.iterator(); if (!offsets.isEmpty()) { final Timer.Context _timer2 = inEdgePhase2Timer.time(); int firstOff = offsets.get(0); if (tmpPointerIdxBuffer != null) { final Timer.Context _timer3 = inEdgeIndexLookupTimer.time(); ShardIndex.IndexEntry startIndex = index.lookupByOffset(firstOff * 8); _timer3.stop(); tmpPointerIdxBuffer.position(startIndex.vertexSeq); } int lastOff = firstOff; while (offsetIterator.hasNext()) { off = offsetIterator.next(); if (off - lastOff > 8196 && tmpPointerIdxBuffer != null) { // magic threshold when to consult the index ShardIndex.IndexEntry skipIdx = index.lookupByOffset(off * 8); if (skipIdx.fileOffset > lastOff) { tmpPointerIdxBuffer.position(skipIdx.vertexSeq); } } long vert = findVertexForOff(off, tmpPointerIdxBuffer); if (!callback.immediateReceive()) { inNeighbors.add(vert); inNeighborsPtrs.add(PointerUtil.encodePointer(shardNum, off)); edgeTypes.add(edgeType); // TODO with wild card } else { callback.receiveEdge(vert, queryId, edgeType, PointerUtil.encodePointer(shardNum, off)); } if (cached != null) { cached.add(VertexIdTranslate.encodeVertexPacket(edgeType, vert, off)); } lastOff = off; } _timer2.stop(); if (cached != null) { long[] cachedArr = new long[cached.size()]; for (int j = 0; j < cached.size(); j++) cachedArr[j] = cached.get(j); queryCache.put(incacheKey(queryId, edgeType), cachedArr); } } if (!callback.immediateReceive()) { callback.receiveInNeighbors(queryId, inNeighbors, edgeTypes, inNeighborsPtrs); } } catch (Exception err) { throw new RuntimeException(err); } } public EdgeIterator edgeIterator() { return edgeIterator(0); } public EdgeIterator edgeIterator(long fromSrcVertex) { if (!pinIndexToMemory) { final LongBuffer iterBuffer = adjBuffer.duplicate(); final LongBuffer iterPointerBuffer = pointerIdxBuffer.duplicate(); if (fromSrcVertex <= 0) { iterBuffer.position(0); iterPointerBuffer.position(0); } else { ShardIndex.IndexEntry entry = null; entry = (pinIndexToMemory ? null : index.lookup(fromSrcVertex)); // ugly PointerPair ptr = findIdxAndPos(fromSrcVertex, entry, iterPointerBuffer, new long[2], true); assert (ptr.cur >= 0); iterBuffer.position((int) ptr.cur); iterPointerBuffer.position(iterPointerBuffer.position() - 1); } return new EdgeIterator() { int idx = iterBuffer.position() - 1; long ptr = (iterPointerBuffer.capacity() > 0 ? iterPointerBuffer.get() : -1); long nextPtr = (iterPointerBuffer.capacity() > 0 ? iterPointerBuffer.get() : -1); long nextOff = VertexIdTranslate.getAux(nextPtr); long curSrc = VertexIdTranslate.getVertexId(ptr); long curDst; byte curType; long vertexPacket; @Override public boolean hasNext() { if (idx < numEdges - 1) { vertexPacket = iterBuffer.get(); if (VertexIdTranslate.isEdgeDeleted(vertexPacket)) { next(); // Skip over deleted edges return hasNext(); } else { return true; } } else { return false; } } @Override public void next() { idx++; if (idx == nextOff) { curSrc = VertexIdTranslate.getVertexId(nextPtr); nextPtr = iterPointerBuffer.get(); nextOff = VertexIdTranslate.getAux(nextPtr); } curDst = VertexIdTranslate.getVertexId(vertexPacket); curType = VertexIdTranslate.getType(vertexPacket); } @Override public long getSrc() { return curSrc; } @Override public long getDst() { return curDst; } @Override public byte getType() { return curType; } @Override public int getIdx() { return idx; } }; } else { if (gammaSeqVertices == null) { return new EdgeIterator() { @Override public boolean hasNext() { return false; } @Override public void next() { } @Override public long getSrc() { return 0; } @Override public long getDst() { return 0; } @Override public byte getType() { return 0; } @Override public int getIdx() { return 0; } }; } long startOff = 0; final LongBuffer iterBuffer = adjBuffer.duplicate(); final Iterator<Long> iterPointerVertices = gammaSeqVertices.iterator(fromSrcVertex); if (fromSrcVertex == 0) { iterBuffer.position(0); } else { int startIdx = gammaSeqVertices.getIndex(fromSrcVertex, true); startOff = gammaSeqOffs.get(startIdx); iterBuffer.position((int) startOff); } final Iterator<Long> iterPointerOffs = gammaSeqOffs.iterator(startOff); return new EdgeIterator() { int idx = iterBuffer.position() - 1; long ptr = (iterPointerVertices.hasNext() ? VertexIdTranslate.encodeVertexPacket((byte) 0, iterPointerVertices.next(), iterPointerOffs.next()) : -1); long nextPtr = (iterPointerVertices.hasNext() ? VertexIdTranslate.encodeVertexPacket((byte) 0, iterPointerVertices.next(), iterPointerOffs.next()) : -1); long nextOff = VertexIdTranslate.getAux(nextPtr); long curSrc = VertexIdTranslate.getVertexId(ptr); long curDst; byte curType; long vertexPacket; @Override public boolean hasNext() { if (idx < numEdges - 1) { vertexPacket = iterBuffer.get(); if (VertexIdTranslate.isEdgeDeleted(vertexPacket)) { next(); // Skip over deleted edges return hasNext(); } else { return true; } } else { return false; } } @Override public void next() { idx++; if (idx == nextOff) { curSrc = VertexIdTranslate.getVertexId(nextPtr); if (iterPointerVertices.hasNext()) { nextPtr = VertexIdTranslate.encodeVertexPacket((byte) 0, iterPointerVertices.next(), iterPointerOffs.next()); } else { // System.out.println("Warning: edgeIterator at " + idx + " but no more left..."); } nextOff = VertexIdTranslate.getAux(nextPtr); } curDst = VertexIdTranslate.getVertexId(vertexPacket); curType = VertexIdTranslate.getType(vertexPacket); } @Override public long getSrc() { return curSrc; } @Override public long getDst() { return curDst; } @Override public byte getType() { return curType; } @Override public int getIdx() { return idx; } }; } } }