package edu.ucsb.jpregel.system;
import api.WorkerGraphMaker;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.StringTokenizer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import util.Log2;
import vertices.VertexShortestPathBinaryTree;
/**
* Constructs the nodes associated with a particular Worker.
*
* @author Pete Cappello
*/
public class WorkerGraphMakerBinaryTree2 extends WorkerGraphMaker
{
private final VertexShortestPathBinaryTree vertexFactory = new VertexShortestPathBinaryTree();
private int numParts;
private int numVertices;
private int numPartsPerWorker;
// variable cache
private int lgNumParts;
private int treeHeight;
private int partTreeHeight;
private int numPartTreeFullDepthLeaves;
@Override
public int makeGraph(Worker worker)
{
int numVerticesMade = 0;
Job job = worker.getJob();
FileSystem fileSystem = job.getFileSystem();
try
{
BufferedReader bufferedReader = fileSystem.getWorkerInputFileInputStream( worker.getWorkerNum() );
// read file
String line = bufferedReader.readLine();
bufferedReader.close();
// extract numVertices, numWorkers
StringTokenizer stringTokenizer = new StringTokenizer( line );
numVertices = getToken( stringTokenizer );
int numWorkers = getToken( stringTokenizer );
int numCores = Runtime.getRuntime().availableProcessors();
numPartsPerWorker = 2 * numCores;
treeHeight = Log2.lg( numVertices );
numParts = numWorkers * numPartsPerWorker;
lgNumParts = Log2.lg( numParts );
partTreeHeight = treeHeight - lgNumParts;
numPartTreeFullDepthLeaves = 1 << partTreeHeight;
// System.out.println("WorkerGraphMakerBinaryTree2.make: workerNum: " + worker.getWorkerNum() +
// " numVertices: " + numVertices + " treeHeight: " + treeHeight +
// " numParts: " + numParts + " lgNumParts: " + lgNumParts + " partTreeHeight: " + partTreeHeight
// + " numPartTreeFullDepthLeaves: " + numPartTreeFullDepthLeaves);
// determine my partitions
int myStartPartId = (worker.getWorkerNum() - 1) * numPartsPerWorker;
int myStopPartId = myStartPartId + numPartsPerWorker - 1;
// System.out.println("WorkerGraphMakerBinaryTree2.make: workerNum: " + worker.getWorkerNum()
// + ", numVertices: " + numVertices
// + ", numWorkers: " + numWorkers + ", numCores: " + numCores
// + ", numParts: " + numParts
// + ", myStartPart: " + myStartPartId + ", myStopPart: " + myStopPartId);
// for each of my partitions, p, produce p's vertices.
ExecutorService executorService = Executors.newFixedThreadPool( numCores );
// System.out.println("make: workerNum: " + worker.getWorkerNum() + " myStartPartId: " + myStartPartId);
for ( int partId = myStartPartId; partId <= myStopPartId; partId++ )
{
int partSize = getPartSize( partId );
// System.out.println("WorkerGraphMakerBinaryTree2.makeGraph: partId: " + partId
// + " partSize: " + partSize);
numVerticesMade += partSize;
Callable<Object> task = new PopulatePart( worker, partId, partSize );
executorService.submit( task );
}
executorService.shutdown();
executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.DAYS );
}
catch ( Exception exception )
{
System.err.println( "GridWorkerGraphMaker.makeGraph: Error: " + exception.getMessage());
exception.printStackTrace();
System.exit( 1 );
}
return numVerticesMade;
}
@Override
public int getWorkerNum( int partId, int numWorkers )
{
return partId / numPartsPerWorker + 1;
}
private int getPartSize( int partId )
{
// System.out.println("getPartSize: partId: " + partId +
// " taprootSize: " + getTaprootSize( partId ) +
// " numPartTreeFullDepthLeaves - 1: " + (numPartTreeFullDepthLeaves - 1) +
// " numPartFullDepthLeaves: " + numPartFullDepthLeaves( partId ) );
return getTaprootSize( partId ) + numPartTreeFullDepthLeaves - 1 + numPartFullDepthLeaves( partId );
}
private int numPartFullDepthLeaves( int partId )
{
int leftFullDepthLeafId = ( numParts + partId ) << partTreeHeight;
// System.out.print(" numPartFullDepthLeaves: partId: " + partId + " leftFullDepthLeafId: " + leftFullDepthLeafId);
if ( numVertices < leftFullDepthLeafId )
{
return 0;
}
if ( numVertices >= leftFullDepthLeafId + numPartTreeFullDepthLeaves )
{
return numPartTreeFullDepthLeaves;
}
return numVertices - leftFullDepthLeafId + 1;
}
private int getToken( StringTokenizer stringTokenizer ) throws IOException
{
if ( ! stringTokenizer.hasMoreTokens() )
{
System.err.println( "GridWorkerGraphMaker.makeGraph: getToken: Empty lines are not allowed." );
throw new IOException();
}
return Integer.parseInt( stringTokenizer.nextToken() );
}
private int getTaprootSize( int partNum )
{
return ( partNum == 0 ) ? lgNumParts : Integer.numberOfTrailingZeros( partNum );
}
private class PopulatePart implements Callable<Object>
{
private final Worker worker;
private final int partId;
private final int partSize;
public PopulatePart( Worker worker, int partId, int partSize )
{
this.worker = worker;
this.partId = partId;
this.partSize = partSize;
}
@Override
public Object call()
{
int numVerticesMade = 0;
// make taproot vertices
int vertexId = numParts + partId;
VertexImpl vertex;
while ( Integer.numberOfTrailingZeros( vertexId ) > 0 )
{
vertex = vertexFactory.make( vertexId, 2 );
worker.addVertexToPart(partId, vertex);
numVerticesMade++;
vertexId /= 2;
}
vertex = vertexFactory.make( vertexId, 2 );
worker.addVertexToPart(partId, vertex);
numVerticesMade++;
// make partTree vertices
LinkedList<Integer> q = new LinkedList<Integer>();
int partTreeRootVertexId = numParts + partId; // vertex has been made
q.add( 2 * partTreeRootVertexId );
q.add( 2 * partTreeRootVertexId + 1 );
for ( ; numVerticesMade < partSize; numVerticesMade++ )
{
vertexId = q.removeLast();
int numChildren = 0;
if ( 2 * vertexId <= numVertices )
{
numChildren++;
q.add( 2 * vertexId );
if ( 2 * vertexId + 1 <= numVertices )
{
numChildren++;
q.add( 2 * vertexId + 1 );
}
}
vertex = vertexFactory.make( vertexId, numChildren );
worker.addVertexToPart(partId, vertex);
}
// BEGIN DEBUG
while ( q.size() > 0 )
{
System.out.println(" IN FLUSH LOOP: vertexId: " + q.removeLast() + " partSize: " + partSize);
}
// END DEBUG
assert q.isEmpty();
return null;
}
}
}