/**
* Copyright 2008 - CommonCrawl Foundation
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
**/
package org.commoncrawl.util;
/**
* Bandwidth utilization calculation helper
*
* @author rana
*
*/
public class BandwidthUtils {
public static class BandwidthStats {
public double bitsPerSecond = 0;
public double bytesPerSecond = 0;
public double scaledBytesPerSecond = 0;
public String scaledBytesUnits;
public double scaledBitsPerSecond = 0;
public String scaledBitsUnits;
}
public static class BandwidthHistory {
private static final int SPEED_HISTORY_SIZE = 20;
private static final int SPEED_SAMPLE_MIN = 150;
private static final int STALL_START_TIME = 5000;
private static String byte_units[] = { "B/s", "KB/s", "MB/s",
"GB/s" };
private static String bit_units[] = { "b/s", "Kb/s", "Mb/s",
"Gb/s" };
int pos;
int times[] = new int[SPEED_HISTORY_SIZE];
int bytes[] = new int[SPEED_HISTORY_SIZE];
int total_time;
int total_bytes;
int recent_bytes;
long recent_start;
boolean stalled = false;
private void reset() {
pos = 0;
recent_bytes = 0;
times[0] = 0;
bytes[0] = 0;
total_time = 0;
total_bytes = 0;
}
public void update(int bytesUploaded) {
if (recent_start == 0)
recent_start = System.currentTimeMillis();
long currTime = System.currentTimeMillis();
int recentAge = (int) (currTime - recent_start);
recent_bytes += bytesUploaded;
if (recentAge < SPEED_SAMPLE_MIN) {
return;
}
if (bytesUploaded == 0) {
if (recentAge >= STALL_START_TIME) {
stalled = true;
reset();
}
return;
} else {
if (stalled) {
stalled = false;
recentAge = 1;
}
total_time -= times[pos];
total_bytes -= bytes[pos];
times[pos] = recentAge;
bytes[pos] = recent_bytes;
total_time += recentAge;
total_bytes += recent_bytes;
recent_start = currTime;
recent_bytes = 0;
if (++pos == SPEED_HISTORY_SIZE)
pos = 0;
}
}
public void calcSpeed(BandwidthStats statsOut) {
int downloadAmount = total_bytes + recent_bytes;
int downloadTime = total_time;
if (recent_start != 0 && !stalled) {
downloadTime += (int) (System.currentTimeMillis() - recent_start);
}
if (downloadAmount != 0 && downloadTime != 0) {
statsOut.bytesPerSecond = (double) downloadAmount
/ ((double) downloadTime / 1000.0);
statsOut.bitsPerSecond = statsOut.bytesPerSecond * 8.0;
if (statsOut.bytesPerSecond < 1024.0) {
statsOut.scaledBytesPerSecond = statsOut.bytesPerSecond;
statsOut.scaledBytesUnits = byte_units[0];
} else if (statsOut.bytesPerSecond < 1024.0 * 1024.0) {
statsOut.scaledBytesPerSecond = statsOut.bytesPerSecond / 1024.0;
statsOut.scaledBytesUnits = byte_units[1];
} else if (statsOut.bytesPerSecond < 1024.0 * 1024.0 * 1024.0) {
statsOut.scaledBytesPerSecond = statsOut.bytesPerSecond
/ (1024.0 * 1024.0);
statsOut.scaledBytesUnits = byte_units[2];
} else {
statsOut.scaledBytesPerSecond = statsOut.bytesPerSecond
/ (1024.0 * 1024.0 * 1024.0);
statsOut.scaledBytesUnits = byte_units[3];
}
if (statsOut.bitsPerSecond < 1024.0) {
statsOut.scaledBitsPerSecond = statsOut.bitsPerSecond;
statsOut.scaledBitsUnits = bit_units[0];
} else if (statsOut.bitsPerSecond < 1024.0 * 1024.0) {
statsOut.scaledBitsPerSecond = statsOut.bitsPerSecond / 1024.0;
statsOut.scaledBitsUnits = bit_units[1];
} else if (statsOut.bitsPerSecond < 1024.0 * 1024.0 * 1024.0) {
statsOut.scaledBitsPerSecond = statsOut.bitsPerSecond
/ (1024.0 * 1024.0);
statsOut.scaledBitsUnits = bit_units[2];
} else {
statsOut.scaledBitsPerSecond = statsOut.bitsPerSecond
/ (1024.0 * 1024.0 * 1024.0);
statsOut.scaledBitsUnits = bit_units[3];
}
}
}
};
public static class RateLimiter {
// desired rate limit ...
private int _desiredBandwidthBytes;
// history object to collect stats ...
private BandwidthHistory _history = new BandwidthHistory();
// window start time ...
private long _windowStartTime;
private int _bytesAccumulated;
public RateLimiter(int maxBitsPerSecond) {
_desiredBandwidthBytes = maxBitsPerSecond / 8;
}
/**
* checkRateLimit
*
* take in bytes available and based on desired rate limit return adjusted
* bytes available
*
* @param bytesAvilable
* @return
*/
public int checkRateLimit(int byteAvailable) {
long currentTime = System.currentTimeMillis();
// if first time into call or if delta between last call and current
// window
if (_windowStartTime == 0 || (currentTime - _windowStartTime) >= 1000) {
_windowStartTime = currentTime;
_bytesAccumulated = 0;
}
// now calulate bandwidth available
return Math.min((_desiredBandwidthBytes - _bytesAccumulated),
byteAvailable);
}
public void updateStats(int bytesInOrOut) {
_bytesAccumulated += bytesInOrOut;
_history.update(bytesInOrOut);
}
public void getStats(BandwidthStats bandwidthStats) {
_history.calcSpeed(bandwidthStats);
}
}
}