Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

codel: use Newton method instead of sqrt() and divides

As Van pointed out, interval/sqrt(count) can be implemented using
multiplies only.

http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots

This patch implements the Newton method and reciprocal divide.

Total cost is 15 cycles instead of 120 on my Corei5 machine (64bit
kernel).

There is a small 'error' for count values < 5, but we don't really care.

I reuse a hole in struct codel_vars :
- pack the dropping boolean into one bit
- use 31bit to store the reciprocal value of sqrt(count).

Suggested-by: Van Jacobson <van@pollere.net>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Dave Taht <dave.taht@bufferbloat.net>
Cc: Kathleen Nichols <nichols@pollere.com>
Cc: Tom Herbert <therbert@google.com>
Cc: Matt Mathis <mattmathis@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Nandita Dukkipati <nanditad@google.com>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
536edd67 470f16c8

+37 -31
+37 -31
include/net/codel.h
··· 46 46 #include <linux/skbuff.h> 47 47 #include <net/pkt_sched.h> 48 48 #include <net/inet_ecn.h> 49 + #include <linux/reciprocal_div.h> 49 50 50 51 /* Controlling Queue Delay (CoDel) algorithm 51 52 * ========================================= ··· 124 123 * entered dropping state 125 124 * @lastcount: count at entry to dropping state 126 125 * @dropping: set to true if in dropping state 126 + * @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1 127 127 * @first_above_time: when we went (or will go) continuously above target 128 128 * for interval 129 129 * @drop_next: time to drop next packet, or when we dropped last ··· 133 131 struct codel_vars { 134 132 u32 count; 135 133 u32 lastcount; 136 - bool dropping; 134 + bool dropping:1; 135 + u32 rec_inv_sqrt:31; 137 136 codel_time_t first_above_time; 138 137 codel_time_t drop_next; 139 138 codel_time_t ldelay; ··· 161 158 162 159 static void codel_vars_init(struct codel_vars *vars) 163 160 { 164 - vars->drop_next = 0; 165 - vars->first_above_time = 0; 166 - vars->dropping = false; /* exit dropping state */ 167 - vars->count = 0; 168 - vars->lastcount = 0; 161 + memset(vars, 0, sizeof(*vars)); 169 162 } 170 163 171 164 static void codel_stats_init(struct codel_stats *stats) ··· 169 170 stats->maxpacket = 256; 170 171 } 171 172 172 - /* return interval/sqrt(x) with good precision 173 - * relies on int_sqrt(unsigned long x) kernel implementation 173 + /* 174 + * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots 175 + * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) 176 + * 177 + * Here, invsqrt is a fixed point number (< 1.0), 31bit mantissa) 174 178 */ 175 - static u32 codel_inv_sqrt(u32 _interval, u32 _x) 179 + static void codel_Newton_step(struct codel_vars *vars) 176 180 { 177 - u64 interval = _interval; 178 - unsigned long x = _x; 181 + u32 invsqrt = vars->rec_inv_sqrt; 182 + u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 31; 183 + u64 val = (3LL << 31) - ((u64)vars->count * invsqrt2); 179 184 180 - /* Scale operands for max precision */ 185 + val = (val * invsqrt) >> 32; 181 186 182 - #if BITS_PER_LONG == 64 183 - x <<= 32; /* On 64bit arches, we can prescale x by 32bits */ 184 - interval <<= 16; 185 - #endif 186 - 187 - while (x < (1UL << (BITS_PER_LONG - 2))) { 188 - x <<= 2; 189 - interval <<= 1; 190 - } 191 - do_div(interval, int_sqrt(x)); 192 - return (u32)interval; 187 + vars->rec_inv_sqrt = val; 193 188 } 194 189 190 + /* 191 + * CoDel control_law is t + interval/sqrt(count) 192 + * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid 193 + * both sqrt() and divide operation. 194 + */ 195 195 static codel_time_t codel_control_law(codel_time_t t, 196 196 codel_time_t interval, 197 - u32 count) 197 + u32 rec_inv_sqrt) 198 198 { 199 - return t + codel_inv_sqrt(interval, count); 199 + return t + reciprocal_divide(interval, rec_inv_sqrt << 1); 200 200 } 201 201 202 202 203 - static bool codel_should_drop(struct sk_buff *skb, 203 + static bool codel_should_drop(const struct sk_buff *skb, 204 204 unsigned int *backlog, 205 205 struct codel_vars *vars, 206 206 struct codel_params *params, ··· 272 274 */ 273 275 while (vars->dropping && 274 276 codel_time_after_eq(now, vars->drop_next)) { 275 - if (++vars->count == 0) /* avoid zero divides */ 276 - vars->count = ~0U; 277 + vars->count++; /* dont care of possible wrap 278 + * since there is no more divide 279 + */ 280 + codel_Newton_step(vars); 277 281 if (params->ecn && INET_ECN_set_ce(skb)) { 278 282 stats->ecn_mark++; 279 283 vars->drop_next = 280 284 codel_control_law(vars->drop_next, 281 285 params->interval, 282 - vars->count); 286 + vars->rec_inv_sqrt); 283 287 goto end; 284 288 } 285 289 qdisc_drop(skb, sch); ··· 296 296 vars->drop_next = 297 297 codel_control_law(vars->drop_next, 298 298 params->interval, 299 - vars->count); 299 + vars->rec_inv_sqrt); 300 300 } 301 301 } 302 302 } ··· 319 319 if (codel_time_before(now - vars->drop_next, 320 320 16 * params->interval)) { 321 321 vars->count = (vars->count - vars->lastcount) | 1; 322 + /* we dont care if rec_inv_sqrt approximation 323 + * is not very precise : 324 + * Next Newton steps will correct it quadratically. 325 + */ 326 + codel_Newton_step(vars); 322 327 } else { 323 328 vars->count = 1; 329 + vars->rec_inv_sqrt = 0x7fffffff; 324 330 } 325 331 vars->lastcount = vars->count; 326 332 vars->drop_next = codel_control_law(now, params->interval, 327 - vars->count); 333 + vars->rec_inv_sqrt); 328 334 } 329 335 end: 330 336 return skb;