package edu.cmu.graphchi.engine.auxdata; import edu.cmu.graphchi.ChiFilenames; import edu.cmu.graphchi.ChiLogger; import ucar.unidata.io.RandomAccessFile; import java.io.EOFException; import java.io.File; import java.io.IOException; /** * Copyright [2012] [Aapo Kyrola, Guy Blelloch, Carlos Guestrin / 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. */ /** * GraphChi keeps track of the degree of each vertex (count of in- and out-edges). This class * allows accessing the vertex degrees efficiently by loading a window * of the edges a time. This supports both sparse and dense representation * of the degrees. This class should not be needed by application writers, and * is only used internally by GraphChi. * @author Aapo Kyrola */ public class DegreeData { private RandomAccessFile degreeFile; private byte[] degreeData; private int vertexSt, vertexEn; private boolean sparse = false; private int lastQuery = 0, lastId = -1; public DegreeData(String baseFilename) throws IOException { File sparseFile = new File(ChiFilenames.getFilenameOfDegreeData(baseFilename, true)); File denseFile = new File(ChiFilenames.getFilenameOfDegreeData(baseFilename, false)); if (sparseFile.exists()) { sparse = true; degreeFile = new RandomAccessFile(sparseFile.getAbsolutePath(), "r"); } else { sparse = false; degreeFile = new RandomAccessFile(denseFile.getAbsolutePath(), "r"); } vertexEn = vertexSt = 0; } /** * Load degrees for an interval of vertices * @param _vertexSt first vertex * @param _vertexEn last vertex (inclusive) * @throws IOException */ public void load(int _vertexSt, int _vertexEn) throws IOException { int prevVertexEn = vertexEn; int prevVertexSt = vertexSt; vertexSt = _vertexSt; vertexEn = _vertexEn; long dataSize = (long) (vertexEn - vertexSt + 1) * 4 * 2; byte[] prevData = degreeData; degreeData = new byte[(int)dataSize]; int len = 0; // Little bit cmoplicated book keeping to avoid redundant reads if (prevVertexEn > _vertexSt && prevVertexSt <= _vertexSt) { // Copy previous len = (int) ((prevVertexEn - vertexSt + 1) * 8); System.arraycopy(prevData, prevData.length - len, degreeData, 0, len); } if (!sparse) { int adjLen = (int) (dataSize - len); if (adjLen == 0) return; long dataStart = (long) vertexSt * 4l * 2l + len; try { degreeFile.seek(dataStart); degreeFile.readFully(degreeData, (int)(dataSize - adjLen), adjLen); } catch (EOFException eof) { ChiLogger.getLogger("engine").info("Error: Tried to read past file: " + dataStart + " --- " + (dataStart + dataSize)); // But continue } } else { if (lastQuery > _vertexSt) { lastId = -1; degreeFile.seek(0); } try { while(true) { int vertexId = (lastId < 0 ? degreeFile.readInt() : lastId); if (vertexId >= _vertexSt && vertexId <= _vertexEn) { degreeFile.readFully(degreeData, (vertexId - vertexSt) * 8, 8); VertexDegree deg = getDegree(vertexId); lastId = -1; } else if (vertexId > vertexEn){ lastId = vertexId; // Remember last one read break; } else { degreeFile.skipBytes(8); } } } catch (EOFException eof) { degreeFile.seek(0); } lastQuery = _vertexEn; } } /** * Returns degree of a vertex. The vertex must be in the previous * interval loaded using load(). * @param vertexId id of the vertex * @return VertexDegree object */ public VertexDegree getDegree(int vertexId) { assert(vertexId >= vertexSt && vertexId <= vertexEn); byte[] tmp = new byte[4]; int idx = vertexId - vertexSt; System.arraycopy(degreeData, idx * 8, tmp, 0, 4); int indeg = ((tmp[3] & 0xff) << 24) + ((tmp[2] & 0xff) << 16) + ((tmp[1] & 0xff) << 8) + (tmp[0] & 0xff); System.arraycopy(degreeData, idx * 8 + 4, tmp, 0, 4); int outdeg = ((tmp[3] & 0xff) << 24) + ((tmp[2] & 0xff) << 16) + ((tmp[1] & 0xff) << 8) + (tmp[0] & 0xff); return new VertexDegree(indeg, outdeg); } }