aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llthrottle.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/llmessage/llthrottle.cpp561
1 files changed, 561 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llthrottle.cpp b/linden/indra/llmessage/llthrottle.cpp
new file mode 100644
index 0000000..87dc929
--- /dev/null
+++ b/linden/indra/llmessage/llthrottle.cpp
@@ -0,0 +1,561 @@
1/**
2 * @file llthrottle.cpp
3 * @brief LLThrottle class used for network bandwidth control.
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "llthrottle.h"
31#include "llmath.h"
32#include "lldatapacker.h"
33#include "message.h"
34
35
36LLThrottle::LLThrottle(const F32 rate)
37{
38 mRate = rate;
39 mAvailable = 0.f;
40 mLookaheadSecs = 0.25f;
41 mLastSendTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
42}
43
44
45void LLThrottle::setRate(const F32 rate)
46{
47 // Need to accumulate available bits when adjusting the rate.
48 mAvailable = getAvailable();
49 mLastSendTime = LLMessageSystem::getMessageTimeSeconds();
50 mRate = rate;
51}
52
53F32 LLThrottle::getAvailable()
54{
55 // use a temporary bits_available
56 // since we don't want to change mBitsAvailable every time
57 F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
58 return mAvailable + (mRate * elapsed_time);
59}
60
61BOOL LLThrottle::checkOverflow(const F32 amount)
62{
63 BOOL retval = TRUE;
64
65 F32 lookahead_amount = mRate * mLookaheadSecs;
66
67 // use a temporary bits_available
68 // since we don't want to change mBitsAvailable every time
69 F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
70 F32 amount_available = mAvailable + (mRate * elapsed_time);
71
72 if ((amount_available >= lookahead_amount) || (amount_available > amount))
73 {
74 // ...enough space to send this message
75 // Also do if > lookahead so we can use if amount > capped amount.
76 retval = FALSE;
77 }
78
79 return retval;
80}
81
82BOOL LLThrottle::throttleOverflow(const F32 amount)
83{
84 F32 elapsed_time;
85 F32 lookahead_amount;
86 BOOL retval = TRUE;
87
88 lookahead_amount = mRate * mLookaheadSecs;
89
90 F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
91 elapsed_time = (F32)(mt_sec - mLastSendTime);
92 mLastSendTime = mt_sec;
93
94 mAvailable += mRate * elapsed_time;
95
96 if (mAvailable >= lookahead_amount)
97 {
98 // ...channel completely open, so allow send regardless
99 // of size. This allows sends on very low BPS channels.
100 mAvailable = lookahead_amount;
101 retval = FALSE;
102 }
103 else if (mAvailable > amount)
104 {
105 // ...enough space to send this message
106 retval = FALSE;
107 }
108
109 // We actually already sent the bits.
110 mAvailable -= amount;
111
112 // What if bitsavailable goes negative?
113 // That's OK, because it means someone is banging on the channel,
114 // so we need some time to recover.
115
116 return retval;
117}
118
119
120
121const F32 THROTTLE_LOOKAHEAD_TIME = 1.f; // seconds
122
123// Make sure that we don't set above these
124// values, even if the client asks to be set
125// higher
126// Note that these values are replicated on the
127// client side to set max bandwidth throttling there,
128// in llviewerthrottle.cpp. These values are the sum
129// of the top two tiers of bandwidth there.
130
131F32 gThrottleMaximumBPS[TC_EOF] =
132{
133 150000.f, // TC_RESEND
134 170000.f, // TC_LAND
135 34000.f, // TC_WIND
136 34000.f, // TC_CLOUD
137 446000.f, // TC_TASK
138 446000.f, // TC_TEXTURE
139 220000.f, // TC_ASSET
140};
141
142// Start low until viewer informs us of capability
143// Asset and resend get high values, since they
144// aren't used JUST by the viewer necessarily.
145// This is a HACK and should be dealt with more properly on
146// circuit creation.
147
148F32 gThrottleDefaultBPS[TC_EOF] =
149{
150 100000.f, // TC_RESEND
151 4000.f, // TC_LAND
152 4000.f, // TC_WIND
153 4000.f, // TC_CLOUD
154 4000.f, // TC_TASK
155 4000.f, // TC_TEXTURE
156 100000.f, // TC_ASSET
157};
158
159// Don't throttle down lower than this
160// This potentially wastes 50 kbps, but usually
161// wont.
162F32 gThrottleMinimumBPS[TC_EOF] =
163{
164 10000.f, // TC_RESEND
165 10000.f, // TC_LAND
166 4000.f, // TC_WIND
167 4000.f, // TC_CLOUD
168 20000.f, // TC_TASK
169 10000.f, // TC_TEXTURE
170 10000.f, // TC_ASSET
171};
172
173const char* THROTTLE_NAMES[TC_EOF] =
174{
175 "Resend ",
176 "Land ",
177 "Wind ",
178 "Cloud ",
179 "Task ",
180 "Texture",
181 "Asset "
182};
183
184LLThrottleGroup::LLThrottleGroup()
185{
186 S32 i;
187 for (i = 0; i < TC_EOF; i++)
188 {
189 mThrottleTotal[i] = gThrottleDefaultBPS[i];
190 mNominalBPS[i] = gThrottleDefaultBPS[i];
191 }
192
193 resetDynamicAdjust();
194}
195
196void LLThrottleGroup::packThrottle(LLDataPacker &dp) const
197{
198 S32 i;
199 for (i = 0; i < TC_EOF; i++)
200 {
201 dp.packF32(mThrottleTotal[i], "Throttle");
202 }
203}
204
205void LLThrottleGroup::unpackThrottle(LLDataPacker &dp)
206{
207 S32 i;
208 for (i = 0; i < TC_EOF; i++)
209 {
210 F32 temp_throttle;
211 dp.unpackF32(temp_throttle, "Throttle");
212 temp_throttle = llclamp(temp_throttle, 0.f, 2250000.f);
213 mThrottleTotal[i] = temp_throttle;
214 if(mThrottleTotal[i] > gThrottleMaximumBPS[i])
215 {
216 mThrottleTotal[i] = gThrottleMaximumBPS[i];
217 }
218 }
219}
220
221// Call this whenever mNominalBPS changes. Need to reset
222// the measurement systems. In the future, we should look
223// into NOT resetting the system.
224void LLThrottleGroup::resetDynamicAdjust()
225{
226 F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
227 S32 i;
228 for (i = 0; i < TC_EOF; i++)
229 {
230 mCurrentBPS[i] = mNominalBPS[i];
231 mBitsAvailable[i] = mNominalBPS[i] * THROTTLE_LOOKAHEAD_TIME;
232 mLastSendTime[i] = mt_sec;
233 mBitsSentThisPeriod[i] = 0;
234 mBitsSentHistory[i] = 0;
235 }
236 mDynamicAdjustTime = mt_sec;
237}
238
239
240BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec)
241{
242 BOOL changed = FALSE;
243 S32 i;
244 for (i = 0; i < TC_EOF; i++)
245 {
246 if (mNominalBPS[i] != throttle_vec[i])
247 {
248 changed = TRUE;
249 mNominalBPS[i] = throttle_vec[i];
250 }
251 }
252
253 // If we changed the nominal settings, reset the dynamic
254 // adjustment subsystem.
255 if (changed)
256 {
257 resetDynamicAdjust();
258 }
259
260 return changed;
261}
262
263
264BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits)
265{
266 BOOL retval = TRUE;
267
268 F32 category_bps = mCurrentBPS[throttle_cat];
269 F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
270
271 // use a temporary bits_available
272 // since we don't want to change mBitsAvailable every time
273 F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]);
274 F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time);
275
276 if (bits_available >= lookahead_bits)
277 {
278 // ...channel completely open, so allow send regardless
279 // of size. This allows sends on very low BPS channels.
280 mBitsAvailable[throttle_cat] = lookahead_bits;
281 retval = FALSE;
282 }
283 else if ( bits_available > bits )
284 {
285 // ...enough space to send this message
286 retval = FALSE;
287 }
288
289 return retval;
290}
291
292BOOL LLThrottleGroup::throttleOverflow(S32 throttle_cat, F32 bits)
293{
294 F32 elapsed_time;
295 F32 category_bps;
296 F32 lookahead_bits;
297 BOOL retval = TRUE;
298
299 category_bps = mCurrentBPS[throttle_cat];
300 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
301
302 F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
303 elapsed_time = (F32)(mt_sec - mLastSendTime[throttle_cat]);
304 mLastSendTime[throttle_cat] = mt_sec;
305 mBitsAvailable[throttle_cat] += category_bps * elapsed_time;
306
307 if (mBitsAvailable[throttle_cat] >= lookahead_bits)
308 {
309 // ...channel completely open, so allow send regardless
310 // of size. This allows sends on very low BPS channels.
311 mBitsAvailable[throttle_cat] = lookahead_bits;
312 retval = FALSE;
313 }
314 else if ( mBitsAvailable[throttle_cat] > bits )
315 {
316 // ...enough space to send this message
317 retval = FALSE;
318 }
319
320 // We actually already sent the bits.
321 mBitsAvailable[throttle_cat] -= bits;
322
323 mBitsSentThisPeriod[throttle_cat] += bits;
324
325 // What if bitsavailable goes negative?
326 // That's OK, because it means someone is banging on the channel,
327 // so we need some time to recover.
328
329 return retval;
330}
331
332
333BOOL LLThrottleGroup::dynamicAdjust()
334{
335 const F32 DYNAMIC_ADJUST_TIME = 1.0f; // seconds
336 const F32 CURRENT_PERIOD_WEIGHT = .25f; // how much weight to give to last period while determining BPS utilization
337 const F32 BUSY_PERCENT = 0.75f; // if use more than this fraction of BPS, you are busy
338 const F32 IDLE_PERCENT = 0.70f; // if use less than this fraction, you are "idle"
339 const F32 TRANSFER_PERCENT = 0.90f; // how much unused bandwidth to take away each adjustment
340 const F32 RECOVER_PERCENT = 0.25f; // how much to give back during recovery phase
341
342 S32 i;
343
344 F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
345
346 // Only dynamically adjust every few seconds
347 if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME)
348 {
349 return FALSE;
350 }
351 mDynamicAdjustTime = mt_sec;
352
353 S32 total = 0;
354 // Update historical information
355 for (i = 0; i < TC_EOF; i++)
356 {
357 if (mBitsSentHistory[i] == 0)
358 {
359 // first run, just copy current period
360 mBitsSentHistory[i] = mBitsSentThisPeriod[i];
361 }
362 else
363 {
364 // have some history, so weight accordingly
365 mBitsSentHistory[i] = (1.f - CURRENT_PERIOD_WEIGHT) * mBitsSentHistory[i]
366 + CURRENT_PERIOD_WEIGHT * mBitsSentThisPeriod[i];
367 }
368
369 mBitsSentThisPeriod[i] = 0;
370 total += llround(mBitsSentHistory[i]);
371 }
372
373 // Look for busy channels
374 // TODO: Fold into loop above.
375 BOOL channels_busy = FALSE;
376 F32 busy_nominal_sum = 0;
377 BOOL channel_busy[TC_EOF];
378 BOOL channel_idle[TC_EOF];
379 BOOL channel_over_nominal[TC_EOF];
380
381 for (i = 0; i < TC_EOF; i++)
382 {
383 // Is this a busy channel?
384 if (mBitsSentHistory[i] >= BUSY_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i])
385 {
386 // this channel is busy
387 channels_busy = TRUE;
388 busy_nominal_sum += mNominalBPS[i]; // use for allocation of pooled idle bandwidth
389 channel_busy[i] = TRUE;
390 }
391 else
392 {
393 channel_busy[i] = FALSE;
394 }
395
396 // Is this an idle channel?
397 if ((mBitsSentHistory[i] < IDLE_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i]) &&
398 (mBitsAvailable[i] > 0))
399 {
400 channel_idle[i] = TRUE;
401 }
402 else
403 {
404 channel_idle[i] = FALSE;
405 }
406
407 // Is this an overpumped channel?
408 if (mCurrentBPS[i] > mNominalBPS[i])
409 {
410 channel_over_nominal[i] = TRUE;
411 }
412 else
413 {
414 channel_over_nominal[i] = FALSE;
415 }
416
417 //if (total)
418 //{
419 // llinfos << i << ": B" << channel_busy[i] << " I" << channel_idle[i] << " N" << channel_over_nominal[i];
420 // llcont << " Nom: " << mNominalBPS[i] << " Cur: " << mCurrentBPS[i] << " BS: " << mBitsSentHistory[i] << llendl;
421 //}
422 }
423
424 if (channels_busy)
425 {
426 // Some channels are busy. Let's see if we can get them some bandwidth.
427 F32 used_bps;
428 F32 avail_bps;
429 F32 transfer_bps;
430
431 F32 pool_bps = 0;
432
433 for (i = 0; i < TC_EOF; i++)
434 {
435 if (channel_idle[i] || channel_over_nominal[i] )
436 {
437 // Either channel i is idle, or has been overpumped.
438 // Therefore it's a candidate to give up some bandwidth.
439 // Figure out how much bandwidth it has been using, and how
440 // much is available to steal.
441 used_bps = mBitsSentHistory[i] / DYNAMIC_ADJUST_TIME;
442
443 // CRO make sure to keep a minimum amount of throttle available
444 // CRO NB: channels set to < MINIMUM_BPS will never give up bps,
445 // which is correct I think
446 if (used_bps < gThrottleMinimumBPS[i])
447 {
448 used_bps = gThrottleMinimumBPS[i];
449 }
450
451 if (channel_over_nominal[i])
452 {
453 F32 unused_current = mCurrentBPS[i] - used_bps;
454 avail_bps = llmax(mCurrentBPS[i] - mNominalBPS[i], unused_current);
455 }
456 else
457 {
458 avail_bps = mCurrentBPS[i] - used_bps;
459 }
460
461 //llinfos << i << " avail " << avail_bps << llendl;
462
463 // Historically, a channel could have used more than its current share,
464 // even if it's idle right now.
465 // Make sure we don't steal too much.
466 if (avail_bps < 0)
467 {
468 continue;
469 }
470
471 // Transfer some bandwidth from this channel into the global pool.
472 transfer_bps = avail_bps * TRANSFER_PERCENT;
473 mCurrentBPS[i] -= transfer_bps;
474 pool_bps += transfer_bps;
475 }
476 }
477
478 //llinfos << "Pool BPS: " << pool_bps << llendl;
479 // Now redistribute the bandwidth to busy channels.
480 F32 unused_bps = 0.f;
481
482 for (i = 0; i < TC_EOF; i++)
483 {
484 if (channel_busy[i])
485 {
486 F32 add_amount = pool_bps * (mNominalBPS[i] / busy_nominal_sum);
487 //llinfos << "Busy " << i << " gets " << pool_bps << llendl;
488 mCurrentBPS[i] += add_amount;
489
490 // CRO: make sure this doesn't get too huge
491 // JC - Actually, need to let mCurrentBPS go less than nominal, otherwise
492 // you aren't allowing bandwidth to actually be moved from one channel
493 // to another.
494 // *TODO: If clamping high end, would be good to re-
495 // allocate to other channels in the above code.
496 const F32 MAX_BPS = 4 * mNominalBPS[i];
497 if (mCurrentBPS[i] > MAX_BPS)
498 {
499 F32 overage = mCurrentBPS[i] - MAX_BPS;
500 mCurrentBPS[i] -= overage;
501 unused_bps += overage;
502 }
503
504 // Paranoia
505 if (mCurrentBPS[i] < gThrottleMinimumBPS[i])
506 {
507 mCurrentBPS[i] = gThrottleMinimumBPS[i];
508 }
509 }
510 }
511
512 // For fun, add the overage back in to objects
513 if (unused_bps > 0.f)
514 {
515 mCurrentBPS[TC_TASK] += unused_bps;
516 }
517 }
518 else
519 {
520 // No one is busy.
521 // Make the channel allocations seek toward nominal.
522
523 // Look for overpumped channels
524 F32 starved_nominal_sum = 0;
525 F32 avail_bps = 0;
526 F32 transfer_bps = 0;
527 F32 pool_bps = 0;
528 for (i = 0; i < TC_EOF; i++)
529 {
530 if (mCurrentBPS[i] > mNominalBPS[i])
531 {
532 avail_bps = (mCurrentBPS[i] - mNominalBPS[i]);
533 transfer_bps = avail_bps * RECOVER_PERCENT;
534
535 mCurrentBPS[i] -= transfer_bps;
536 pool_bps += transfer_bps;
537 }
538 }
539
540 // Evenly distribute bandwidth to channels currently
541 // using less than nominal.
542 for (i = 0; i < TC_EOF; i++)
543 {
544 if (mCurrentBPS[i] < mNominalBPS[i])
545 {
546 // We're going to weight allocations by nominal BPS.
547 starved_nominal_sum += mNominalBPS[i];
548 }
549 }
550
551 for (i = 0; i < TC_EOF; i++)
552 {
553 if (mCurrentBPS[i] < mNominalBPS[i])
554 {
555 // Distribute bandwidth according to nominal allocation ratios.
556 mCurrentBPS[i] += pool_bps * (mNominalBPS[i] / starved_nominal_sum);
557 }
558 }
559 }
560 return TRUE;
561}