PageRenderTime 57ms CodeModel.GetById 2ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llthrottle.cpp

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