/** * This method will modify the existing transfers. It will only modify those who are taking more * than their fairshare. It will modify them proportional to their bandwidth, i.e. the more share * they have, the more they will be cut. * * @invariant availableBandwidthInBPS >= bps * @param bps the target bandwidth * @throws Exception */ private void makeRoomFor(final double bps) throws Exception { double fairShare = totalBandwidthInBPS / (double) (transfers.size() + 1); // assert less than fair share // A.ssert(bps <= getNextTransferBandwidth()); ArrayList<Transfer> trespassingTransfers = new ArrayList<Transfer>(); sanityCheck(); // this will only be true if the fair share is larger than the available bandwidth if (bps > availableInBPS) // if this is true, then there is trespassing transfers { final double amountToReclaim = bps - availableInBPS; double actualReclaimed = 0; double total = 0; for (Transfer t : transfers) { if (t.bandwidthInBPS > fairShare) { trespassingTransfers.add(t); total += t.bandwidthInBPS; } } // sort them ascending by bandwidth Collections.sort( trespassingTransfers, new Comparator<Transfer>() { public int compare(Transfer o1, Transfer o2) { return (int) (o1.bandwidthInBPS - o2.bandwidthInBPS); } }); double extra = 0; for (Transfer t : trespassingTransfers) { double deduction = amountToReclaim * t.bandwidthInBPS / total; deduction = Math.ceil(deduction); // to prevent future rounding errors, avoid fractions if (t.bandwidthInBPS - deduction < fairShare) { double oldDeduction = deduction; deduction = t.bandwidthInBPS - fairShare; extra += oldDeduction - deduction; } else if (t.bandwidthInBPS - deduction > fairShare && extra > 0) { // take from extra double oldDeduction = deduction; double newBW = t.bandwidthInBPS - deduction; double diffFromFairShare = newBW - fairShare; if (diffFromFairShare > extra) { double takeFromExtra = extra; deduction += takeFromExtra; } else // diff <= extra { double takeFromExtra = diffFromFairShare; deduction += takeFromExtra; } extra -= oldDeduction - deduction; } deduction = Math.ceil(deduction); actualReclaimed += deduction; BandwidthManager remoteBwm = t.getOtherBandwidthManagerThan(this); A.ssert(remoteBwm != this); // remoteBwm.sanityCheck(); if (deduction < BANDWIDTH_TOL) continue; double newBw = t.bandwidthInBPS - deduction; t.setNextEpochBW(newBw); } HashSet<BandwidthManager> candidates = new HashSet<BandwidthManager>(); for (Transfer t : trespassingTransfers) { if (t.getNextEpochBW() < 0) continue; double deducted = t.bandwidthInBPS - t.getNextEpochBW(); t.applyNextEpochBW(); BandwidthManager remoteBWM = t.getOtherBandwidthManagerThan(this); remoteBWM.availableInBPS += deducted; candidates.add(remoteBWM); } availableInBPS += actualReclaimed; A.ssert(availableInBPS > bps || Math.abs(availableInBPS - bps) < BANDWIDTH_TOL); // we are going to lie about our available bandwidth cuz we are reserving bps of them. // availableInBPS -= bps; double oldVal = availableInBPS; availableInBPS = 0; for (BandwidthManager bwm : candidates) bwm.boostLocalTransfers(); // availableInBPS += bps; availableInBPS = oldVal; } A.ssert(availableInBPS > bps || Math.abs(availableInBPS - bps) < BANDWIDTH_TOL); }
/** * This method does bandwidth allocation. This method should be used only for upload bandwidth * managers. * * @param remoteNodeId * @param message * @param bytes * @throws Exception */ public Transfer sendBytes(int remoteNodeId, Message message, long bytes) throws Exception { // System.out.print("Send Bytes: "); // the delay is only used as a minimum possible delay in case the bandwidth delay is less that // it // in other words, the final delay will not be less that 'delay'. long delay = 0; // delayManager.getDelay(this.nodeId, remoteNodeId); BandwidthManager remoteBandwidthManager = network.get(remoteNodeId).getDownBandwidthManager(); sanityCheck(); remoteBandwidthManager.sanityCheck(); double bandwidthInBPS = Math.min(getNextTransferBandwidth(), remoteBandwidthManager.getNextTransferBandwidth()); bandwidthInBPS = Math.floor(bandwidthInBPS); // to prevent future rounding errors, prevent fractions // ms = b/(b/ms) long timeInMilliSec = Math.max((long) UnitConversion.secToMilliSec(bytes / (bandwidthInBPS)), /*delay*/ 0); long now = scheduler.now; message.setDest(remoteNodeId); message.setSource(this.nodeId); message.setBytes(bytes); MessageDeliveryEvent evt = new MessageDeliveryEvent(now + timeInMilliSec, message); Transfer trns = new Transfer(bandwidthInBPS, this, remoteBandwidthManager, bytes, evt); message.setTransfer(trns); trns.setMessage(message); // for debug purposes only sanityCheck(); remoteBandwidthManager.sanityCheck(); this.makeRoomFor(bandwidthInBPS); this.availableInBPS -= bandwidthInBPS; remoteBandwidthManager.makeRoomFor(bandwidthInBPS); remoteBandwidthManager.availableInBPS -= bandwidthInBPS; // this must not be done before we call makeRoomFor, // cuz that function depend on the old size of the transfers array List // and it depends on the old availableInBPS transfers.add(trns); remoteBandwidthManager.transfers.add(trns); sanityCheck(); remoteBandwidthManager.sanityCheck(); scheduler.enqueue(evt); P.rint( "t( " + scheduler.now + " ) SEND BYTES (" + message.getSource() + "->" + message.getDest() + ") " + "[" + bytes + " b] , alloc_bw = " + bandwidthInBPS + " BPS of " + totalBandwidthInBPS + ", curr_trans = " + transfers.size() + "/ " + remoteBandwidthManager.transfers.size() + ", time_est. = " + timeInMilliSec + " ms, delvr = " + evt.getTime() + ", " + message.toString()); return trns; }