org.cloudbus.cloudsim.cloudlets.CloudletExecution Maven / Gradle / Ivy
Show all versions of cloudsim-plus Show documentation
/*
* Title: CloudSim Toolkit
* Description: CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation of Clouds
* Licence: GPL - http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2009-2012, The University of Melbourne, Australia
*/
package org.cloudbus.cloudsim.cloudlets;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.core.CloudSimTag;
import org.cloudbus.cloudsim.core.events.CloudSimEvent;
import org.cloudbus.cloudsim.datacenters.Datacenter;
import org.cloudbus.cloudsim.schedulers.cloudlet.CloudletScheduler;
import org.cloudbus.cloudsim.util.Conversion;
import java.util.Objects;
/**
* Stores execution information about a {@link Cloudlet} submitted to a
* specific {@link Datacenter} for processing.
* This class keeps track of the time for all activities in the
* Datacenter for a specific Cloudlet. Before a Cloudlet exits the Datacenter,
* it is RECOMMENDED to call this method {@link #finalizeCloudlet()}.
*
*
* It acts as a placeholder for maintaining the amount of resource share allocated
* at various times for simulating any scheduling using internal events.
*
*
* As the VM where the Cloudlet is running might migrate to another
* Datacenter, each CloudletExecutionInfo object represents the data about
* execution of the cloudlet when the Vm was in a given Datacenter.
*
* @author Manzur Murshed
* @author Rajkumar Buyya
* @since CloudSim Toolkit 1.0
*/
public class CloudletExecution {
/**
* A property that implements the Null Object Design Pattern for {@link CloudletExecution}
* objects.
*/
public static final CloudletExecution NULL = new CloudletExecution(Cloudlet.NULL);
/** @see #getCloudlet() */
private final Cloudlet cloudlet;
/** @see #getFileTransferTime() */
private double fileTransferTime;
/** @see #getCloudletArrivalTime() */
private final double arrivalTime;
/** @see #getFinishTime() */
private double finishedTime;
/** @see #getOverSubscriptionDelay() */
private double overSubscriptionDelay;
/**
* The time a request was sent to the broker to finish the Cloudlet
* when it has a negative length.
* @see Cloudlet#getLength()
*/
private double finishRequestTime;
/**
* The length of Cloudlet finished so far, in number of Instructions (I).
* The attribute stores the execution length of the cloudlet
* in previous datacenters. Thus, it represents the actual executed
* length of the cloudlet (not just the executed length in the current Datacenter).
*/
private long instructionsFinishedSoFar;
/**
* Latest cloudlet execution start time in the current Datacenter.
* This attribute will only hold the latest
* time since a Cloudlet can be canceled, paused or resumed.
*/
private double startExecTime;
/** @see #getLastProcessingTime() */
private double lastProcessingTime;
/**
* The total time the Cloudlet spent in the last state
* at the current Datacenter.
* For instance, if the last state was paused and now
* the cloudlet was resumed (it is running),
* this time represents the time the cloudlet
* stayed paused.
*/
private double totalCompletionTime;
/** @see #getVirtualRuntime() */
private double virtualRuntime;
/** @see #getTimeSlice() */
private double timeSlice;
/** @see #getLastAllocatedMips() */
private double lastAllocatedMips;
/**
* Instantiates a CloudletExecutionInfo object upon the arrival of a Cloudlet inside a Datacenter.
* The arriving time is determined by {@link CloudSim#clock()}.
*
* @param cloudlet the Cloudlet to store execution information from
*/
public CloudletExecution(final Cloudlet cloudlet) {
this.cloudlet = cloudlet;
this.arrivalTime = cloudlet.registerArrivalInDatacenter();
this.finishedTime = Cloudlet.NOT_ASSIGNED;
this.lastProcessingTime = Cloudlet.NOT_ASSIGNED;
this.totalCompletionTime = 0.0;
this.startExecTime = 0.0;
this.virtualRuntime = 0;
//In case a Cloudlet has been executed partially by some other Host
this.instructionsFinishedSoFar = cloudlet.getFinishedLengthSoFar() * Conversion.MILLION;
}
/**
* Gets the {@link Cloudlet#getLength() Cloudlet's length} (in MI).
* @return
*/
public long getCloudletLength() {
return cloudlet.getLength();
}
public long getNumberOfPes(){
return cloudlet.getNumberOfPes();
}
/**
* Sets the Cloudlet status.
*
* @param newStatus the Cloudlet status
* @return true if the new status has been set, false otherwise
*/
public boolean setStatus(final Cloudlet.Status newStatus) {
final var prevStatus = cloudlet.getStatus();
if (prevStatus.equals(newStatus)) {
return false;
}
final double clock = cloudlet.getSimulation().clock();
cloudlet.setStatus(newStatus);
if (prevStatus == Cloudlet.Status.INEXEC && isNotRunning(newStatus)) {
totalCompletionTime += clock - startExecTime;
return true;
}
if (prevStatus == Cloudlet.Status.RESUMED && newStatus == Cloudlet.Status.SUCCESS) {
totalCompletionTime += clock - startExecTime;
return true;
}
startOrResumeCloudlet(newStatus, prevStatus);
return true;
}
/**
* Starts or resumes the Cloudlet if the new status is requesting that.
*
* @param newStatus the new status that will be checked to start or resume the Cloudlet
* @param oldStatus the old Cloudlet status
*/
private void startOrResumeCloudlet(final Cloudlet.Status newStatus, final Cloudlet.Status oldStatus) {
final double clock = cloudlet.getSimulation().clock();
if (newStatus == Cloudlet.Status.INEXEC || isTryingToResumePausedCloudlet(newStatus, oldStatus)) {
startExecTime = clock;
if(cloudlet.getExecStartTime() == 0) {
cloudlet.setExecStartTime(startExecTime);
}
}
}
private boolean isTryingToResumePausedCloudlet(final Cloudlet.Status newStatus, final Cloudlet.Status oldStatus) {
return newStatus == Cloudlet.Status.RESUMED && oldStatus == Cloudlet.Status.PAUSED;
}
/**
* Checks if the cloudlet is NOT in a running state.
*
* @param status The current cloudlet status
* @return true if the cloudlet is NOT running, false if it is.
*/
private static boolean isNotRunning(final Cloudlet.Status status) {
return status == Cloudlet.Status.CANCELED ||
status == Cloudlet.Status.PAUSED ||
status == Cloudlet.Status.SUCCESS;
}
/**
* Gets the remaining cloudlet length (in MI) that has to be executed yet,
* considering the {@link Cloudlet#getLength()}.
*
* @return remaining cloudlet length in MI
*/
public long getRemainingCloudletLength() {
final long absLength = Math.abs(cloudlet.getLength());
final double miFinishedSoFar = instructionsFinishedSoFar / (double) Conversion.MILLION;
if(cloudlet.getLength() > 0){
return (long)Math.max(absLength - miFinishedSoFar, 0);
}
/**
* If length is negative, that means it is undefined.
* This way, here it's ensured the remaining length keeps
* increasing until a {@link CloudSimTag#CLOUDLET_FINISH} message
* is received by the broker to finish the cloudlet
*
* Getting here, it's ensured the length is negative. This way,
* if the different between the length and the number of executed MI is
* zero, in a scenario of a regular Cloudlet with a positive length,
* that means the Cloudlet has finished.
* If the length is negative, that doesn't mean it is finished.
* In this case, we just return the absolute length to make the
* Cloudlet keep running. */
if(absLength-miFinishedSoFar == 0) {
return absLength;
}
/*
* In case the difference above is not zero, the remaining
* length of the indefinite-length Cloudlet will be computed
* to ensure it will return the lower value as possible, so that
* it execute as little instructions as possible before checking
* if a message to finish the cloudlet was sent.
*/
return (long)Math.min(Math.abs(absLength-miFinishedSoFar), absLength);
}
/**
* Finalizes all relevant information before exiting the Datacenter
* entity. This method sets the final data of:
*
* - wall clock time, i.e. the time of this Cloudlet resides in a
* Datacenter (from arrival time until departure time);
* - actual CPU time, i.e. the total execution time of this Cloudlet in a Datacenter;
* - Cloudlet's finished time so far.
*
*/
public void finalizeCloudlet() {
// Sets the wall clock time and actual CPU time
final double wallClockTime = cloudlet.getSimulation().clock() - arrivalTime;
cloudlet.setWallClockTime(wallClockTime, totalCompletionTime);
final long finishedLengthMI =
cloudlet.getStatus() == Cloudlet.Status.SUCCESS ?
cloudlet.getLength() :
instructionsFinishedSoFar / Conversion.MILLION;
cloudlet.addFinishedLengthSoFar(finishedLengthMI);
}
/**
* Updates the length of cloudlet that has executed so far.
*
* @param partialFinishedInstructions the partial amount of instructions just executed, to be
* added to the {@link #instructionsFinishedSoFar},
* in Number of Instructions (instead of Million Instructions)
*/
public void updateProcessing(final double partialFinishedInstructions) {
final var simulation = cloudlet.getSimulation();
setLastProcessingTime(simulation.clock());
final boolean terminate = simulation.isTimeToTerminateSimulationUnderRequest();
if(partialFinishedInstructions == 0 && !terminate){
return;
}
this.instructionsFinishedSoFar += partialFinishedInstructions;
final double partialFinishedMI = partialFinishedInstructions / Conversion.MILLION;
cloudlet.addFinishedLengthSoFar((long)partialFinishedMI);
/* If a simulation termination time was defined and the length of the Cloudlet is negative
* (to indicate that they must not finish before the termination time),
* then sends a request to finish the Cloudlet. */
if(finishRequestTime <= 0 && terminate && cloudlet.getLength() < 0){
finishRequestTime = simulation.clock();
simulation.sendFirst(new CloudSimEvent(cloudlet.getBroker(), CloudSimTag.CLOUDLET_FINISH, cloudlet));
}
}
/**
* Gets the time the cloudlet arrived for execution inside the Datacenter.
* @return
*/
public double getCloudletArrivalTime() {
return arrivalTime;
}
/**
* Gets the time when the Cloudlet has finished completely
* (not just in a given Datacenter, but finished at all).
* If the cloudlet wasn't finished completely yet,
* the value is equals to {@link Cloudlet#NOT_ASSIGNED}.
*
* @return finish time of a cloudlet;
* or -1 if it cannot finish in this hourly slot
*/
public double getFinishTime() {
return finishedTime;
}
/**
* Sets the finish time for this Cloudlet.
* If time is negative, then it will be ignored.
* @param time finish time
*/
public void setFinishTime(final double time) {
if (time < 0) {
return;
}
finishedTime = time;
}
/**
* Gets the Cloudlet for which the execution information is related to.
*
* @return cloudlet for this execution information object
*/
public Cloudlet getCloudlet() {
return cloudlet;
}
/**
* Gets the ID of the Cloudlet this execution info is related to.
* @return
*/
public long getCloudletId(){
return cloudlet.getId();
}
/**
* Gets the time to transfer the list of files required by the Cloudlet
* from the Datacenter storage (such as a Storage Area Network)
* to the Vm of the Cloudlet.
* @return
*/
public double getFileTransferTime() {
return fileTransferTime;
}
/**
* Sets the time to transfer the list of files required by the Cloudlet
* from the Datacenter storage (such as a Storage Area Network)
* to the Vm of the Cloudlet.
*
* @param fileTransferTime the file transfer time to set
*/
public void setFileTransferTime(final double fileTransferTime) {
this.fileTransferTime = fileTransferTime;
}
/**
* Gets the last time the Cloudlet was processed at the Datacenter
* where this execution information is related to.
*
* @return the last time the Cloudlet was processed or zero when it has never been processed yet
*/
public double getLastProcessingTime() {
return lastProcessingTime;
}
/**
* Sets the last time this Cloudlet was processed at a Datacenter.
* @param lastProcessingTime the last processing time to set
*/
public void setLastProcessingTime(final double lastProcessingTime) {
this.lastProcessingTime = lastProcessingTime;
cloudlet.notifyOnUpdateProcessingListeners(lastProcessingTime);
}
/**
* Gets the virtual runtime (vruntime) that indicates how long the Cloudlet
* has been executing by a {@link CloudletScheduler} (in seconds).
*
* The default value of this attribute is zero. Each scheduler
* implementation might set a value to such attribute
* to use it for context switch,
* preempting running Cloudlets to enable other ones to start executing.
* This way, the attribute is just used internally by specific CloudletSchedulers.
*
*
* @return
*/
public double getVirtualRuntime(){
return virtualRuntime;
}
/**
* Adds a given time to the {@link #getVirtualRuntime() virtual runtime}.
*
* @param timeToAdd time to add to the virtual runtime (in seconds)
* @return the new virtual runtime (in seconds)
*/
public double addVirtualRuntime(final double timeToAdd) {
if(timeToAdd >= 0) {
setVirtualRuntime(virtualRuntime + timeToAdd);
}
return virtualRuntime;
}
/**
* Sets the virtual runtime (vruntime) that indicates how long the Cloudlet
* has been executing by a {@link CloudletScheduler} (in seconds). This attribute is used
* just internally by specific CloudletSchedulers.
*
* @param virtualRuntime the value to set (in seconds)
* @see #getVirtualRuntime()
*/
public void setVirtualRuntime(final double virtualRuntime){
this.virtualRuntime = virtualRuntime;
}
/**
* Gets the time-slice assigned by a {@link CloudletScheduler} for a Cloudlet, which is
* the amount of time (in seconds) that such a Cloudlet will have to use the PEs of a Vm.
*
* Each CloudletScheduler implementation can make use of this attribute or not.
* CloudletSchedulers that use it, are in charge to compute the time-slice to
* assign to each Cloudlet.
*
*
* @return Cloudlet time-slice (in seconds)
*
*/
public double getTimeSlice() {
return timeSlice;
}
/**
* Sets the time-slice assigned by a {@link CloudletScheduler} for a Cloudlet, which is
* the amount of time (in seconds) that such a Cloudlet will have to use the PEs of a Vm.
*
* Each CloudletScheduler implementation can make use of this attribute or not.
* CloudletSchedulers that use it, are in charge to compute the time-slice to
* assign to each Cloudlet.
*
*
* @param timeSlice the Cloudlet time-slice to set (in seconds)
*/
public void setTimeSlice(final double timeSlice) {
this.timeSlice = timeSlice;
}
@Override
public String toString() {
return String.format("Cloudlet %d", cloudlet.getId());
}
@Override
public boolean equals(final Object obj) {
return obj instanceof CloudletExecution that &&
that.cloudlet.getId() == this.cloudlet.getId();
}
@Override
public int hashCode() {
return Objects.hash(cloudlet.getId());
}
/**
* Gets the last actually allocated MIPS for the Cloudlet.
* That means if no MIPS was allocated for a given time,
* it is not stored.
*
* This value is used to compute the expected finish time
* of a Cloudlet. If the allocated MIPS is zero,
* we don't have how to compute that.
*
*
* @return
*/
public double getLastAllocatedMips() {
return lastAllocatedMips;
}
/**
* Sets the last actually allocated MIPS for the Cloudlet.
* That means if no MIPS was allocated for a given time,
* it is not stored.
*
* This value is used to compute the expected finish time
* of a Cloudlet. If the allocated MIPS is zero,
* we don't have how to compute that.
*
*
* @param lastAllocatedMips the value to set (if zero or negative, the attribute is not changed)
*/
public void setLastAllocatedMips(final double lastAllocatedMips) {
if(lastAllocatedMips > 0) {
this.lastAllocatedMips = lastAllocatedMips;
}
}
/** Gets the total processing delay imposed to the cloudlet processing
* due to over-subscription of RAM and/or BW.
* If there is resource over-subscription,
* the {@see #getFinishTime finish time} already includes the imposed delay. */
public double getOverSubscriptionDelay() {
return overSubscriptionDelay;
}
/**
* Gets the expected cloudlet finish time (in seconds) if no RAM or BW over-subscription occurs.
* @return
* @see #getOverSubscriptionDelay()
*/
public double getExpectedFinishTime() {
return getCloudlet().getActualCpuTime() - overSubscriptionDelay;
}
/**
* Checks if Cloudlet's RAM or BW has been over-subscribed, causing
* processing delay.
* @return returns the over-subscription delay or 0 if there was no over-subscription up to now.
*/
public boolean hasOverSubscription(){
return overSubscriptionDelay > 0;
}
/**
* Increments the total delay caused by RAM/BW over-subscription
* @param newDelay the new delay to add (in seconds)
*/
public void incOverSubscriptionDelay(final double newDelay) {
if(newDelay < 0)
throw new IllegalArgumentException("Over-subscription delay cannot be negative");
this.overSubscriptionDelay += newDelay;
}
}