llthrottle.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "llthrottle.h"
00035 #include "llmath.h"
00036 #include "lldatapacker.h"
00037 #include "message.h"
00038 
00039 
00040 LLThrottle::LLThrottle(const F32 rate)
00041 {
00042         mRate = rate;
00043         mAvailable = 0.f;
00044         mLookaheadSecs = 0.25f;
00045         mLastSendTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
00046 }
00047 
00048 
00049 void LLThrottle::setRate(const F32 rate)
00050 {
00051         // Need to accumulate available bits when adjusting the rate.
00052         mAvailable = getAvailable();
00053         mLastSendTime = LLMessageSystem::getMessageTimeSeconds();
00054         mRate = rate;
00055 }
00056 
00057 F32 LLThrottle::getAvailable()
00058 {
00059         // use a temporary bits_available
00060         // since we don't want to change mBitsAvailable every time
00061         F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
00062         return mAvailable + (mRate * elapsed_time);
00063 }
00064 
00065 BOOL LLThrottle::checkOverflow(const F32 amount)
00066 {
00067         BOOL retval = TRUE;
00068 
00069         F32 lookahead_amount = mRate * mLookaheadSecs;
00070 
00071         // use a temporary bits_available
00072         // since we don't want to change mBitsAvailable every time
00073         F32 elapsed_time =  (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
00074         F32 amount_available = mAvailable + (mRate * elapsed_time);
00075 
00076         if ((amount_available >= lookahead_amount) || (amount_available > amount))
00077         {
00078                 // ...enough space to send this message
00079                 // Also do if > lookahead so we can use if amount > capped amount.
00080                 retval = FALSE;
00081         }
00082         
00083         return retval;
00084 }
00085 
00086 BOOL LLThrottle::throttleOverflow(const F32 amount)
00087 {
00088         F32 elapsed_time;
00089         F32 lookahead_amount;
00090         BOOL retval = TRUE;
00091 
00092         lookahead_amount = mRate * mLookaheadSecs;
00093 
00094         F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
00095         elapsed_time = (F32)(mt_sec - mLastSendTime);
00096         mLastSendTime = mt_sec;
00097 
00098         mAvailable += mRate * elapsed_time;
00099 
00100         if (mAvailable >= lookahead_amount)
00101         {
00102                 // ...channel completely open, so allow send regardless
00103                 // of size.  This allows sends on very low BPS channels.
00104                 mAvailable = lookahead_amount;
00105                 retval = FALSE;
00106         }
00107         else if (mAvailable > amount)
00108         {
00109                 // ...enough space to send this message
00110                 retval = FALSE;
00111         }
00112 
00113         // We actually already sent the bits.
00114         mAvailable -= amount;
00115 
00116         // What if bitsavailable goes negative?
00117         // That's OK, because it means someone is banging on the channel,
00118         // so we need some time to recover.
00119 
00120         return retval;
00121 }
00122 
00123 
00124 
00125 const F32 THROTTLE_LOOKAHEAD_TIME = 1.f;        // seconds
00126 
00127 // Make sure that we don't set above these
00128 // values, even if the client asks to be set
00129 // higher
00130 // Note that these values are replicated on the 
00131 // client side to set max bandwidth throttling there,
00132 // in llviewerthrottle.cpp. These values are the sum
00133 // of the top two tiers of bandwidth there.
00134 
00135 F32 gThrottleMaximumBPS[TC_EOF] =
00136 {
00137         150000.f, // TC_RESEND
00138         170000.f, // TC_LAND
00139         34000.f, // TC_WIND
00140         34000.f, // TC_CLOUD
00141         446000.f, // TC_TASK
00142         446000.f, // TC_TEXTURE
00143         220000.f, // TC_ASSET
00144 };
00145 
00146 // Start low until viewer informs us of capability
00147 // Asset and resend get high values, since they
00148 // aren't used JUST by the viewer necessarily.
00149 // This is a HACK and should be dealt with more properly on
00150 // circuit creation.
00151 
00152 F32 gThrottleDefaultBPS[TC_EOF] =
00153 {
00154         100000.f, // TC_RESEND
00155         4000.f, // TC_LAND
00156         4000.f, // TC_WIND
00157         4000.f, // TC_CLOUD
00158         4000.f, // TC_TASK
00159         4000.f, // TC_TEXTURE
00160         100000.f, // TC_ASSET
00161 };
00162 
00163 // Don't throttle down lower than this
00164 // This potentially wastes 50 kbps, but usually
00165 // wont.
00166 F32 gThrottleMinimumBPS[TC_EOF] =
00167 {
00168         10000.f,        // TC_RESEND
00169         10000.f,        // TC_LAND
00170          4000.f,        // TC_WIND
00171          4000.f,        // TC_CLOUD
00172         20000.f,        // TC_TASK
00173         10000.f,        // TC_TEXTURE
00174         10000.f,        // TC_ASSET
00175 };
00176 
00177 const char* THROTTLE_NAMES[TC_EOF] =
00178 {
00179         "Resend ",
00180         "Land   ",
00181         "Wind   ",
00182         "Cloud  ",
00183         "Task   ",
00184         "Texture",
00185         "Asset  "
00186 };
00187 
00188 LLThrottleGroup::LLThrottleGroup()
00189 {
00190         S32 i;
00191         for (i = 0; i < TC_EOF; i++)
00192         {
00193                 mThrottleTotal[i]       = gThrottleDefaultBPS[i];
00194                 mNominalBPS[i]          = gThrottleDefaultBPS[i];
00195         }
00196 
00197         resetDynamicAdjust();
00198 }
00199 
00200 void LLThrottleGroup::packThrottle(LLDataPacker &dp) const
00201 {
00202         S32 i;
00203         for (i = 0; i < TC_EOF; i++)
00204         {
00205                 dp.packF32(mThrottleTotal[i], "Throttle");
00206         }
00207 }
00208 
00209 void LLThrottleGroup::unpackThrottle(LLDataPacker &dp)
00210 {
00211         S32 i;
00212         for (i = 0; i < TC_EOF; i++)
00213         {
00214                 F32 temp_throttle;
00215                 dp.unpackF32(temp_throttle, "Throttle");
00216                 temp_throttle = llclamp(temp_throttle, 0.f, 2250000.f);
00217                 mThrottleTotal[i] = temp_throttle;
00218                 if(mThrottleTotal[i] > gThrottleMaximumBPS[i])
00219                 {
00220                         mThrottleTotal[i] = gThrottleMaximumBPS[i];
00221                 }
00222         }
00223 }
00224 
00225 // Call this whenever mNominalBPS changes.  Need to reset
00226 // the measurement systems.  In the future, we should look
00227 // into NOT resetting the system.
00228 void LLThrottleGroup::resetDynamicAdjust()
00229 {
00230         F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
00231         S32 i;
00232         for (i = 0; i < TC_EOF; i++)
00233         {
00234                 mCurrentBPS[i]          = mNominalBPS[i];
00235                 mBitsAvailable[i]       = mNominalBPS[i] * THROTTLE_LOOKAHEAD_TIME;
00236                 mLastSendTime[i] = mt_sec;
00237                 mBitsSentThisPeriod[i] = 0;
00238                 mBitsSentHistory[i] = 0;
00239         }
00240         mDynamicAdjustTime = mt_sec;
00241 }
00242 
00243 
00244 BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec)
00245 {
00246         BOOL changed = FALSE;
00247         S32 i;
00248         for (i = 0; i < TC_EOF; i++)
00249         {
00250                 if (mNominalBPS[i] != throttle_vec[i])
00251                 {
00252                         changed = TRUE;
00253                         mNominalBPS[i] = throttle_vec[i];
00254                 }
00255         }
00256 
00257         // If we changed the nominal settings, reset the dynamic
00258         // adjustment subsystem.
00259         if (changed)
00260         {
00261                 resetDynamicAdjust();
00262         }
00263 
00264         return changed;
00265 }
00266 
00267 
00268 BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits)
00269 {
00270         BOOL retval = TRUE;
00271 
00272         F32 category_bps = mCurrentBPS[throttle_cat];
00273         F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
00274 
00275         // use a temporary bits_available
00276         // since we don't want to change mBitsAvailable every time
00277         F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]);
00278         F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time);
00279 
00280         if (bits_available >= lookahead_bits)
00281         {
00282                 // ...channel completely open, so allow send regardless
00283                 // of size.  This allows sends on very low BPS channels.
00284                 mBitsAvailable[throttle_cat] = lookahead_bits;
00285                 retval = FALSE;
00286         }
00287         else if ( bits_available > bits )
00288         {
00289                 // ...enough space to send this message
00290                 retval = FALSE;
00291         }
00292         
00293         return retval;
00294 }
00295 
00296 BOOL LLThrottleGroup::throttleOverflow(S32 throttle_cat, F32 bits)
00297 {
00298         F32 elapsed_time;
00299         F32 category_bps;
00300         F32 lookahead_bits;
00301         BOOL retval = TRUE;
00302 
00303         category_bps = mCurrentBPS[throttle_cat];
00304         lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
00305 
00306         F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
00307         elapsed_time = (F32)(mt_sec - mLastSendTime[throttle_cat]);
00308         mLastSendTime[throttle_cat] = mt_sec;
00309         mBitsAvailable[throttle_cat] += category_bps * elapsed_time;
00310 
00311         if (mBitsAvailable[throttle_cat] >= lookahead_bits)
00312         {
00313                 // ...channel completely open, so allow send regardless
00314                 // of size.  This allows sends on very low BPS channels.
00315                 mBitsAvailable[throttle_cat] = lookahead_bits;
00316                 retval = FALSE;
00317         }
00318         else if ( mBitsAvailable[throttle_cat] > bits )
00319         {
00320                 // ...enough space to send this message
00321                 retval = FALSE;
00322         }
00323 
00324         // We actually already sent the bits.
00325         mBitsAvailable[throttle_cat] -= bits;
00326 
00327         mBitsSentThisPeriod[throttle_cat] += bits;
00328 
00329         // What if bitsavailable goes negative?
00330         // That's OK, because it means someone is banging on the channel,
00331         // so we need some time to recover.
00332 
00333         return retval;
00334 }
00335 
00336 
00337 BOOL LLThrottleGroup::dynamicAdjust()
00338 {
00339         const F32 DYNAMIC_ADJUST_TIME = 1.0f;           // seconds
00340         const F32 CURRENT_PERIOD_WEIGHT = .25f;         // how much weight to give to last period while determining BPS utilization
00341         const F32 BUSY_PERCENT = 0.75f;         // if use more than this fraction of BPS, you are busy
00342         const F32 IDLE_PERCENT = 0.70f;         // if use less than this fraction, you are "idle"
00343         const F32 TRANSFER_PERCENT = 0.90f;     // how much unused bandwidth to take away each adjustment
00344         const F32 RECOVER_PERCENT = 0.25f;      // how much to give back during recovery phase
00345 
00346         S32 i;
00347 
00348         F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
00349 
00350         // Only dynamically adjust every few seconds
00351         if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME)
00352         {
00353                 return FALSE;
00354         }
00355         mDynamicAdjustTime = mt_sec;
00356 
00357         S32 total = 0;
00358         // Update historical information
00359         for (i = 0; i < TC_EOF; i++)
00360         {
00361                 if (mBitsSentHistory[i] == 0)
00362                 {
00363                         // first run, just copy current period
00364                         mBitsSentHistory[i] = mBitsSentThisPeriod[i];
00365                 }
00366                 else
00367                 {
00368                         // have some history, so weight accordingly
00369                         mBitsSentHistory[i] = (1.f - CURRENT_PERIOD_WEIGHT) * mBitsSentHistory[i] 
00370                                 + CURRENT_PERIOD_WEIGHT * mBitsSentThisPeriod[i];
00371                 }
00372 
00373                 mBitsSentThisPeriod[i] = 0;
00374                 total += llround(mBitsSentHistory[i]);
00375         }
00376 
00377         // Look for busy channels
00378         // TODO: Fold into loop above.
00379         BOOL channels_busy = FALSE;
00380         F32  busy_nominal_sum = 0;
00381         BOOL channel_busy[TC_EOF];
00382         BOOL channel_idle[TC_EOF];
00383         BOOL channel_over_nominal[TC_EOF];
00384 
00385         for (i = 0; i < TC_EOF; i++)
00386         {
00387                 // Is this a busy channel?
00388                 if (mBitsSentHistory[i] >= BUSY_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i])
00389                 {
00390                         // this channel is busy
00391                         channels_busy = TRUE;
00392                         busy_nominal_sum += mNominalBPS[i];             // use for allocation of pooled idle bandwidth
00393                         channel_busy[i] = TRUE;
00394                 }
00395                 else
00396                 {
00397                         channel_busy[i] = FALSE;
00398                 }
00399 
00400                 // Is this an idle channel?
00401                 if ((mBitsSentHistory[i] < IDLE_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i]) &&
00402                         (mBitsAvailable[i] > 0))
00403                 {
00404                         channel_idle[i] = TRUE;
00405                 }
00406                 else
00407                 {
00408                         channel_idle[i] = FALSE;
00409                 }
00410 
00411                 // Is this an overpumped channel?
00412                 if (mCurrentBPS[i] > mNominalBPS[i])
00413                 {
00414                         channel_over_nominal[i] = TRUE;
00415                 }
00416                 else
00417                 {
00418                         channel_over_nominal[i] = FALSE;
00419                 }
00420 
00421                 //if (total)
00422                 //{
00423                 //      llinfos << i << ": B" << channel_busy[i] << " I" << channel_idle[i] << " N" << channel_over_nominal[i];
00424                 //      llcont << " Nom: " << mNominalBPS[i] << " Cur: " << mCurrentBPS[i] << " BS: " << mBitsSentHistory[i] << llendl;
00425                 //}
00426         }
00427 
00428         if (channels_busy)
00429         {
00430                 // Some channels are busy.  Let's see if we can get them some bandwidth.
00431                 F32 used_bps;
00432                 F32 avail_bps;
00433                 F32 transfer_bps;
00434 
00435                 F32 pool_bps = 0;
00436 
00437                 for (i = 0; i < TC_EOF; i++)
00438                 {
00439                         if (channel_idle[i] || channel_over_nominal[i] )
00440                         {
00441                                 // Either channel i is idle, or has been overpumped.
00442                                 // Therefore it's a candidate to give up some bandwidth.
00443                                 // Figure out how much bandwidth it has been using, and how
00444                                 // much is available to steal.
00445                                 used_bps = mBitsSentHistory[i] / DYNAMIC_ADJUST_TIME;
00446 
00447                                 // CRO make sure to keep a minimum amount of throttle available
00448                                 // CRO NB: channels set to < MINIMUM_BPS will never give up bps, 
00449                                 // which is correct I think
00450                                 if (used_bps < gThrottleMinimumBPS[i])
00451                                 {
00452                                         used_bps = gThrottleMinimumBPS[i];
00453                                 }
00454 
00455                                 if (channel_over_nominal[i])
00456                                 {
00457                                         F32 unused_current = mCurrentBPS[i] - used_bps;
00458                                         avail_bps = llmax(mCurrentBPS[i] - mNominalBPS[i], unused_current);
00459                                 }
00460                                 else
00461                                 {
00462                                         avail_bps = mCurrentBPS[i] - used_bps;
00463                                 }
00464 
00465                                 //llinfos << i << " avail " << avail_bps << llendl;
00466 
00467                                 // Historically, a channel could have used more than its current share,
00468                                 // even if it's idle right now.
00469                                 // Make sure we don't steal too much.
00470                                 if (avail_bps < 0)
00471                                 {
00472                                         continue;
00473                                 }
00474 
00475                                 // Transfer some bandwidth from this channel into the global pool.
00476                                 transfer_bps = avail_bps * TRANSFER_PERCENT;
00477                                 mCurrentBPS[i] -= transfer_bps;
00478                                 pool_bps += transfer_bps;
00479                         }
00480                 }
00481 
00482                 //llinfos << "Pool BPS: " << pool_bps << llendl;
00483                 // Now redistribute the bandwidth to busy channels.
00484                 F32 unused_bps = 0.f;
00485 
00486                 for (i = 0; i < TC_EOF; i++)
00487                 {
00488                         if (channel_busy[i])
00489                         {
00490                                 F32 add_amount = pool_bps * (mNominalBPS[i] / busy_nominal_sum);
00491                                 //llinfos << "Busy " << i << " gets " << pool_bps << llendl;
00492                                 mCurrentBPS[i] += add_amount;
00493 
00494                                 // CRO: make sure this doesn't get too huge
00495                                 // JC - Actually, need to let mCurrentBPS go less than nominal, otherwise
00496                                 // you aren't allowing bandwidth to actually be moved from one channel
00497                                 // to another.  
00498                                 // *TODO: If clamping high end, would be good to re-
00499                                 // allocate to other channels in the above code.
00500                                 const F32 MAX_BPS = 4 * mNominalBPS[i];
00501                                 if (mCurrentBPS[i] > MAX_BPS)
00502                                 {
00503                                         F32 overage = mCurrentBPS[i] - MAX_BPS;
00504                                         mCurrentBPS[i] -= overage;
00505                                         unused_bps += overage;
00506                                 }
00507 
00508                                 // Paranoia
00509                                 if (mCurrentBPS[i] < gThrottleMinimumBPS[i])
00510                                 {
00511                                         mCurrentBPS[i] = gThrottleMinimumBPS[i];
00512                                 }
00513                         }
00514                 }
00515 
00516                 // For fun, add the overage back in to objects
00517                 if (unused_bps > 0.f)
00518                 {
00519                         mCurrentBPS[TC_TASK] += unused_bps;
00520                 }
00521         }
00522         else
00523         {
00524                 // No one is busy.
00525                 // Make the channel allocations seek toward nominal.
00526 
00527                 // Look for overpumped channels
00528                 F32 starved_nominal_sum = 0;
00529                 F32 avail_bps = 0;
00530                 F32 transfer_bps = 0;
00531                 F32 pool_bps = 0;
00532                 for (i = 0; i < TC_EOF; i++)
00533                 {
00534                         if (mCurrentBPS[i] > mNominalBPS[i])
00535                         {
00536                                 avail_bps = (mCurrentBPS[i] - mNominalBPS[i]);
00537                                 transfer_bps = avail_bps * RECOVER_PERCENT;
00538 
00539                                 mCurrentBPS[i] -= transfer_bps;
00540                                 pool_bps += transfer_bps;
00541                         }
00542                 }
00543 
00544                 // Evenly distribute bandwidth to channels currently
00545                 // using less than nominal.
00546                 for (i = 0; i < TC_EOF; i++)
00547                 {
00548                         if (mCurrentBPS[i] < mNominalBPS[i])
00549                         {
00550                                 // We're going to weight allocations by nominal BPS.
00551                                 starved_nominal_sum += mNominalBPS[i];
00552                         }
00553                 }
00554 
00555                 for (i = 0; i < TC_EOF; i++)
00556                 {
00557                         if (mCurrentBPS[i] < mNominalBPS[i])
00558                         {
00559                                 // Distribute bandwidth according to nominal allocation ratios.
00560                                 mCurrentBPS[i] += pool_bps * (mNominalBPS[i] / starved_nominal_sum);
00561                         }
00562                 }
00563         }
00564         return TRUE;
00565 }

Generated on Thu Jul 1 06:09:19 2010 for Second Life Viewer by  doxygen 1.4.7