package edu.ucsb.jpregel.system;
import api.WorkerGraphMaker;
import java.io.BufferedReader;
import java.io.IOException;
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 vertices.VertexShortestPathBinaryTree;
/**
*
* @author Pete Cappello
*/
public class WorkerGraphMakerBinaryTree extends WorkerGraphMaker
{
private static final VertexShortestPathBinaryTree vertexMaker = new VertexShortestPathBinaryTree();
private static final ThreadLocal< StringBuilder> uniqueNum = new ThreadLocal<StringBuilder>() {
@Override
protected StringBuilder initialValue() {
return new StringBuilder();
}
};
@Override
public int makeGraph(Worker worker)
{
int workerNum = worker.getWorkerNum();
Job job = worker.getJob();
FileSystem fileSystem = job.getFileSystem();
int numVertices = 0;
try
{
BufferedReader bufferedReader = fileSystem.getWorkerInputFileInputStream( workerNum );
// read file
String line = bufferedReader.readLine();
bufferedReader.close();
// extract startVertexId, stopVertexId
StringTokenizer stringTokenizer = new StringTokenizer( line );
int startVertexId = getToken( stringTokenizer );
int stopVertexId = getToken( stringTokenizer );
numVertices = getToken( stringTokenizer );
int lastTwoChildVertex = Math.min(stopVertexId,(numVertices -1)/2);
System.out.println("BinaryTreeWorkerGraphMaker.make: workerNum: " + workerNum
+ ", startVertexId: " + startVertexId
+ ", stopVertexId: " + stopVertexId + ", numVertices: " + numVertices
+ ", lastTwoChildVertex: "+ lastTwoChildVertex);
// construct vertices
/*
* Let the binary tree have nodes numbered 1 to N, where 1 is
* the root, and if the node numbered n has 2 children,
* the left child is numbered 2n;
* the right child is numbered 2n + 1.
* Then, node n has:
* 2 children, when 1 <= n <= (N - 1)/2;
* 1 child (a left child), when n is even and n = N/2;
* 0 children, otherwise (i.e., (N + 1)/2 <= n <= N).
*/
ExecutorService exec = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() );
makeNodes(exec,startVertexId, lastTwoChildVertex, 2, vertexMaker, job, worker, workerNum );
if ( numVertices % 2 == 0)
{
System.out.println("From " + Math.max(lastTwoChildVertex+1, startVertexId) + " to " + Math.min(lastTwoChildVertex+1, stopVertexId));
makeNodes(exec,Math.max(lastTwoChildVertex+1, startVertexId), Math.min(lastTwoChildVertex+1, stopVertexId), 1, vertexMaker, job, worker, workerNum );
}
makeNodes(exec,Math.max((numVertices+1)/2, startVertexId), stopVertexId, 0, vertexMaker, job, worker, workerNum );
exec.shutdown();
exec.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
}
catch ( Exception exception )
{
System.err.println( "GridWorkerGraphMaker.makeGraph: Error: " + exception.getMessage());
exception.printStackTrace();
System.exit( 1 );
}
return numVertices;
}
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 class NodeMaker implements Callable<Object>
{
private final int startVertexId;
private final int stopVertexId;
private final int numChildren;
private final VertexShortestPathBinaryTree vertexFactory;
private final Job job;
private final Worker worker;
private final int workerNum;
public NodeMaker(int startVertexId, int stopVertexId, int numChildren, VertexShortestPathBinaryTree vertexFactory, Job job, Worker worker, int workerNum)
{
this.startVertexId = startVertexId;
this.stopVertexId = stopVertexId;
this.numChildren = numChildren;
this.vertexFactory = vertexFactory;
this.job = job;
this.worker = worker;
this.workerNum = workerNum;
}
@Override
public Object call() throws Exception
{
makeNodes(startVertexId, stopVertexId, numChildren, vertexFactory, job, worker, workerNum);
return null;
}
}
private void makeNodes(int startVertexId, int stopVertexId, int numChildren, VertexShortestPathBinaryTree vertexFactory, Job job, Worker worker, int workerNum)
{
int numParts = job.getNumParts();
StringBuilder stringVertex = uniqueNum.get();
for ( int vertexId = startVertexId; vertexId <= stopVertexId; vertexId++ )
{
int partId = vertexFactory.getPartId( vertexId, numParts );
int destinationWorkerNum = worker.getWorkerNum( partId );
if ( destinationWorkerNum == workerNum )
{ // vertex belongs to this worker
VertexImpl vertex = vertexFactory.make( vertexId, numChildren );
worker.addVertexToPart(partId, vertex);
} else
{ // vertex belongs to another worker
stringVertex.append(vertexId).append(" ").append(numChildren);
worker.addRemoteVertex(destinationWorkerNum, partId, stringVertex.toString() );
stringVertex.setLength(0);
}
}
}
private void makeNodes( ExecutorService exec, int startVertexId, int stopVertexId, int numChildren,
VertexShortestPathBinaryTree vertexFactory, Job job, Worker worker,
int workerNum)
{
int naturalChunkSize = ( stopVertexId - startVertexId + 1 ) / ( Runtime.getRuntime().availableProcessors() * 2 );
final int chunkSize = Math.max( 1024 * 64, naturalChunkSize ); // 500000;
for (int i = startVertexId; i <= stopVertexId; i += chunkSize)
{
exec.submit( new NodeMaker( i, Math.min( i + chunkSize, stopVertexId ),
numChildren, vertexFactory, job, worker, workerNum ) ) ;
}
}
}