www.pudn.com > choke-ns.rar > choke.cc, change:2005-03-27,size:23669b
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990-1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* by HNN:
* This is the implementation of the CHOKe algorithm. This algorithm
* is got through some minor modification of red.cc.
* end HNN
*
* Here is one set of parameters from one of Sally's simulations
* (this is from tcpsim, the older simulator):
*
* ed [ q_weight=0.002 thresh=5 linterm=30 maxthresh=15
* mean_pktsize=500 dropmech=random-drop queue-size=60
* plot-file=none bytes=false doubleq=false dqthresh=50
* wait=true ]
*
* 1/"linterm" is the max probability of dropping a packet.
* There are different options that make the code
* more messy that it would otherwise be. For example,
* "doubleq" and "dqthresh" are for a queue that gives priority to
* small (control) packets,
* "bytes" indicates whether the queue should be measured in bytes
* or in packets,
* "dropmech" indicates whether the drop function should be random-drop
* or drop-tail when/if the queue overflows, and
* the commented-out Holt-Winters method for computing the average queue
* size can be ignored.
* "wait" indicates whether the gateway should wait between dropping
* packets.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/red.cc,v 1.54 2000/10/05 21:25:34 sfloyd Exp $ (LBL)";
#endif
#include <math.h>
#include <sys/types.h>
#include "config.h"
#include "template.h"
#include "random.h"
#include "flags.h"
#include "delay.h"
#include "choke.h"
static class CHOKEClass : public TclClass {
public:
CHOKEClass() : TclClass("Queue/CHOKE") {}
TclObject* create(int argc, const char*const* argv) {
//printf("creating CHOKE Queue. argc = %d\n", argc);
//mod to enable CHOKE to take arguments
if (argc==5)
return (new CHOKEQueue(argv[4]));
else
return (new CHOKEQueue("Drop"));
}
} class_choke;
/* Strangely this didn't work.
* Seg faulted for child classes.
CHOKEQueue::CHOKEQueue() {
CHOKEQueue("Drop");
}
*/
/*
* modified to enable instantiation with special Trace objects - ratul
*/
CHOKEQueue::CHOKEQueue(const char * trace) : link_(NULL), bcount_(0), de_drop_(NULL), EDTrace(NULL),
tchan_(0), idle_(1), first_reset_(1)
{
// printf("Making trace type %s\n", trace);
if (strlen(trace) >=20) {
printf("trace type too long - allocate more space to traceType in red.h and recompile\n");
exit(0);
}
strcpy(traceType, trace);
bind_bool("bytes_", &choke_edp_.bytes); // boolean: use bytes?
bind_bool("queue_in_bytes_", &qib_); // boolean: q in bytes?
// _RENAMED("queue-in-bytes_", "queue_in_bytes_");
// by HNN
bind("max_cand_num_", &max_cand_num_);
bind("cand_num_", &cand_num_);
bind("choke_adaptive_", &choke_adaptive_);
bind("interval_num_", &interval_num_);
// end HNN
bind("thresh_", &choke_edp_.th_min); // minthresh
bind("maxthresh_", &choke_edp_.th_max); // maxthresh
bind("mean_pktsize_", &choke_edp_.mean_pktsize); // avg pkt size
bind("q_weight_", &choke_edp_.q_w); // for EWMA
bind_bool("wait_", &choke_edp_.wait);
bind("linterm_", &choke_edp_.max_p_inv);
bind_bool("setbit_", &choke_edp_.setbit); // mark instead of drop
bind_bool("gentle_", &choke_edp_.gentle); // increase the packet
// drop prob. slowly
// when ave queue
// exceeds maxthresh
bind_bool("drop_tail_", &drop_tail_); // drop last pkt
// _RENAMED("drop-tail_", "drop_tail_");
bind_bool("drop_front_", &drop_front_); // drop first pkt
// _RENAMED("drop-front_", "drop_front_");
bind_bool("drop_rand_", &drop_rand_); // drop pkt at random
// _RENAMED("drop-rand_", "drop_rand_");
bind_bool("ns1_compat_", &ns1_compat_); // ns-1 compatibility
// _RENAMED("ns1-compat_", "ns1_compat_");
bind("ave_", &choke_edv_.v_ave); // average queue sie
bind("prob1_", &choke_edv_.v_prob1); // dropping probability
bind("curq_", &curq_); // current queue size
q_ = new PacketQueue(); // underlying queue
pq_ = q_;
reset();
#ifdef notdef
print_edp();
print_edv();
#endif
}
void CHOKEQueue::reset()
{
/*
* If queue is measured in bytes, scale min/max thresh
* by the size of an average packet (which is specified by user).
*/
if (qib_ && first_reset_ == 1) {
//printf ("choke_edp_.th_min: %5.3f \n", choke_edp_.th_min);
choke_edp_.th_min *= choke_edp_.mean_pktsize;
choke_edp_.th_max *= choke_edp_.mean_pktsize;
//printf ("choke_edp_.th_min: %5.3f \n", choke_edp_.th_min);
first_reset_ = 0;
}
/*
* Compute the "packet time constant" if we know the
* link bandwidth. The ptc is the max number of (avg sized)
* pkts per second which can be placed on the link.
* The link bw is given in bits/sec, so scale mean psize
* accordingly.
*/
if (link_)
choke_edp_.ptc = link_->bandwidth() /
(8. * choke_edp_.mean_pktsize);
choke_edv_.v_ave = 0.0;
choke_edv_.v_slope = 0.0;
choke_edv_.count = 0;
choke_edv_.count_bytes = 0;
choke_edv_.old = 0;
choke_edv_.v_a = 1 / (choke_edp_.th_max - choke_edp_.th_min);
choke_edv_.v_b = - choke_edp_.th_min / (choke_edp_.th_max - choke_edp_.th_min);
if (choke_edp_.gentle) {
choke_edv_.v_c = ( 1.0 - 1 / choke_edp_.max_p_inv ) / choke_edp_.th_max;
choke_edv_.v_d = 2 / choke_edp_.max_p_inv - 1.0;
}
idle_ = 1;
if (&Scheduler::instance() != NULL)
idletime_ = Scheduler::instance().clock();
else
idletime_ = 0.0; /* sched not instantiated yet */
if (debug_)
printf("Doing a queue reset\n");
Queue::reset();
if (debug_)
printf("Done queue reset\n");
bcount_ = 0;
}
/*
* Compute the average queue size.
* Nqueued can be bytes or packets.
*/
double CHOKEQueue::estimator(int nqueued, int m, double ave, double q_w)
{
double new_ave, old_ave;
new_ave = ave;
while (--m >= 1) {
old_ave = new_ave;
new_ave *= 1.0 - q_w;
}
old_ave = new_ave;
new_ave *= 1.0 - q_w;
new_ave += q_w * nqueued;
return new_ave;
}
/*
* Return the next packet in the queue for transmission.
*/
Packet* CHOKEQueue::deque()
{
Packet *p;
p = q_->deque();
if (p != 0) {
idle_ = 0;
bcount_ -= hdr_cmn::access(p)->size();
} else {
idle_ = 1;
// deque() may invoked by Queue::reset at init
// time (before the scheduler is instantiated).
// deal with this case
if (&Scheduler::instance() != NULL)
idletime_ = Scheduler::instance().clock();
else
idletime_ = 0.0;
}
return (p);
}
/*
* Calculate the drop probability.
*/
double
CHOKEQueue::calculate_p(double v_ave, double th_max, int gentle, double v_a,
double v_b, double v_c, double v_d, double max_p_inv)
{
double p;
if (gentle && v_ave >= th_max) {
// p ranges from max_p to 1 as the average queue
// size ranges from th_max to twice th_max
p = v_c * v_ave + v_d;
} else {
// p ranges from 0 to max_p as the average queue
// size ranges from th_min to th_max
p = v_a * v_ave + v_b;
p /= max_p_inv;
}
if (p > 1.0)
p = 1.0;
return p;
}
/*
* Make uniform instead of geometric interdrop periods.
*/
double
CHOKEQueue::modify_p(double p, int count, int count_bytes, int bytes,
int mean_pktsize, int wait, int size)
{
double count1 = (double) count;
if (bytes)
count1 = (double) (count_bytes/mean_pktsize);
if (wait) {
if (count1 * p < 1.0)
p = 0.0;
else if (count1 * p < 2.0)
p /= (2 - count1 * p);
else
p = 1.0;
} else {
if (count1 * p < 1.0)
p /= (1.0 - count1 * p);
else
p = 1.0;
}
if (bytes && p < 1.0) {
p = p * size / mean_pktsize;
}
if (p > 1.0)
p = 1.0;
return p;
}
/*
*
*/
/*
* should the packet be dropped/marked due to a probabilistic drop?
*/
int
CHOKEQueue::drop_early(Packet* pkt)
{
hdr_cmn* ch = hdr_cmn::access(pkt);
choke_edv_.v_prob1 = calculate_p(choke_edv_.v_ave, choke_edp_.th_max, choke_edp_.gentle,
choke_edv_.v_a, choke_edv_.v_b, choke_edv_.v_c, choke_edv_.v_d, choke_edp_.max_p_inv);
choke_edv_.v_prob = modify_p(choke_edv_.v_prob1, choke_edv_.count, choke_edv_.count_bytes,
choke_edp_.bytes, choke_edp_.mean_pktsize, choke_edp_.wait, ch->size());
// drop probability is computed, pick random number and act
double u = Random::uniform();
if (u <= choke_edv_.v_prob) {
// DROP or MARK
choke_edv_.count = 0;
choke_edv_.count_bytes = 0;
hdr_flags* hf = hdr_flags::access(pickPacketForECN(pkt));
if (choke_edp_.setbit && hf->ect() && choke_edv_.v_ave < choke_edp_.th_max) {
hf->ce() = 1; // mark Congestion Experienced bit
return (0); // no drop
} else {
return (1); // drop
}
}
return (0); // no DROP/mark
}
/*
* Pick packet for early congestion notification (ECN). This packet is then
* marked or dropped. Having a separate function do this is convenient for
* supporting derived classes that use the standard CHOKE algorithm to compute
* average queue size but use a different algorithm for choosing the packet for
* ECN notification.
*/
Packet*
CHOKEQueue::pickPacketForECN(Packet* pkt)
{
return pkt; /* pick the packet that just arrived */
}
/*
* Pick packet to drop. Having a separate function do this is convenient for
* supporting derived classes that use the standard RED algorithm to compute
* average queue size but use a different algorithm for choosing the victim.
*/
Packet*
CHOKEQueue::pickPacketToDrop()
{
int victim;
if (drop_front_)
victim = min(1, q_->length()-1);
else if (drop_rand_)
victim = Random::integer(q_->length());
else /* default is drop_tail_ */
victim = q_->length() - 1;
return(q_->lookup(victim));
}
/*
* Receive a new packet arriving at the queue.
* The average queue size is computed. If the average size
* exceeds the threshold, then the dropping probability is computed,
* and the newly-arriving packet is dropped with that probability.
* The packet is also dropped if the maximum queue size is exceeded.
*
* "Forced" drops mean a packet arrived when the underlying queue was
* full or when the average q size exceeded maxthresh.
* "Unforced" means a RED random drop.
*
* For forced drops, either the arriving packet is dropped or one in the
* queue is dropped, depending on the setting of drop_tail_.
* For unforced drops, the arriving packet is always the victim.
*/
#define DTYPE_NONE 0 /* ok, no drop */
#define DTYPE_FORCED 1 /* a "forced" drop */
#define DTYPE_UNFORCED 2 /* an "unforced" (random) drop */
#define DTYPE_BOTH_FORCED 3 /* an "unforced" (random) drop */
void CHOKEQueue::enque(Packet* pkt)
{
/*
* if we were idle, we pretend that m packets arrived during
* the idle period. m is set to be the ptc times the amount
* of time we've been idle for
*/
int m = 0;
if (idle_) {
// A packet that arrives to an idle queue will never
// be dropped.
double now = Scheduler::instance().clock();
/* To account for the period when the queue was empty. */
idle_ = 0;
m = int(choke_edp_.ptc * (now - idletime_));
}
/*
* Run the estimator with either 1 new packet arrival, or with
* the scaled version above [scaled by m due to idle time]
* (bcount_ maintains the byte count in the underlying queue).
* If the underlying queue is able to delete packets without
* us knowing, then bcount_ will not be maintained properly!
*/
choke_edv_.v_ave = estimator(qib_ ? bcount_ : q_->length(), m + 1, choke_edv_.v_ave, choke_edp_.q_w);
//printf("v_ave: %6.4f (%13.12f) q: %d)\n",
// double(choke_edv_.v_ave), double(choke_edv_.v_ave), q_->length());
//run_estimator(qib_ ? bcount_ : q_->length(), m + 1);
/*
* count and count_bytes keeps a tally of arriving traffic
* that has not been dropped (i.e. how long, in terms of traffic,
* it has been since the last early drop)
*/
hdr_cmn* ch = hdr_cmn::access(pkt);
++choke_edv_.count;
choke_edv_.count_bytes += ch->size();
/*
* DROP LOGIC:
* q = current q size, ~q = averaged q size
* 1> if ~q > maxthresh, this is a FORCED drop
* 2> if minthresh < ~q < maxthresh, this may be an UNFORCED drop
* 3> if (q+1) > hard q limit, this is a FORCED drop
*/
register double qavg = choke_edv_.v_ave;
int droptype = DTYPE_NONE;
int qlen = qib_ ? bcount_ : q_->length();
int qlim = qib_ ? (qlim_ * choke_edp_.mean_pktsize) : qlim_;
curq_ = qlen; // helps to trace queue during arrival, if enabled
// by HNN
int cand_id[max_cand_num_];
int cand_fid[max_cand_num_];
int cur_fid;
Packet* cand_pkt[max_cand_num_];
int up_i=0;
// end HNN
if (qavg >= choke_edp_.th_min && qlen > 1) {
// add by HNN
// choose a candidate packet for comparison
hdr_ip * iph = hdr_ip::access(pkt);
cur_fid = iph->flowid();
if (choke_adaptive_) {
up_i = (int) (2 + 2 * (qavg - choke_edp_.th_min) * interval_num_ / (choke_edp_.th_max - choke_edp_.th_min));
up_i = (up_i <= q_->length()) ? up_i : q_->length() ;
// printf("qlen = %d \t", q_->length());
}
else {
up_i = (cand_num_<=q_->length())?cand_num_:q_->length() ;
}
for (int i=0; i<up_i; i++) {
cand_id[i] = Random::integer(q_->length());
int j = 0;
while (j<i) {
if (cand_id[i] == cand_id[j]) {
cand_id[i] = Random::integer(q_->length());
j = 0;
}
else j++;
}
cand_pkt[i] = q_->lookup(cand_id[i]);
iph = hdr_ip::access(cand_pkt[i]);
cand_fid[i] = iph->flowid();
if (cur_fid == cand_fid[i]) {
droptype = DTYPE_BOTH_FORCED;
}
}
if (droptype == DTYPE_BOTH_FORCED) {
}
// end HNN
else if ((!choke_edp_.gentle && qavg >= choke_edp_.th_max) ||
(choke_edp_.gentle && qavg >= 2 * choke_edp_.th_max)) {
droptype = DTYPE_FORCED;
} else if (choke_edv_.old == 0) {
/*
* The average queue size has just crossed the
* threshold from below to above "minthresh", or
* from above "minthresh" with an empty queue to
* above "minthresh" with a nonempty queue.
*/
choke_edv_.count = 1;
choke_edv_.count_bytes = ch->size();
choke_edv_.old = 1;
} else if (drop_early(pkt)) {
droptype = DTYPE_UNFORCED;
}
} else {
/* No packets are being dropped. */
choke_edv_.v_prob = 0.0;
choke_edv_.old = 0;
}
// if (qlen >= qlim) {
if ((droptype != DTYPE_BOTH_FORCED) && (qlen >= qlim)) {
// see if we've exceeded the queue size
droptype = DTYPE_FORCED;
}
if (droptype == DTYPE_UNFORCED) {
/* pick packet for ECN, which is dropping in this case */
Packet *pkt_to_drop = pickPacketForECN(pkt);
/*
* If the packet picked is different that the one that just arrived,
* add it to the queue and remove the chosen packet.
*/
if (pkt_to_drop != pkt) {
q_->enque(pkt);
bcount_ += ch->size();
q_->remove(pkt_to_drop);
bcount_ -= hdr_cmn::access(pkt_to_drop)->size();
pkt = pkt_to_drop; /* XXX okay because pkt is not needed anymore */
}
// deliver to special "edrop" target, if defined
if (de_drop_ != NULL) {
//trace first if asked
// if no snoop object (de_drop_) is defined,
// this packet will not be traced as a special case.
if (EDTrace != NULL)
((Trace *)EDTrace)->recvOnly(pkt);
de_drop_->recv(pkt);
}
else
drop(pkt);
// add by HNN
} else if (droptype == DTYPE_BOTH_FORCED) {
/* Here is the tricky thing, we need it just for
* the maintainance of the parameters of other
* structures */
q_->enque(pkt);
q_->remove(pkt);
drop(pkt);
for (int i=0; i<up_i; i++) {
if (cur_fid == cand_fid[i]) {
q_->remove(cand_pkt[i]);
bcount_ -= hdr_cmn::access(cand_pkt[i])->size();
drop(cand_pkt[i]);
}
}
// end HNN
} else {
/* forced drop, or not a drop: first enqueue pkt */
q_->enque(pkt);
bcount_ += ch->size();
/* drop a packet if we were told to */
if (droptype == DTYPE_FORCED) {
/* drop random victim or last one */
pkt = pickPacketToDrop();
q_->remove(pkt);
bcount_ -= hdr_cmn::access(pkt)->size();
drop(pkt);
if (!ns1_compat_) {
// bug-fix from Philip Liu, <phill@ece.ubc.ca>
choke_edv_.count = 0;
choke_edv_.count_bytes = 0;
}
}
}
return;
}
int CHOKEQueue::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "reset") == 0) {
reset();
return (TCL_OK);
}
if (strcmp(argv[1], "early-drop-target") == 0) {
if (de_drop_ != NULL)
tcl.resultf("%s", de_drop_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "edrop-trace") == 0) {
if (EDTrace != NULL) {
tcl.resultf("%s", EDTrace->name());
if (debug_)
printf("edrop trace exists according to RED\n");
}
else {
if (debug_)
printf("edrop trace doesn't exist according to RED\n");
tcl.resultf("0");
}
return (TCL_OK);
}
if (strcmp(argv[1], "trace-type") == 0) {
tcl.resultf("%s", traceType);
return (TCL_OK);
}
}
else if (argc == 3) {
// attach a file for variable tracing
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (tchan_ == 0) {
tcl.resultf("CHOKE: trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
// tell CHOKE about link stats
if (strcmp(argv[1], "link") == 0) {
LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
if (del == 0) {
tcl.resultf("CHOKE: no LinkDelay object %s",
argv[2]);
return(TCL_ERROR);
}
// set ptc now
link_ = del;
choke_edp_.ptc = link_->bandwidth() /
(8. * choke_edp_.mean_pktsize);
return (TCL_OK);
}
if (strcmp(argv[1], "early-drop-target") == 0) {
NsObject* p = (NsObject*)TclObject::lookup(argv[2]);
if (p == 0) {
tcl.resultf("no object %s", argv[2]);
return (TCL_ERROR);
}
de_drop_ = p;
return (TCL_OK);
}
if (strcmp(argv[1], "edrop-trace") == 0) {
if (debug_)
printf("Ok, Here\n");
NsObject * t = (NsObject *)TclObject::lookup(argv[2]);
if (debug_)
printf("Ok, Here too\n");
if (t == 0) {
tcl.resultf("no object %s", argv[2]);
return (TCL_ERROR);
}
EDTrace = t;
if (debug_)
printf("Ok, Here too too too %d\n", ((Trace *)EDTrace)->type_);
return (TCL_OK);
}
if (!strcmp(argv[1], "packetqueue-attach")) {
delete q_;
if (!(q_ = (PacketQueue*) TclObject::lookup(argv[2])))
return (TCL_ERROR);
else {
pq_ = q_;
return (TCL_OK);
}
}
}
return (Queue::command(argc, argv));
}
/*
* Routine called by TracedVar facility when variables change values.
* Currently used to trace values of avg queue size, drop probability,
* and the instantaneous queue size seen by arriving packets.
* Note that the tracing of each var must be enabled in tcl to work.
*/
void
CHOKEQueue::trace(TracedVar* v)
{
char wrk[500], *p;
if (((p = strstr(v->name(), "ave")) == NULL) &&
((p = strstr(v->name(), "prob")) == NULL) &&
((p = strstr(v->name(), "curq")) == NULL)) {
fprintf(stderr, "CHOKE:unknown trace var %s\n",
v->name());
return;
}
if (tchan_) {
int n;
double t = Scheduler::instance().clock();
// XXX: be compatible with nsv1 RED trace entries
if (*p == 'c') {
sprintf(wrk, "Q %g %d", t, int(*((TracedInt*) v)));
} else {
sprintf(wrk, "%c %g %g", *p, t,
double(*((TracedDouble*) v)));
}
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
}
return;
}
/* for debugging help */
void CHOKEQueue::print_edp()
{
printf("mean_pktsz: %d\n", choke_edp_.mean_pktsize);
printf("bytes: %d, wait: %d, setbit: %d\n",
choke_edp_.bytes, choke_edp_.wait, choke_edp_.setbit);
printf("minth: %f, maxth: %f\n", choke_edp_.th_min, choke_edp_.th_max);
printf("max_p_inv: %f, qw: %f, ptc: %f\n",
choke_edp_.max_p_inv, choke_edp_.q_w, choke_edp_.ptc);
printf("qlim: %d, idletime: %f\n", qlim_, idletime_);
printf("=========\n");
}
void CHOKEQueue::print_edv()
{
printf("v_a: %f, v_b: %f\n", choke_edv_.v_a, choke_edv_.v_b);
}
/************************************************************/
/*
* This procedure is obsolete, and only included for backward compatibility.
* The new procedure is CHOKEQueue::estimator
*/
/*
* Compute the average queue size.
* The code contains two alternate methods for this, the plain EWMA
* and the Holt-Winters method.
* nqueued can be bytes or packets
*/
void CHOKEQueue::run_estimator(int nqueued, int m)
{
double f, f_sl, f_old;
f = choke_edv_.v_ave;
f_sl = choke_edv_.v_slope;
#define CHOKE_EWMA
#ifdef CHOKE_EWMA
while (--m >= 1) {
f_old = f;
f *= 1.0 - choke_edp_.q_w;
}
f_old = f;
f *= 1.0 - choke_edp_.q_w;
f += choke_edp_.q_w * nqueued;
#endif
#ifdef CHOKE_HOLT_WINTERS
while (--m >= 1) {
f_old = f;
f += f_sl;
f *= 1.0 - choke_edp_.q_w;
f_sl *= 1.0 - 0.5 * choke_edp_.q_w;
f_sl += 0.5 * choke_edp_.q_w * (f - f_old);
}
f_old = f;
f += f_sl;
f *= 1.0 - choke_edp_.q_w;
f += choke_edp_.q_w * nqueued;
f_sl *= 1.0 - 0.5 * choke_edp_.q_w;
f_sl += 0.5 * choke_edp_.q_w * (f - f_old);
#endif
choke_edv_.v_ave = f;
choke_edv_.v_slope = f_sl;
}