/** * CopyRight by Chinamobile * * GraphDataForDisk.java */ package com.chinamobile.bcbsp.graph; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.chinamobile.bcbsp.BSPConfiguration; import com.chinamobile.bcbsp.Constants; import com.chinamobile.bcbsp.api.Edge; import com.chinamobile.bcbsp.api.Vertex; import com.chinamobile.bcbsp.bspstaff.Staff; import com.chinamobile.bcbsp.util.BSPJob; import com.chinamobile.bcbsp.util.BSPJobID; import com.chinamobile.bcbsp.util.ObjectSizer; /** * GraphDataForDisk Graph data manager for disk supported. * * @author * @version */ public class GraphDataForDisk implements GraphDataInterface { // For Log private static final Log LOG = LogFactory.getLog(GraphDataForDisk.class); // For time accumulation private long writeDiskTime = 0; /** Clock */ private long readDiskTime = 0; /** Clock */ private Class<? extends Vertex<?, ?, ?>> vertexClass; private Class<? extends Edge<?, ?>> edgeClass; /** The metadata for a bucket */ class BucketMeta { // Have been accessed by which number of super step public int superStepCount; // Is on disk flag public boolean onDiskFlag; // The length of the bucket by Bytes. public long length; // The length of the part of the bucket still in memory by Bytes. public long lengthInMemory; // Number of all nodes in the bucket. public int count; // Number of active nodes in the bucket public int activeCount; } /** * The beta parameter for the proportion of data memory for the graph data, * 1-beta for messages data */ private float beta; /** * The parameter for the percentage of the heap memory for the data memory * (graph & messages) */ private float dataPercent; /** Hash bucket number */ private int hashBucketNumber; /** Hash buckets of the headnode list */ @SuppressWarnings("unchecked") private ArrayList<ArrayList<Vertex>> hashBuckets; /** * Bitmaps for each hash bucket, the bits is sorted(from right) the same as * the list of nodes in each bucket. A int(4 bytes) represents 32 head * nodes. */ private ArrayList<ArrayList<Integer>> bitmaps; /** Meta data table for hash buckets */ private ArrayList<BucketMeta> metaTable; private final ObjectSizer sizer; // Object sizer. private int sizeOfVertex; // Size of Vertex type instance.(Bytes) private int sizeOfEdge; // Size of Edge type instance.(Bytes) private long totalSizeOfVertex; // Total size of Vertex.(Bytes) private int totalCountOfVertex; // Total count of Vertex. @SuppressWarnings("unused") private long totalSizeOfEdge; // Total size of Edge.(Bytes) private int totalCountOfEdge; // Total count of Edge. private final int sizeOfRef; // Size of a reference. private final int sizeOfInteger; // Size of an Integer. private long sizeOfGraphSpace; // The total space for graph data.(Bytes) private long sizeThreshold; // The threshold size for graph data.(Bytes) private long sizeOfGraphDataInMem; // The current size of graph data.(Bytes) private long sizeOfBitmapsInMem; // The size of bitmaps in memory.(Bytes) private long sizeOfMetaTable; // The size of metaTable in memory.(Bytes) private BSPJobID jobID; private int partitionID; private File fileRoot; private File graphDataFile; private File graphDataFile_bucket; private FileReader fr_graphData; private BufferedReader br_graphData; private FileWriter fw_graphData; private BufferedWriter bw_graphData; private int sizeForAll; // Total number of head nodes. private int[] sortedBucketIndexList; private int currentBucketIndex; // Current pointer of bucket index for // traversal. private int currentNodeIndex; // Current pointer of node index in bucket for // traversal. private Staff staff; public GraphDataForDisk(){ //initialize some const variables BSPConfiguration conf = new BSPConfiguration(); if (conf.getInt(Constants.BC_BSP_JVM_VERSION, 32) == 64) { sizer = ObjectSizer.forSun64BitsVM(); } else { sizer = ObjectSizer.forSun32BitsVM(); } this.sizeOfRef = sizer.sizeOfRef(); this.sizeOfInteger = sizer.sizeOf(new Integer(0)); } public void setStaff(Staff staff) { this.staff = staff; } public void initialize() { BSPJob job = this.staff.getConf(); int partitionID = this.staff.getPartition(); initialize(job, partitionID); } /** * * * @param job * @param partitionID */ @SuppressWarnings("unchecked") public void initialize(BSPJob job, int partitionID) { vertexClass = job.getVertexClass(); edgeClass = job.getEdgeClass(); LOG.info("========== Initializing Graph Data For Disk =========="); this.dataPercent = job.getMemoryDataPercent(); // Default 0.8 this.jobID = job.getJobID(); this.partitionID = partitionID; this.beta = job.getBeta(); this.hashBucketNumber = job.getHashBucketNumber(); LOG.info("[beta] = " + this.beta); LOG.info("[hashBucketNumber] = " + this.hashBucketNumber); this.hashBuckets = new ArrayList<ArrayList<Vertex>>(hashBucketNumber); // So the bitmaps's length decides the maximum nodes of a bucket is // 320*32. this.bitmaps = new ArrayList<ArrayList<Integer>>(hashBucketNumber); this.metaTable = new ArrayList<BucketMeta>(hashBucketNumber); // Initialize the meta table and bitmaps. for (int i = 0; i < hashBucketNumber; i++) { this.hashBuckets.add(null); // init the meta table. BucketMeta meta = new BucketMeta(); meta.superStepCount = -1; meta.onDiskFlag = false; meta.length = 0; meta.lengthInMemory = 0; meta.count = 0; meta.activeCount = 0; metaTable.add(meta); // init the bitmapsCache. ArrayList<Integer> bitmap = new ArrayList<Integer>( Constants.GRAPH_BITMAP_BUCKET_NUM_BYTES); for (int j = 0; j < Constants.GRAPH_BITMAP_BUCKET_NUM_BYTES; j++) bitmap.add(0); this.bitmaps.add(bitmap); } // Initialize the size of objects and data structures. int sizeOfMetaBucket = sizer.sizeOf(new BucketMeta()); this.sizeOfMetaTable = (sizeOfMetaBucket + sizeOfRef) * hashBucketNumber; int sizeOfBitmap = sizer.sizeOf(new ArrayList<Integer>()); this.sizeOfBitmapsInMem = (sizeOfBitmap + sizeOfRef) * hashBucketNumber; Vertex<?, ?, ?> tmpVertex = null; Edge<?, ?> tmpEdge = null; try { tmpVertex = this.vertexClass.newInstance(); tmpEdge = this.edgeClass.newInstance(); } catch (InstantiationException e) { LOG.error("[GraphDataForDisk] caught: ", e); } catch (IllegalAccessException e) { LOG.error("[GraphDataForDisk] caught: ", e); } this.sizeOfVertex = sizer.sizeOf(tmpVertex); this.sizeOfEdge = sizer.sizeOf(tmpEdge); LOG.info("[Default initial size of Vertex] = " + this.sizeOfVertex + "B"); LOG.info("[Default initial size of Edge] = " + this.sizeOfEdge + "B"); // Get the memory mxBean. MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); // Get the heap memory usage. MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage(); long maxHeapSize = memoryUsage.getMax(); LOG.info("[JVM max Heap size] = " + maxHeapSize / 1048576 + "MB"); this.sizeOfGraphSpace = ( long ) (maxHeapSize * dataPercent * beta); this.sizeThreshold = ( long ) (sizeOfGraphSpace); this.sizeOfGraphDataInMem = 0; this.sizeForAll = 0; this.totalSizeOfVertex = 0; this.totalCountOfVertex = 0; this.totalSizeOfEdge = 0; this.totalCountOfEdge = 0; this.sortedBucketIndexList = new int[hashBucketNumber]; this.fileRoot = new File("/tmp/bcbsp/" + this.jobID.toString() + "/" + "partition-" + this.partitionID); this.graphDataFile = new File(this.fileRoot + "/" + "GraphData"); LOG.info("[size of Graph Data Space] = " + this.sizeOfGraphSpace / 1048576 + "MB"); LOG.info("[threshold of Graph Data] = " + this.sizeThreshold / 1048576 + "MB"); LOG.info("======================================================"); } @SuppressWarnings("unchecked") @Override public synchronized void addForAll(Vertex vertex) { this.sizeForAll++; String vertexID = String.valueOf(vertex.getVertexID()); int hashCode = vertexID.hashCode(); int hashIndex = hashCode % this.hashBucketNumber; // bucket index hashIndex = (hashIndex < 0 ? hashIndex + this.hashBucketNumber : hashIndex); /** Add the vertex to the right hash bucket. */ ArrayList<Vertex> hashBucket = this.hashBuckets.get(hashIndex); // When the hash bucket reference is null, create a bucket. if (hashBucket == null) { hashBucket = new ArrayList<Vertex>(); } hashBucket.add(vertex); this.hashBuckets.set(hashIndex, hashBucket); // Evaluate the memory size of the new Vertex. int newVertexSize = this.sizeOfRef + this.sizer.sizeOf(vertex); this.totalSizeOfVertex += newVertexSize; this.totalCountOfVertex++; this.totalCountOfEdge += vertex.getEdgesNum(); /** Add the vertex's size to the length of the bucket's meta. */ BucketMeta meta = this.metaTable.get(hashIndex); meta.length = meta.length + newVertexSize; meta.lengthInMemory = meta.lengthInMemory + newVertexSize; meta.count = meta.count + 1; meta.activeCount = meta.activeCount + 1; this.metaTable.set(hashIndex, meta); /** Add the headnode's size to the graph data's size. */ this.sizeOfGraphDataInMem = this.sizeOfGraphDataInMem + newVertexSize; /** Set the bitmap's refered bit to 1 */ setBitmapTrue(hashIndex, meta.activeCount - 1, this.bitmaps); // Add a new Integer into the bitmaps every 8 nodes added. if (this.sizeForAll % 32 == 1) { this.sizeOfBitmapsInMem = this.sizeOfBitmapsInMem + this.sizeOfInteger + this.sizeOfRef; } onVertexAdded(); } @SuppressWarnings("unchecked") @Override public Vertex get(int index) { // When it's the first time to access head node, // sort the buckets into an order. if (index == 0) { sortBucketsForAll(); } // Locate the bucket for the index. int activeCount = 0; int i = 0; while ((activeCount = this.metaTable.get(this.sortedBucketIndexList[i]).activeCount) < index + 1) { index = index - activeCount; i++; } int bucketIndex = this.sortedBucketIndexList[i]; // Locate the nodeIndex in bucket for the index. int nodeIndex = 0; // prictical node index. int counter = 0; // active counter. while (counter < index + 1) { // If the bit for the nodeIndex is active if (getBitmap(bucketIndex, nodeIndex, this.bitmaps)) { counter++; } nodeIndex++; } nodeIndex = nodeIndex - 1; // If the bucket is on disk. if (this.metaTable.get(bucketIndex).onDiskFlag) { long bucketLength = this.metaTable.get(bucketIndex).length; long idleLength = 0; // Idle space that can be swap out. // Look up the bucket before the i bucket for in-memory bucket's // space. // Swap the in-memory buckets out on disk until the idleLength // enough // for the on-disk bucket to swap in. for (int j = 0; j < i; j++) { int m = this.sortedBucketIndexList[j]; // If the m bucket is in memory, swap it out, // and add the length to the idleLength. if (!this.metaTable.get(m).onDiskFlag) { idleLength = idleLength + this.metaTable.get(m).length; try { saveBucket(m); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } // When idle is enough, break the for. if (idleLength >= bucketLength) { break; } }// end-for /** Load bucket */ try { loadBucket(bucketIndex); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } // For set method use. this.currentBucketIndex = bucketIndex; this.currentNodeIndex = nodeIndex; return this.hashBuckets.get(bucketIndex).get(nodeIndex); } @SuppressWarnings("unchecked") @Override public Vertex getForAll(int index) { // When it's the first time to access head node, // sort the buckets into an order. if (index == 0) { sortBucketsForAll(); } // Locate the bucket for the index. int count = 0; int i = 0; while ((count = this.metaTable.get(this.sortedBucketIndexList[i]).count) < index + 1) { index = index - count; // Set the accessed bucket's referrence to null. // this.hashBuckets.set(this.sortedBucketIndexList[i], null); i++; } int bucketIndex = this.sortedBucketIndexList[i]; // If the bucket is on disk. if (this.metaTable.get(bucketIndex).onDiskFlag) { long bucketLength = this.metaTable.get(bucketIndex).length; long idleLength = this.sizeThreshold - this.sizeOfGraphDataInMem; // Idle space that can be swap out. // Look up the bucket before the i bucket for in-memory bucket's space. // Swap the in-memory buckets out on disk until the idleLength // enough for the on-disk bucket to swap in. for (int j = 0; j < i; j++) { // When idle is enough, break the for. if (idleLength >= bucketLength) { break; } int m = this.sortedBucketIndexList[j]; // If the m bucket is in memory, swap it out, // and add the length to the idleLength. if (!this.metaTable.get(m).onDiskFlag) { idleLength = idleLength + this.metaTable.get(m).length; try { saveBucket(m); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } }// end-for /** Load bucket */ try { loadBucket(bucketIndex); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } // For set method use. this.currentBucketIndex = bucketIndex; this.currentNodeIndex = index; return this.hashBuckets.get(bucketIndex).get(index); } @SuppressWarnings("unchecked") @Override public void set(int index, Vertex vertex, boolean activeState) { this.hashBuckets.get(this.currentBucketIndex).set( this.currentNodeIndex, vertex); boolean oldActiveState = this.getBitmap(this.currentBucketIndex, this.currentNodeIndex, this.bitmaps); if (activeState) { if (!oldActiveState) { this.setBitmapTrue(this.currentBucketIndex, this.currentNodeIndex, this.bitmaps); this.metaTable.get(this.currentBucketIndex).activeCount++; } } else { if (oldActiveState) { this.setBitmapFalse(this.currentBucketIndex, this.currentNodeIndex, this.bitmaps); this.metaTable.get(this.currentBucketIndex).activeCount--; } } } @Override public int size() { int count = 0; for (BucketMeta meta : this.metaTable) { count = count + meta.activeCount; } return count; } @Override public int sizeForAll() { return this.sizeForAll; } private void onVertexAdded() { // When the graph data's size exceeds the threshold. if (this.sizeOfGraphDataInMem + this.sizeOfBitmapsInMem >= this.sizeThreshold) { // LOG.info("[size fo Graph Data In Memory] = " + // this.sizeOfGraphDataInMem/1024 + "KB"); // If the root dir does not exit, create it. if (!this.fileRoot.exists()) { this.fileRoot.mkdirs(); } // If the graph data dir does not exit, create it. if (!this.graphDataFile.exists()) { this.graphDataFile.mkdir(); } int bucketIndex = findLongestBucket(); try { saveBucket(bucketIndex); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } } /** * Find the longest bucket now in the memory. * * @return bucketIndex */ private int findLongestBucket() { int bucketIndex = 0; long longestLength = 0; for (int i = 0; i < this.hashBucketNumber; i++) { BucketMeta meta = metaTable.get(i); // Find the longest but lengthInMemory is not zero. if (meta.length > longestLength && meta.lengthInMemory != 0) { longestLength = meta.length; bucketIndex = i; } } return bucketIndex; } /** * Save the given bucket to the disk file. * * @param index * of the bucket * @throws IOException */ @SuppressWarnings("unchecked") private void saveBucket(int index) throws IOException { long start = System.currentTimeMillis(); /** Clock */ this.graphDataFile_bucket = new File(this.graphDataFile + "/" + "bucket-" + index); boolean isNewFile = false; // The bucket file does not exit, create it. if (!this.graphDataFile_bucket.exists()) { this.graphDataFile_bucket.createNewFile(); isNewFile = true; } // Append to the bucket file by line. this.fw_graphData = new FileWriter(graphDataFile_bucket, true); this.bw_graphData = new BufferedWriter(fw_graphData, 65536); if (isNewFile) { // Write the file header. this.bw_graphData.write(Constants.GRAPH_BUCKET_FILE_HEADER + "-" + index); } ArrayList<Vertex> hashBucket = this.hashBuckets.get(index); for (int i = 0; i < hashBucket.size(); i++) { this.bw_graphData.newLine(); this.bw_graphData.write(hashBucket.get(i).intoString()); } this.bw_graphData.close(); this.fw_graphData.close(); // Update the meta data for the bucket. BucketMeta meta = metaTable.get(index); // Update the size fo graph data. this.sizeOfGraphDataInMem = this.sizeOfGraphDataInMem - meta.lengthInMemory; meta.onDiskFlag = true; // Set the on disk flag true. meta.lengthInMemory = 0; // Set the length in memory to 0. metaTable.set(index, meta); // Set the bucket's reference in the list to null. this.hashBuckets.set(index, null); this.writeDiskTime = this.writeDiskTime + (System.currentTimeMillis() - start); /** Clock */ } /** * Load the given bucket from disk file into the memory array list. * * @param index * @throws IOException */ @SuppressWarnings("unchecked") private void loadBucket(int index) throws IOException { long start = System.currentTimeMillis(); /** Clock */ this.graphDataFile_bucket = new File(this.graphDataFile + "/" + "bucket-" + index); if (!this.graphDataFile_bucket.exists()) { throw new IOException("Bucket file does not exit!"); } // Open file readers. this.fr_graphData = new FileReader(this.graphDataFile_bucket); this.br_graphData = new BufferedReader(this.fr_graphData); // Read the file header. @SuppressWarnings("unused") String bucketHeader = this.br_graphData.readLine(); ArrayList<Vertex> hashBucket = this.hashBuckets.get(index); if (hashBucket == null) { hashBucket = new ArrayList<Vertex>(); } String vertexData; try { while ((vertexData = this.br_graphData.readLine()) != null) { Vertex vertex = this.vertexClass.newInstance(); vertex.fromString(vertexData); hashBucket.add(vertex); } } catch (Exception e) { LOG.error("[GraphDataForDisk] caught: ", e); } this.br_graphData.close(); this.fr_graphData.close(); // Update the meta data for the bucket. BucketMeta meta = metaTable.get(index); // Update the size of graph data. this.sizeOfGraphDataInMem = this.sizeOfGraphDataInMem + (meta.length - meta.lengthInMemory); meta.onDiskFlag = false; meta.lengthInMemory = meta.length; metaTable.set(index, meta); this.hashBuckets.set(index, hashBucket); if (!this.graphDataFile_bucket.delete()) { throw new IOException("Bucket file delete failed!"); } this.readDiskTime = this.readDiskTime + (System.currentTimeMillis() - start); /** Clock */ } /** * Set the bitmap's bit for the hashIndex hash bucket's nodeIndex headnode * to be 1. * * @param hashIndex * @param nodeIndex * @param aBitmaps */ private void setBitmapTrue(int hashIndex, int nodeIndex, ArrayList<ArrayList<Integer>> aBitmaps) { // Get the refered bitmap of the bucket. ArrayList<Integer> bitmap = aBitmaps.get(hashIndex); // The node bit belongs to the point int element of the array. int point = ( int ) (nodeIndex / 32);// a int has 32 bits. // If the nodeIndex over the bitmap's size, append new 32 bit. if ((point + 1) > bitmap.size()) { bitmap.add(new Integer(0)); } // The bit shift number for the int element. int shift = 31 - nodeIndex % 32; // The unit for the int element to or. int orUnit = 1; orUnit = orUnit << shift; // Or the int element with the unit to set the bit to 1. bitmap.set(point, bitmap.get(point) | orUnit); // Set back the bitmap into the bucket. aBitmaps.set(hashIndex, bitmap); } /** * Set the bitmap's bit for the hashIndex hash bucket's nodeIndex headnode * to be 0. * * @param hashIndex * @param nodeIndex * @param aBitmaps */ private void setBitmapFalse(int hashIndex, int nodeIndex, ArrayList<ArrayList<Integer>> aBitmaps) { // Get the refered bitmap of the bucket. ArrayList<Integer> bitmap = aBitmaps.get(hashIndex); // The node bit belongs to the point int element of the array. int point = ( int ) (nodeIndex / 32);// a int has 32 bits. // If the nodeIndex over the bitmap's size, append new 32 bit. if ((point + 1) > bitmap.size()) { bitmap.add(new Integer(0)); } // The bit shift number for the int element. int shift = 31 - nodeIndex % 32; // The unit for the int element to and. int andUnit = 1; andUnit = andUnit << shift; andUnit = ~andUnit; // And the int element with the unit to set the bit to 0. bitmap.set(point, bitmap.get(point) & andUnit); // Set back the bitmap into the bucket. aBitmaps.set(hashIndex, bitmap); } /** * Get the bitmap's bit for the hashIndex hash bucket's nodeIndex * headnode.If it's 1, returns true, otherwise, returns false. * * @param hashIndex * @param nodeIndex * @param aBitmaps * @return */ private boolean getBitmap(int hashIndex, int nodeIndex, ArrayList<ArrayList<Integer>> aBitmaps) { // Get the refered bitmap of the bucket. ArrayList<Integer> bitmap = aBitmaps.get(hashIndex); // The node bit belongs to the point int element of the array. int point = ( int ) (nodeIndex / 32);// a int has 32 bits. // The bit shift number for the int element. int shift = 31 - nodeIndex % 32; // The unit for the int element to and. int andUnit = 1; andUnit = andUnit << shift; // And the int element with the unit to get the referred bit. int result = bitmap.get(point) & andUnit; if (result == 0) { return false; } else return true; } /** * Sort the buckets for traversing all the nodes. After this operation, the * buckets are in a order as in-memory buckets descending by length at * first, and on-disk buckets ascending by length at last. */ private void sortBucketsForAll() { int begin = 0; int end = this.hashBucketNumber - 1; // 1st loop, sink all the on disk buckets to end. for (int i = 0; i < this.hashBucketNumber; i++) { BucketMeta meta = this.metaTable.get(i); if (meta.onDiskFlag) { // The on disk bucket should sink. this.sortedBucketIndexList[end] = i; end--; } else { this.sortedBucketIndexList[begin] = i; begin++; } } // come here, end points to the end of buckets in memory; // begin points to the start of the buckets on disk. // 2nd loop, bubble sort the in memory buckets by length descending. for (int i = 0; i < end; i++) { for (int j = i + 1; j <= end; j++) { if (this.metaTable.get(this.sortedBucketIndexList[i]).length < this.metaTable .get(this.sortedBucketIndexList[j]).length) { // Swap int temp = this.sortedBucketIndexList[i]; this.sortedBucketIndexList[i] = this.sortedBucketIndexList[j]; this.sortedBucketIndexList[j] = temp; } } } // 3rd loop, bubble sort the on disk buckets by length ascending. for (int i = begin; i < this.hashBucketNumber - 1; i++) { for (int j = i + 1; j <= this.hashBucketNumber - 1; j++) { if (this.metaTable.get(this.sortedBucketIndexList[i]).length > this.metaTable .get(this.sortedBucketIndexList[j]).length) { // Swap int temp = this.sortedBucketIndexList[i]; this.sortedBucketIndexList[i] = this.sortedBucketIndexList[j]; this.sortedBucketIndexList[j] = temp; } } } }// end-sortBucketsForAll @SuppressWarnings("unchecked") @Override public void clean() { for (int i = 0; i < this.hashBucketNumber; i++) { this.graphDataFile_bucket = new File(this.graphDataFile + "/" + "bucket-" + i); if (this.graphDataFile_bucket.exists()) { if (!this.graphDataFile_bucket.delete()) { LOG.warn("[File] Delete file:" + this.graphDataFile_bucket + " failed!"); } } } if (this.graphDataFile.exists()) { if (!this.graphDataFile.delete()) { LOG.warn("[File] Delete directory:" + this.graphDataFile + " failed!"); } } this.hashBuckets = new ArrayList<ArrayList<Vertex>>(hashBucketNumber); // So the bitmaps's length decides the maximum nodes of a bucket is // 320*32. this.bitmaps = new ArrayList<ArrayList<Integer>>(hashBucketNumber); this.metaTable = new ArrayList<BucketMeta>(hashBucketNumber); // Initialize the meta table and bitmaps. for (int i = 0; i < hashBucketNumber; i++) { this.hashBuckets.add(null); // init the meta table. BucketMeta meta = new BucketMeta(); meta.superStepCount = -1; meta.onDiskFlag = false; meta.length = 0; meta.lengthInMemory = 0; meta.count = 0; meta.activeCount = 0; // meta.activeCountCache = 0; metaTable.add(meta); // init the bitmapsCache. ArrayList<Integer> bitmap = new ArrayList<Integer>( Constants.GRAPH_BITMAP_BUCKET_NUM_BYTES); for (int j = 0; j < Constants.GRAPH_BITMAP_BUCKET_NUM_BYTES; j++) bitmap.add(0); this.bitmaps.add(bitmap); } int sizeOfMetaBucket = sizer.sizeOf(new BucketMeta()); this.sizeOfMetaTable = (sizeOfMetaBucket + sizeOfRef) * hashBucketNumber; int sizeOfBitmap = sizer.sizeOf(new ArrayList<Integer>()); this.sizeOfBitmapsInMem = (sizeOfBitmap + sizeOfRef) * hashBucketNumber; Vertex<?, ?, ?> tmpVertex = null; Edge<?, ?> tmpEdge = null; try { tmpVertex = this.vertexClass.newInstance(); tmpEdge = this.edgeClass.newInstance(); } catch (InstantiationException e) { LOG.error("[GraphDataForDisk] caught: ", e); } catch (IllegalAccessException e) { LOG.error("[GraphDataForDisk] caught: ", e); } this.sizeOfVertex = sizer.sizeOf(tmpVertex); this.sizeOfEdge = sizer.sizeOf(tmpEdge); // Get the memory mxBean. MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); // Get the heap memory usage. MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage(); long maxHeapSize = memoryUsage.getMax(); this.sizeOfGraphSpace = ( long ) (maxHeapSize * dataPercent * beta); this.sizeThreshold = ( long ) (sizeOfGraphSpace); this.sizeOfGraphDataInMem = 0; this.sizeForAll = 0; this.totalSizeOfVertex = 0; this.totalCountOfVertex = 0; this.totalSizeOfEdge = 0; this.totalCountOfEdge = 0; this.sortedBucketIndexList = new int[hashBucketNumber]; this.fileRoot = new File("/tmp/bcbsp/" + this.jobID.toString() + "/" + "partition-" + this.partitionID); this.graphDataFile = new File(this.fileRoot + "/" + "GraphData"); } @Override public void finishAdd() { if (this.totalCountOfVertex > 0) this.sizeOfVertex = ( int ) (this.totalSizeOfVertex / this.totalCountOfVertex); LOG.info("[Graph Data For Disk] Finish loading data!!!"); showMemoryInfo(); this.sizeThreshold = this.sizeThreshold - this.sizeOfBitmapsInMem; // Sort the buckets for all in memory buckets on the left // descending by length, and the buckets with part on disk // on the right ascending by length. sortBucketsForAll(); // Traverse from right to left, and try best to load buckets // that part on disk all into memory, if too big for memory // to contain, then write it the whole on to disk. for (int i = this.hashBucketNumber - 1; i >= 0; i--) { int bucketIndex = this.sortedBucketIndexList[i]; BucketMeta meta = this.metaTable.get(bucketIndex); // If the bucket has part on disk and part in memory. if (meta.lengthInMemory > 0 && meta.lengthInMemory < meta.length) { long sizeOnDisk = meta.length - meta.lengthInMemory; // The memory still has enough space for the size on disk. if (sizeOnDisk < (this.sizeThreshold - this.sizeOfGraphDataInMem)) { try { loadBucket(bucketIndex); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } else { // The memory cannot contain the size on disk. try { saveBucket(bucketIndex); } catch (IOException e) { LOG.error("[GraphDataForDisk] caught: ", e); } } } }// end-for this.showSortedHashBucketsInfo(); } @Override public long getActiveCounter() { int count = 0; for (BucketMeta meta : this.metaTable) { count = count + meta.activeCount; } return count; } @Override public boolean getActiveFlagForAll(int index) { return getBitmap(this.currentBucketIndex, this.currentNodeIndex, this.bitmaps); } public void showMemoryInfo() { LOG.info("----------------- Memory Info of Graph -----------------"); MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage(); long used = memoryUsage.getUsed(); long committed = memoryUsage.getCommitted(); LOG.info("<Real> [Memory used] = " + used / 1048576 + "MB"); LOG.info("<Real> [Memory committed] = " + committed / 1048576 + "MB"); LOG.info("<Evaluate> [size of Vertex] = " + this.sizeOfVertex + "B"); LOG.info("<Evaluate> [total size of Vertex] = " + this.totalSizeOfVertex / 1048576 + "MB"); LOG.info("<Evaluate> [total count of Vertex] = " + this.totalCountOfVertex); LOG.info("<Evaluate> [total count of Edge] = " + this.totalCountOfEdge); LOG.info("<Evaluate> [size fo MetaTable In Memory] = " + this.sizeOfMetaTable / 1024 + "KB"); LOG.info("<Evaluate> [size of Graph Data In Memory] = " + this.sizeOfGraphDataInMem / 1048576 + "MB"); LOG.info("<Evaluate> [size fo Bitmaps In Memory] = " + this.sizeOfBitmapsInMem / 1024 + "KB"); LOG.info("<Evaluate> [size of Graph Data Threshold] = " + this.sizeThreshold / 1048576 + "MB"); LOG.info("----------------- -------------------- -----------------"); this.showHashBucketsInfo(); LOG.info("[==>Clock<==] <GraphDataForDisk: save bucket> totally used " + this.writeDiskTime / 1000f + " seconds"); /** Clock */ LOG.info("[==>Clock<==] <GraphDataForDisk: load bucket> totally used " + this.readDiskTime / 1000f + " seconds"); /** Clock */ LOG.info("[==>Clock<==] <GraphDataForDisk: Disk I/O> totally used " + (this.writeDiskTime + this.readDiskTime) / 1000f + " seconds"); /** Clock */ this.writeDiskTime = 0; /** Clock */ this.readDiskTime = 0; /** Clock */ } private void showHashBucketsInfo() { LOG.info("------------ Buckets Info of Graph ------------"); long maxLength = 0; for (int i = 0; i < this.metaTable.size(); i++) { BucketMeta meta = this.metaTable.get(i); if (meta.length > maxLength) { maxLength = meta.length; } } for (int i = 0; i < this.metaTable.size(); i++) { BucketMeta meta = this.metaTable.get(i); String out = "[Bucket-" + i + "] "; if (meta.onDiskFlag) { out = out + "OnDisk "; } else { out = out + " "; } out = out + meta.lengthInMemory / 1048576 + "MB - " + meta.length / 1048576 + "MB "; int nMax = 30; int nAll = ( int ) (nMax * (( float ) meta.length / ( float ) maxLength)); int nMem = ( int ) (nAll * (( float ) meta.lengthInMemory / ( float ) meta.length)); int nDisk = nAll - nMem; for (int j = 0; j < nMem; j++) { out = out + "-"; } for (int j = 0; j < nDisk; j++) { out = out + "*"; } LOG.info(out); } LOG.info("------------ --------------------- ------------"); } private void showSortedHashBucketsInfo() { LOG.info("------------ Buckets Info of Graph ------------"); long maxLength = 0; for (int i = 0; i < this.metaTable.size(); i++) { BucketMeta meta = this.metaTable.get(i); if (meta.length > maxLength) { maxLength = meta.length; } } for (int i = 0; i < this.sortedBucketIndexList.length; i++) { int p = this.sortedBucketIndexList[i]; BucketMeta meta = this.metaTable.get(p); String out = "[Bucket-" + p + "] "; if (meta.onDiskFlag) { out = out + "OnDisk "; } else { out = out + " "; } out = out + meta.lengthInMemory / 1048576 + "MB - " + meta.length / 1048576 + "MB "; int nMax = 30; int nAll = ( int ) (nMax * (( float ) meta.length / ( float ) maxLength)); int nMem = ( int ) (nAll * (( float ) meta.lengthInMemory / ( float ) meta.length)); int nDisk = nAll - nMem; for (int j = 0; j < nMem; j++) { out = out + "-"; } for (int j = 0; j < nDisk; j++) { out = out + "*"; } LOG.info(out); } LOG.info("------------ --------------------- ------------"); } @Override public int getEdgeSize() { return this.totalCountOfEdge; } }