package edu.cmu.graphchi; import edu.cmu.graphchi.datablocks.BytesToValueConverter; import edu.cmu.graphchi.datablocks.ChiPointer; import edu.cmu.graphchi.datablocks.DataBlockManager; import edu.cmu.graphchi.engine.auxdata.VertexDegree; import sun.misc.Unsafe; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.lang.reflect.Field; import java.security.AccessController; import java.util.concurrent.atomic.AtomicInteger; /** * 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. */ /** * Represents a vertex. Vertex contains a value and a set of in- and out-edges. * @param <VertexValue> * @param <EdgeValue> */ public class ChiVertex<VertexValue, EdgeValue> { /** * To save memory, support now only 32-bit vertex ids. */ private int id; public static DataBlockManager blockManager; public static BytesToValueConverter vertexValueConverter; public static BytesToValueConverter edgeValueConverter; public static boolean disableInedges = false; public static boolean disableOutedges = false; private volatile int nInedges = 0; private int[] inEdgeDataArray = null; private volatile int nOutedges = 0; private int[] outEdgeDataArray = null; /* Internal management */ public boolean parallelSafe = true; /* We replicate the behavior of atomic integer to save some memory and improve performance */ @SuppressWarnings("restriction") // http://stackoverflow.com/questions/13003871/how-do-i-get-the-instance-of-sun-misc-unsafe private static Unsafe getUnsafe() { try { Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); singleoneInstanceField.setAccessible(true); return (Unsafe) singleoneInstanceField.get(null); } catch (IllegalArgumentException e) { throw e; } catch (SecurityException e) { throw e; } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private static final Unsafe unsafe = getUnsafe(); private static final long valueOffset_nInedges; private static final long valueOffset_nOutedges; static { try { valueOffset_nInedges = unsafe.objectFieldOffset (ChiVertex.class.getDeclaredField("nInedges")); valueOffset_nOutedges = unsafe.objectFieldOffset (ChiVertex.class.getDeclaredField("nOutedges")); } catch (Exception ex) { throw new Error(ex); } } private ChiPointer vertexPtr; public ChiVertex(int id, VertexDegree degree) { this.id = id; if (degree != null) { if (!disableInedges) { inEdgeDataArray = new int[degree.inDegree * (edgeValueConverter != null ? 3 : 1)]; } else { nInedges = degree.inDegree; } if (!disableOutedges) { outEdgeDataArray = new int[degree.outDegree * (edgeValueConverter != null ? 3 : 1)]; } else { nOutedges = degree.outDegree; } } } public int getId() { return this.id; } public void setDataPtr(ChiPointer vertexPtr) { this.vertexPtr = vertexPtr; } /** * Access the value of a vertex */ public VertexValue getValue() { return blockManager.dereference(vertexPtr, (BytesToValueConverter<VertexValue>) vertexValueConverter); } /** * Set the value of the vertex. * @param x new value */ public void setValue(VertexValue x) { blockManager.writeValue(vertexPtr, vertexValueConverter, x); } /** * Returns a random out-neighbors vertex id. * @return */ public int getRandomOutNeighbor() { int i = (int) (Math.random() * numOutEdges()); if (edgeValueConverter != null) { int idx = i * 3; return outEdgeDataArray[idx + 2]; } else { return outEdgeDataArray[i]; } } /** * Returns a random neighbor's vertex id. * @return */ public int getRandomNeighbor() { if (numEdges() == 0) { return -1; } int i = (int) (Math.random() * numEdges()); return edge(i).getVertexId(); } public int numOutEdges() { return nOutedges; } public int numInEdges() { return nInedges; } /** * INTERNAL USE ONLY (TODO: separate better) */ public void addInEdge(int chunkId, int offset, int vertexId) { int tmpInEdges; /* Note: it would be nicer to use AtomicInteger, but I want to save as much memory as possible */ for (;;) { int current = nInedges; tmpInEdges = current + 1; if (unsafe.compareAndSwapInt(this, valueOffset_nInedges, current, tmpInEdges)) { break; } } tmpInEdges--; if (edgeValueConverter != null) { int idx = tmpInEdges * 3; inEdgeDataArray[idx] = chunkId; inEdgeDataArray[idx + 1] = offset; inEdgeDataArray[idx + 2] = vertexId; } else { if (inEdgeDataArray != null) inEdgeDataArray[tmpInEdges] = vertexId; } } /** * INTERNAL USE ONLY (TODO: separate better) */ public void addOutEdge(int chunkId, int offset, int vertexId) { int tmpOutEdges; /* Note: it would be nicer to use AtomicInteger, but I want to save as much memory as possible */ for (;;) { int current = nOutedges; tmpOutEdges = current + 1; if (unsafe.compareAndSwapInt(this, valueOffset_nOutedges, current, tmpOutEdges)) { break; } } tmpOutEdges--; if (edgeValueConverter != null) { int idx = tmpOutEdges * 3; outEdgeDataArray[idx] = chunkId; outEdgeDataArray[idx + 1] = offset; outEdgeDataArray[idx + 2] = vertexId; } else { if (outEdgeDataArray != null) outEdgeDataArray[tmpOutEdges] = vertexId; } } /** * Get i'th in-edge * @param i * @return edge object */ public ChiEdge<EdgeValue> inEdge(int i) { if (edgeValueConverter != null) { int idx = i * 3; return new Edge(new ChiPointer(inEdgeDataArray[idx], inEdgeDataArray[idx + 1]), inEdgeDataArray[idx + 2]); } else { return new Edge(null, inEdgeDataArray[i]); } } /** * Get i'th outedge * @param i * @return edge object */ public ChiEdge<EdgeValue> outEdge(int i) { if (edgeValueConverter != null) { int idx = i * 3; return new Edge(new ChiPointer(outEdgeDataArray[idx], outEdgeDataArray[idx + 1]), outEdgeDataArray[idx + 2]); } else { return new Edge(null, outEdgeDataArray[i]); } } /** * Get vertex-id of the i'th out edge (avoid creating the edge-object). * @param i */ public int getOutEdgeId(int i) { if (edgeValueConverter != null) { int idx = i * 3; return outEdgeDataArray[idx + 2]; } else { return outEdgeDataArray[i]; } } /** * Get i'th edge (in- our out-edge). * @param i * @return edge object */ public ChiEdge<EdgeValue> edge(int i) { if (i < nInedges) return inEdge(i); else return outEdge(i - nInedges); } /** * @return the number of in- and out-edges */ public int numEdges() { return nInedges + nOutedges; } /** * ONLY ADVANCED USE */ public int[] getOutNeighborArray() { if (edgeValueConverter != null) { int[] nbrs = new int[numOutEdges()]; for(int i=0; i<nbrs.length; i++) { nbrs[i] = outEdgeDataArray[(i * 3) + 2]; } return nbrs; } else { return outEdgeDataArray.clone(); } } /** * Returns the value of i'th outedge (short-cut to outEdge(i)->getValue()) * @param i * @return the value of i'th outedge (short-cut to outEdge(i)->getValue()) */ public EdgeValue getOutEdgeValue(int i) { int idx = i * 3; return blockManager.dereference(new ChiPointer(outEdgeDataArray[idx], outEdgeDataArray[idx + 1]), (BytesToValueConverter<EdgeValue>) edgeValueConverter); } class Edge implements ChiEdge<EdgeValue> { Edge(ChiPointer dataPtr, int vertexId) { this.dataPtr = dataPtr; this.vertexId = vertexId; } ChiPointer dataPtr; int vertexId; public int getVertexId() { return vertexId; } public EdgeValue getValue() { return blockManager.dereference(dataPtr, (BytesToValueConverter<EdgeValue>) edgeValueConverter); } public void setValue(EdgeValue x) { blockManager.writeValue(dataPtr, edgeValueConverter, x); } } }