/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: WorkPool.java * Written by: Andreas Uebelhoer, Alexander Bieles, Emre Selegin (Team 6) * * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.routing.experimentalLeeMoore1; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.electric.tool.routing.RoutingFrame.RoutingSegment; /** * A workpool contains all the work partitions and manages them. */ public class WorkPool { private static Logger logger = LoggerFactory.getLogger(WorkPool.class); private static ConcurrentLinkedQueue<RoutingSegment> inputList = new ConcurrentLinkedQueue<RoutingSegment>(); private static BlockingQueue<RoutingPart> globalRoutingQueue; private static ArrayList<WorkPartition> workPartitions; private static ConcurrentLinkedQueue<WorkPartition> workPartitionsList; private static int segmentCounter; // CountDownLatch only decrease the segmentCounter with the help of the decrease function! private static int additionalSegments; private static int WorkDivideIn_X_Dir; // represents the number of regions // in x direction private static int WorkDivideIn_Y_Dir; // analogeous to above in y direction private static int minimumRegionBorderLength; private static int size_x; private static int size_y; private static int numPartitions; private static int[][] regionPartition; public static boolean output = false; /** * This method has to be called before any other! * @param segmentsToRoute the segments from Electric, which have to be routed * @param numPartitions in how many partitions should the grid be devided * @param size_x grid size in x direction * @param size_y grid size in y direction */ public static void init(List<RoutingSegment> segmentsToRoute, int numPartitions, int size_x, int size_y, boolean output) { WorkPool.output = output; //set variables WorkPool.size_x = size_x; WorkPool.size_y = size_y; WorkPool.numPartitions = numPartitions; minimumRegionBorderLength = yana.minimumRegionBorderLength; segmentCounter = segmentsToRoute.size(); additionalSegments = segmentCounter; //initialize objects globalRoutingQueue = new ArrayBlockingQueue<RoutingPart>(segmentCounter); for (RoutingSegment rs : segmentsToRoute) { inputList.add(rs); } workPartitions = new ArrayList<WorkPartition>(numPartitions); //initialize working partitions for (int i = 0; i < numPartitions; i++) { workPartitions.add(new WorkPartition(i)); } //calculate and create ThreadBorders for the partitions createThreadBorders(yana.regionDivideMethod); createRegionArray(); } /** * This method creates the borders of regions. Therefore the "region_"-methods are called * @param method there are currently three methods how the grid can be devided into numPartition regions. */ private static void createThreadBorders(int method) { //be sure, that the regions are not toooooo small do { switch (method) { case 1: regions_OptimalDivisionFactor(); break; case 2: regions_simpleStripes(); break; case 3: regions_AdaptedRegions(); break; default: regions_OneRegion(); } numPartitions--; logger.debug("partitions: " + numPartitions); } while ((numPartitions > 0) && (size_x / WorkDivideIn_X_Dir < minimumRegionBorderLength || size_y / WorkDivideIn_Y_Dir < minimumRegionBorderLength)); //restore last usable numpartitions numPartitions++; if(output) System.out.println("size: " + size_x + "x" + size_y); if(output) System.out.println("region devision: " + WorkDivideIn_X_Dir + "x" + WorkDivideIn_Y_Dir + " for " + numPartitions +" regions"); for (int i = 0; i < numPartitions; i++) { workPartitions.get(i).setThreadBorders(new ThreadBorders(WorkPool.getLowIndexIn_X(i), WorkPool.getHighIndexIn_X(i), WorkPool.getLowIndexIn_Y(i), WorkPool.getHighIndexIn_Y(i))); } } /** * We divide the whole grid into regions (as great and squared as possible) */ private static void regions_OptimalDivisionFactor() { int smallerFactor = (int) Math.sqrt(numPartitions); aproximateXYdirection(smallerFactor); } /** * Divide the grid into stripes so that the longer direction is divided */ private static void regions_simpleStripes() { aproximateXYdirection(1); } /** * Allocate the regions to the two dimensions, so that the ratio of the grid dimension sizes equals the number of the regions they get * so if the grid ratio is 2/3 its possible to allocate 4regions to the x-direction and 6 to the y-direction, because its the same ratio */ private static void regions_AdaptedRegions() { /* * Following equations should be aproximated, to get an adapted region allocation: * * x=size_x, y=size_y, r=ratio of the grid, a=work in x direction, b = work in y direction, n=numwork * * x/y = r = a/b AND a*b=n * * therefore we get the following: * r = n/(b^2), because a=n/b * => b^2 = n/r * * so we can calculate a region division wich is nearly the same than the grid ratio */ double r = (1.0 * size_x) / size_y; if (Math.abs(1.0 - r) < 0.05) { //squared grid regions_OptimalDivisionFactor(); return; } else if (r < 1) { //let the ratio be always r = 1 / r; } int smallerSide = (int) Math.round(Math.sqrt(numPartitions / r)); if (smallerSide == 0) { // grid to widely streched regions_simpleStripes(); return; } aproximateXYdirection(smallerSide); } /** * just one region */ private static void regions_OneRegion() { WorkDivideIn_X_Dir = 1; WorkDivideIn_Y_Dir = 1; numPartitions = 1; } /** * Try to find factor so that the regions allocated to the dimensions equal numPartitions if we multiply them. * @param smallerFactor */ private static void aproximateXYdirection(int smallerFactor) { if (smallerFactor <= 0) { regions_OneRegion(); return; } while (numPartitions % smallerFactor != 0) { smallerFactor--; } if (size_x <= size_y) { WorkDivideIn_X_Dir = smallerFactor; WorkDivideIn_Y_Dir = numPartitions / smallerFactor; } else { WorkDivideIn_X_Dir = numPartitions / smallerFactor; WorkDivideIn_Y_Dir = smallerFactor; } } /** * Change data structure for real work phase. This method has to be called before threads are starting to do detailed routing. */ public static void prepare() { verify(); if(output) System.out.println("Preparing work partitions for real work..."); workPartitionsList = new ConcurrentLinkedQueue<WorkPartition>(workPartitions); workPartitions = null; yana.setProgressMax(WorkPool.getAdditionalSegments()); } /** * debug */ private static void verify() { int numParts = 0; for (WorkPartition wp : workPartitions) { numParts += wp.routingParts.size(); } if (numParts != additionalSegments && output) { System.out.println("ERROR: RoutingParts are missing!"); } } /** * Gets a new routing segment for global routing. * @return RoutingSegment */ public static RoutingSegment getRoutingSegment() { return inputList.poll(); } /** * Get a routing part from the global routingPart-queue * @return RoutingPart */ public static RoutingPart getPartFromGlobalRoutingQueue() { return globalRoutingQueue.poll(); } /** * Puts a routingPart the global routingPart-queue * @param rp routingPart */ public static void addPartToGlobalRoutingQueue(RoutingPart rp) { globalRoutingQueue.offer(rp); } /** * Return the current value of the segment counter * @return segment counter */ synchronized public static int getSegmentCounter() { return segmentCounter; } /** * decreases the current value of the segment counter */ synchronized static public void decreaseSegmentCounter() { segmentCounter--; } /** * This methods puts the routingPart in the queue of the given workPartition * @param rp routingPart * @param partition workPartition */ public static void addWorkToPartition(RoutingPart rp, int partition) { workPartitions.get(partition).addWork(rp); } /** * Get a new work partition for detailed routing. * @return WorkPartition */ public static WorkPartition getWorkFromPartition() { return workPartitionsList.poll(); } /** * Returns the size of the global routingPart queue * @return size of the global routingPart queue */ public static int getGlobalRoutingQueueSize() { return globalRoutingQueue.size(); } /** * Increase the value for additional segments */ synchronized public static void increaseAdditionalSegments() { additionalSegments++; } /** * Returns the current value of how many additional segments were created * @return additional segments */ synchronized public static int getAdditionalSegments() { return additionalSegments; } /* * this functions will return the "begin" and the "end" indexes of given * array. example: 15 long array (0...14), 4 threads (thread 0..3): each * thread id will get: 0-2, 3-6, 7-10, 11-14 depending on their ids. note: * the calculation is done via id*(ges/anz). this is because without * brackets an overflow at id*ges can occur!!! */ private static int oneDimensionalGetHighIndex(int numberOfWork, int numWorkers, int rank) { rank++; if (numWorkers > numberOfWork) { if (rank > numberOfWork) { return -1; } else { return rank - 1; } } return ((int) (rank * (((double) numberOfWork) / ((double) numWorkers)))) - 1; } private static int oneDimensionalGetLowIndex(int numberOfWork, int numWorkers, int rank) { rank++; if (numWorkers > numberOfWork) { if (rank > numberOfWork) { return 0; } else { return rank - 1; } } return (((int) ((rank - 1) * (((double) numberOfWork) / ((double) numWorkers)))) + 1) - 1; } /* * these functions will return the boundary indexes of their region inside * the grid given their id */ protected static int getHighIndexIn_X(int rank) { return oneDimensionalGetHighIndex(size_x, WorkDivideIn_X_Dir, rank % WorkDivideIn_X_Dir); } protected static int getHighIndexIn_Y(int rank) { return oneDimensionalGetHighIndex(size_y, WorkDivideIn_Y_Dir, rank / WorkDivideIn_X_Dir); } protected static int getLowIndexIn_X(int rank) { return oneDimensionalGetLowIndex(size_x, WorkDivideIn_X_Dir, rank % WorkDivideIn_X_Dir); } protected static int getLowIndexIn_Y(int rank) { return oneDimensionalGetLowIndex(size_y, WorkDivideIn_Y_Dir, rank / WorkDivideIn_X_Dir); } /** * just print some statics about which region has which borders */ static void printPartitionBorders() { regionPartition = new int[size_x][size_y]; for (int t = 0; t < numPartitions; t++) { if(output) System.out.println("Partition-" + t + ": " + WorkPool.getLowIndexIn_X(t) + "<x<" + WorkPool.getHighIndexIn_X(t) + ", " + WorkPool.getLowIndexIn_Y(t) + "<y<" + WorkPool.getHighIndexIn_Y(t)); for (int x = WorkPool.getLowIndexIn_X(t); x <= WorkPool.getHighIndexIn_X(t); x++) { for (int y = WorkPool.getLowIndexIn_Y(t); y <= WorkPool.getHighIndexIn_Y(t); y++) { regionPartition[x][y] = t; } } } } /** * this data structure will store the responsible thread index for each grid-element */ static void createRegionArray() { regionPartition = new int[size_x][size_y]; for (int t = 0; t < numPartitions; t++) { for (int x = WorkPool.getLowIndexIn_X(t); x <= WorkPool.getHighIndexIn_X(t); x++) { for (int y = WorkPool.getLowIndexIn_Y(t); y <= WorkPool.getHighIndexIn_Y(t); y++) { regionPartition[x][y] = t; } } } } /** * Get the index of the WorkPartition, which is responsible for this point * @param posX X-coordinate * @param posY Y-coordinate * @return index of the responsible WorkPartition */ public static int getResponsiblePartitionID_ForPoint(int posX, int posY) { return regionPartition[posX][posY]; } }