Author: toad
Date: 2006-06-28 23:40:09 +0000 (Wed, 28 Jun 2006)
New Revision: 9398
Modified:
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/node/Version.java
trunk/freenet/src/freenet/support/math/TimeDecayingRunningAverage.java
Log:
843: Fix very long standing bug in TimeDecayingRunningAverage. Affected many
parts of the node.
SELF MANDATORY AT 0:00 GMT 30/09/06.
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2006-06-28 19:10:58 UTC (rev
9397)
+++ trunk/freenet/src/freenet/node/Node.java 2006-06-28 23:40:09 UTC (rev
9398)
@@ -2080,6 +2080,7 @@
Logger.minor(this, "Reporting 0 because
throttle uncontended: now "+throttledPacketSendAverage.currentValue());
throttledPacketSendAverage.report(0);
Logger.minor(this, "New average:
"+throttledPacketSendAverage.currentValue());
+ Logger.minor(this, "Average:
"+throttledPacketSendAverage.toString());
} else
Logger.minor(this, "Not uncontended");
}
@@ -2982,7 +2983,7 @@
}
public double getBwlimitDelayTime() {
- return this.throttledPacketSendAverage.currentValue();
+ return throttledPacketSendAverage.currentValue();
}
public double getNodeAveragePingTime() {
Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-06-28 19:10:58 UTC (rev
9397)
+++ trunk/freenet/src/freenet/node/Version.java 2006-06-28 23:40:09 UTC (rev
9398)
@@ -18,12 +18,12 @@
public static final String protocolVersion = "1.0";
/** The build number of the current revision */
- private static final int buildNumber = 842;
+ private static final int buildNumber = 843;
/** Oldest build of Fred we will talk to */
private static final int oldLastGoodBuild = 839;
- private static final int newLastGoodBuild = 839;
- private static final long transitionTime = 1151103600000L; // 0:00 GMT
24/06/06
+ private static final int newLastGoodBuild = 843;
+ private static final long transitionTime = 1151625600000L; // 0:00 GMT
30/06/06
public static final int buildNumber() {
return buildNumber;
Modified: trunk/freenet/src/freenet/support/math/TimeDecayingRunningAverage.java
===================================================================
--- trunk/freenet/src/freenet/support/math/TimeDecayingRunningAverage.java
2006-06-28 19:10:58 UTC (rev 9397)
+++ trunk/freenet/src/freenet/support/math/TimeDecayingRunningAverage.java
2006-06-28 23:40:09 UTC (rev 9398)
@@ -7,72 +7,27 @@
import freenet.support.Logger;
/**
- * Time based running average.
- * Smoother and less memory usage than an intervalled running average.
- * Does not implement serialization.
- * Uses Math.exp() and a divide on every report so slow if FP is slow.
- * @author amphibian
- * Mathematical basis:
- * We want a weighted average of all reports ever:
- * We have reports r1...rN, which were t1...tN millis ago.
- * Weight of r1 is 0.5^(t1/tHalf)
- * So total is:
- * T =
- * r1*0.5^(t1/tHalf) +
- * r2*0.5^(t2/tHalf) +
- * r3*0.5^(t3/tHalf) +
- * ...
- * Now, if we move forward tDelta, we get:
- * r1*0.5^((t1+tDelta)/tHalf) +
- * r2*0.5^((t2+tDelta)/tHalf) +
- * r3*0.5^((t3+tDelta)/tHalf) +
- * ...
- * =
- * r1*0.5^((t1/tHalf)+(tDelta/tHalf)) +
- * r2*0.5^((t2/tHalf)+(tDelta/tHalf)) +
- * r3*0.5^((t3/tHalf)+(tDelta/tHalf)) +
- * ...
- * =
- * (r1*0.5^(t1/tHalf)) * 0.5 ^ (tDelta/tHalf) + ...
- * = T * 0.5 ^ (tDelta/tHalf)
- *
- * The weighted mean will then be:
- * Tnew = (Tprev * 0.5 ^ (tDelta/tHalf) + r0 * 0.5^0) /
- * (0.5^(t1/tHalf) + 0.5^(t2/tHalf) + 0.5^(t3/tHalf) + ...)
- *
- * So we need to track:
- * T: T -> T * 0.5 ^ (tDelta/tHalf) + R
- * W (total weight) =
- * 0.5^(t1/tHalf) + 0.5^(t2/tHalf) + ...
- * Now, go forward by tDelta millis, we get:
- * W = 0.5^((t1+tDelta)/tHalf) + 0.5^((t2+tDelta)/tHalf) + ...
- * = W * 0.5^tDelta/tHalf
- * Adding a new report, we get: W -> W * 0.5^(tDelta/tHalf) + 1
+ * Time decaying running average.
*
- * So our equasions are:
- * T -> T * 0.5 ^ (tDelta/tHalf) + R
- * W -> W * 0.5 ^ (tDelta/tHalf) + 1
+ * Decay factor = 0.5 ^ (interval / halflife).
*
- * And the overall weighted average is T / W
+ * So if the interval is exactly the half-life then reporting 0 will halve the
value.
*
- * Stability?
- * Lets say tDelta = 0 every time, for maximum increase.
- * Then we get: T -> T + R
- * W -> W + 1
- * Thus both numbers will work fine.
+ * Note that the older version has a half life on the influence of any given
report without taking
+ * into account the fact that reports persist and accumulate. :)
+ *
*/
public class TimeDecayingRunningAverage implements RunningAverage {
private static final long serialVersionUID = -1;
- static final int MAGIC = 0x5ff4ac92;
+ static final int MAGIC = 0x5ff4ac94;
public final Object clone() {
return new TimeDecayingRunningAverage(this);
}
- double weightedTotal;
- double totalWeights;
- double halfLife;
+ double curValue;
+ final double halfLife;
long lastReportTime;
long createdTime;
long totalReports;
@@ -83,8 +38,7 @@
boolean logDEBUG;
public String toString() {
- return super.toString() + ": weightedTotal="+weightedTotal+
- ", totalWeights="+totalWeights+", halfLife="+halfLife+
+ return super.toString() + ": currentValue="+curValue+",
halfLife="+halfLife+
", lastReportTime="+(System.currentTimeMillis()-lastReportTime)+
"ms ago, createdTime="+(System.currentTimeMillis()-createdTime)+
"ms ago, totalReports="+totalReports+", started="+started+
@@ -93,8 +47,7 @@
public TimeDecayingRunningAverage(double defaultValue, long halfLife,
double min, double max) {
- weightedTotal = 0.0;
- totalWeights = 0.0;
+ curValue = defaultValue;
this.defaultValue = defaultValue;
started = false;
this.halfLife = halfLife;
@@ -113,20 +66,12 @@
if(m != MAGIC) throw new IOException("Invalid magic "+m);
int v = dis.readInt();
if(v != 1) throw new IOException("Invalid version "+v);
- weightedTotal = dis.readDouble();
- if(Double.isInfinite(weightedTotal) || Double.isNaN(weightedTotal))
- throw new IOException("Invalid weightedTotal: "+weightedTotal);
- totalWeights = dis.readDouble();
- if(totalWeights < 0.0 || Double.isInfinite(totalWeights) ||
Double.isNaN(totalWeights))
- throw new IOException("Invalid totalWeights: "+totalWeights);
- double avg = weightedTotal / totalWeights;
- if(avg < min || avg > max)
- throw new IOException("Out of range: weightedTotal =
"+weightedTotal+", totalWeights = "+totalWeights);
+ curValue = dis.readDouble();
+ if(Double.isInfinite(curValue) || Double.isNaN(curValue))
+ throw new IOException("Invalid weightedTotal: "+curValue);
+ if(curValue < min || curValue > max)
+ throw new IOException("Out of range: curValue = "+curValue);
started = dis.readBoolean();
- if(!started) {
- totalWeights = 0.0;
- weightedTotal = 0.0;
- }
long priorExperienceTime = dis.readLong();
this.halfLife = halfLife;
this.minReport = min;
@@ -147,14 +92,11 @@
this.minReport = a.minReport;
this.started = a.started;
this.totalReports = a.totalReports;
- this.totalWeights = a.totalWeights;
- this.weightedTotal = a.weightedTotal;
+ this.curValue = a.curValue;
}
public synchronized double currentValue() {
- if(!started)
- return defaultValue;
- else return weightedTotal / totalWeights;
+ return curValue;
}
public synchronized void report(double d) {
@@ -163,8 +105,7 @@
totalReports++;
long now = System.currentTimeMillis();
if(!started) {
- weightedTotal = d;
- totalWeights = 1.0;
+ curValue = d;
started = true;
if(logDEBUG)
Logger.debug(this, "Reported "+d+" on "+this+" when just
started");
@@ -173,17 +114,18 @@
now - lastReportTime;
double thisHalfLife = halfLife;
long uptime = now - createdTime;
- if((uptime / 4) < thisHalfLife) thisHalfLife = (uptime / 4);
+ //if((uptime / 4) < thisHalfLife) thisHalfLife = (uptime / 4);
if(thisHalfLife == 0) thisHalfLife = 1;
double changeFactor =
Math.pow(0.5, ((double)thisInterval) / thisHalfLife);
- weightedTotal = weightedTotal * changeFactor + d;
- totalWeights = totalWeights * changeFactor + 1.0;
+ double oldCurValue = curValue;
+ curValue = curValue * changeFactor /* close to 1.0 if short
interval, close to 0.0 if long interval */
+ + (1.0 - changeFactor) * d;
if(logDEBUG)
Logger.debug(this, "Reported "+d+" on "+this+":
thisInterval="+thisInterval+
", halfLife="+halfLife+", uptime="+uptime+",
thisHalfLife="+thisHalfLife+
- ", changeFactor="+changeFactor+",
weightedTotal="+weightedTotal+
- ",
totalWeights="+totalWeights+", currentValue="+currentValue()+
+ ", changeFactor="+changeFactor+",
oldCurValue="+oldCurValue+
+ ",
currentValue="+currentValue()+
",
thisInterval="+thisInterval+", thisHalfLife="+thisHalfLife+
", uptime="+uptime+",
changeFactor="+changeFactor);
}
@@ -201,8 +143,7 @@
public synchronized void writeDataTo(DataOutputStream out) throws
IOException {
out.writeInt(MAGIC);
out.writeInt(1);
- out.writeDouble(weightedTotal);
- out.writeDouble(totalWeights);
+ out.writeDouble(curValue);
out.writeBoolean(started);
out.writeLong(totalReports);
out.writeLong(System.currentTimeMillis() - createdTime);