Background
Earlier this semester, we discussed FIFO queues, simple queues that maintained a FIFO ordering. Another type of queue is called a priority queue. In the case of a priority queue, items can be enqueued in any order, but upon dequeue(), the item from the queue with the highest priority, not necessarily the first one to entry, is removed and returned. Since Heaps keep the minimum or maximum valued item very accessible at the top, they are an excellent choice for the implementation of priority queues -- keeping the extreme value at the top and ready-to-go is what they do best.A FIFO queue is actually a special case of a priority queue, where the priority is the time the item entered the queue and lower priorities are considered most importantant, so the item which was earliest enqueued is dequeued first.
Queues are often used as data tructures to hold waiters those persons, processes, or other objects waiting for an event or resource. One common way of organizing waiters is in a simple FIFO queue -- it is simple and "fair" to all involved. But, other priority-based schemes might actually serve the population as a whole better (even if intentionally unfair to some). This assignment will explore one such priority scheme in a simulated real-world application and allow you to compare the results to a "baseline" FIFO queue based approach.
Overview
In this assignment, you will implement both a FIFO queue (like the one you did for the traffic flow assignment) and a priority queue (using a Heap). and you will compare how efficient they are when implementing a web server.A web server will often receive several requests at once, and we need to determine in what order we should handle them. One approach would be to simply process the jobs in the order that they arrive. Another approach would be to always process the smallest job, so that a small HTML file does not get stuck behind a large MPEG file.
This assignment asks you to generate a workload, simulate both approaches, and report the simulated average turn-around time for each approach.
For this assignment, we are not providing you with skeletons for any of the classes. You will need to create the *.java files and build the files from scratch. Please keep the following in mind:
- There should be exactly one class definition per file, and the name of the file should exactly match the name of the class defined inside.
- The methods that are part of the interface for each class that we have specified should be named exactly as we have described. Our test program will assume the methods are named and implemented as we have described here.
- Only those methods specified here should be part of the interface. Anything else that you choose to implement should be a private helper method.
- Your code should be thoroughly commented, including descriptions of the classes and methods as well as explanations of any non-obvious code or important algorithmic steps.
Part I: The Heap Class
This class will be used to model a priority queue. For this assignment, we will assume that smaller items have a higher priority, so you will be implementing a min-heap.You must use a Vector to implement your Heap. To be able to use a Vector in the Heap class, the Heap.java file must begin with:
import java.util.Vector;
The methods that you should define for the Heap class are:
public Heap()
This is the constructor for the Heap class. It should initialize an empty Heap.
public boolean isEmpty()
This method should return true if there are no objects stored within the Heap, and false otherwise.
public void insert(Comparable data)
Inserts the specified object into the Heap. It should run in O(log n) time. It is suggested that you implement this method using one or more helper methods.
public Comparable removeMin()
This method should remove the lowest valued element in the Heap. It should run in O(log n) time. It is suggested that you implement this method with one or more helper methods.
Part II: The FifoQueue Class
This class will be used to model a standard First-In-First-Out queue.You must use a LinkedList to implement the FifoQueue. To make life easier for you, you can use Java's LinkedList class if you prefer. To be able to use Java's LinkedList in the FifoQueue class, the FifoQueue.java file must begin with:
import java.util.LinkedList;
If you want to use your own implementation of a LinkedList, you should not include this line. The methods that you should define for the FifoQueue class are:
public FifoQueue()
This is the constructor for the FifoQueue class. It should initialize an empty FifoQueue.
public boolean isEmpty()
This method should return true if there are no objects stored within the FifoQueue, and false otherwise.
public void insert(Comparable data)
Inserts the specified object at the end/tail of the FifoQueue.
public Comparable removeMin()
This method should remove the oldest element from the head of the FifoQueue.
Part III: The WebObject Class
This class should create simulated Web objects, like HTML pages, MPEGs, GIFs, &c. For the purpose of our simulation, the only property that a WebObject has is a size in the range of 1024Bytes (1KB) to 1,048,576B (1024KB), measured in whole kilobytes.The WebObject class must implement the Comparable interface.
The methods you should define for the WebObject class are:
public WebObject()
This is the constructor. It should create a new web object with a random size in the range of 1KB to 1MB (1024 KB).
public int getSize()
This method returns the size of the WebObject
public WebObject copy()
This method should create an exact copy of the WebObject.
public int compareTo(Object compareObj)
This method defines how to compare two objects of the WebObject class. It returns 0 if the sizes of two WebObjects are equal, a negative number if the size of this WebObject is smaller than the size of the object which was passed in, or a positive number if the size of this object is larger than the size of the object which was passed in. This method is required because we are implementing the Comparable interface.The constructor for the WebObject requires you to generate a random size. To get a random value, you first need to create an object of Java's Random class. Once you have an object, you can call the
nextInt()
method, which generates a random int value.This value will be in the approximate range of -2,000,000,000 to 2,000,000,000. To get this in the range of 1 to 1024, you could pass an argument to nextInt, this will change the range to something usable. Refer to the API documention for more information.Random r = new Random(); int num = r.nextInt();
In order to use the Random class in the WebObject class, the WebObject.java file must begin with the line:
import java.util.Random;
Part IV: The ServerSimulation Class
This class should simulate the activity of two Web servers, one using FIFO scheduling and the other using Shortest Job First (SJF) scheduling (using the Heap). It should simulate the processing of a collection of requests and report the average service time for each of the two simulated servers.The methods you should write for the ServerSimulation class are:
public ServerSimulation(int numObjects, int kiloByteRate)
The constructor should create numObjects of WebObjects. Each WebObject represents a Web page or other object request by a user. Each of these should be copied. One of them should be enqueued in the Heap, a SJF queue, and the other in the FifoQueue.The kiloByteRate defines the speed of the Web server’s connection in terms of kilobytes of data per second. It should be stored within a private instance variable.
public int calculateFIFOAverage()
This method should calculate the average service time for a job on the FIFO-based Web server. It should do this by dequeuing each job one at a time. It should then calculate the service time for this job by dividing its size in kilobytes by the kilobyte rate of the connection. A running total should be kept of the service times. Another running total should be kept that is the sum of the service times encountered by each job (the service times of everything before plus the service time for the object itself). At the end, dividing this total service time by the number of jobs will yield the average service time.The following pseudocode may be useful:
* Initalize delaySoFar to 0
* Initalize sumOfDelays to 0
* Initialize countOfJobs to 0
* While the queue is not empty
- Increment the count of jobs* Return (sumOfDelays / countOfJobs)
- Add the current delaySoFar to the sumOfDelays
- Dequeue the next WebObject
- Divide the size of the object by the byteRate and add this amount of time to the delaySoFar
public int calculateSJFAverage()
This method should do exactly the same thing as calculateFIFOAverage() above, except that it operates on the Heap, the priority queue based on job size.
Part V: The ServerEvaluation Class
You should implement a class with only one method -main()
. This method should be a reasonably good test of the calculateSJFAverage() and calculateFIFOAverage() of the ServerSimulation class. This should show that SJF scheduling by Web servers using priority queues (Heaps) produces a better average service time than traditional queue-based FIFO scheduling.Generating Random Numbers
If you create a new Random object to generate the size of each WebObject, you will likely get several values in a row that are the same. The reason for this is that when you create a Random object, it uses the current time in milliseconds as the seed (the value from which it generates the "random" sequence of numbers). The computer is fast enough that it is possible for many WebObjects to be created within the same millisecond, in which case all of them will have the same seed and therefore generate the same value.
To get around this, you should create one Random object that all of the WebObjects will use when generating their sizes. In order to do this, you need to declare the Random variable to be part of the class like you would for an instance variable, but you will declare it using the keyword "static".
The "static" keyword indicates that instead of being an instance variable where every object has its own copy, it is a variable that is shared by all objects of the class. So, if you create a static Random object as part of the WebObject class, every WebObject will use the same random number generator, and so you wont have the problem of bunches of WebObjects having the same size (although it is still possible that there will be some duplicates because of the random behavior).
So, in your WebObject class, you should include among the declarations of any instance variables:
where "rand" is whatever name you want to give the Random object.private static Random rand = new Random();