Lag
What is
lag, and how can it be reduced? In networking parlance, "lag" (also called "ping" or latency) refers to the
delay incurred when sending data from one
server to another over a network. In
SL, it's commonly used to describe any slowdown experienced, be it low
framerate, a delay in movement, or a
sim running slowly.
What is server-side lag? |
What is client-side lag?
Reducing server-side lag |
Reducing client-side lag
Server-Side
Each
simulator within SL runs on its own independent
CPU. This means that badly written, inefficient
scripts can affect other
residents in a sim. Lag is not just limited to
physical objects, either. Things like
listeners and
sensors can do more than one might realize. There's often a more efficient--and frequently
simpler--way to implement things.
These
functions can help monitor a sim's "health":
- llGetRegionTimeDilation indicates the sim's time dilation--the delay experienced when the sim cannot keep up with the processing of its tasks even after reducing the time allocated to scripts and physics.
- llGetRegionFPS indicates the mean number of frames per second the simulator is running at. Note: llGetRegionFPS does not return a value that can be consistently used to benchmark performance of a sim. llGetRegionTimeDilation is far more straightforward and will correctly identify when the sim is performing poorly.
Client-Side
There are various
client-related things that are central to lag:
texture and
geometry changes must be transmitted from the server to the client, and this information can add up. SL requires a
lot of bandwidth as it is. Frequent texture and
audio changes can result in a
queue for the client that just keeps growing and growing if the client can't download it fast enough.
Once downloaded, the amount of data can slow down the client if the computer it's running on doesn't have a lot of system or video RAM. Low video RAM can also result in textures being dropped, leading to blurry or missing textures on objects and
avatars. Rendering speeds can also be affected by having to draw avatars or large numbers of
prims, particularly when textured with
alpha textures, can also impact rendering speeds, especially on slower computers.
(It is confusing to technical people to call any slowdown at all lag--technically, the "tardiness" of network data, which happens with long distances, busy servers, and having much too little bandwidth) because in SL it will regularly mean it applies only to the reporting person whose settings are taxing their computer.)
So how does one fix slowdown? As someone who is experiencing (rendering) lag, there are two options: either upgrade the computer or tweak your preferences. For the latter, play around with lowering the details in the preferences window
(Ctrl+P), or even turn off less necessary rendering via the debug
(enable using Ctrl+Alt+D) menu. The preferences are often enough: dropping the draw distance ("Adv. Graphics" tab, and ensure "Disable Far Clip" in the "Graphics" tab is not checked) can help tremendously. "Local
Lighting" is also another lag inducer, particularly since SL 1.7, so disabling that can help too.
To make sure one's created objects aren't causing extra rendering lag, avoiding lots of
polygons and textures is a good idea, but most people have computers that can keep up these days. It's server-side lag that we can really do something about.
Q: What about llSetTextureAnim? Someone told me to take it down because it was generating lag. Isn't it supposed to be client-side?
A: This person did not know what they were talking about. Unless you are constantly calling llSetTextureAnim from your script, texture animations cause only a single object update. llSetTextureAnim does not affect sim load. It causes only very minor client load. However, it should be noted, animated textures tend to be big which can cause increased loading times and can lower your framerate because more texture data needs to be swapped in and out of texture memory when changing your view.
Lag Reduction Tips: Server-Side
- Instead of using many llSetColor updates to make a flashing object, for example, use llSetTextureAnim to work with a texture made up of multicolored squares. It reduces the amount of information the server needs to send to the client (one object update, and one texture, rather than possibly thousands of object updates over a relatively short period of time).
- Instead of using a dynamics function to spin an object, use llTargetOmega. This both reduces the amount of lag on the server caused by calculating physics, as well as stops the server from having to transfer all the rotation updates for that object. It often looks better too. Also consider using llSetTextureAnim for predictable object movement.
- Do you really need llSensorRepeat? Probably not. Use llSensor instead.
- Instead of using sensors, things like doors that automatically open (rather than using a touch) can use the collision, collision_start, or collision_end events, or llVolumeDetect.
- Does your overly complex script need to run when nobody's around? If not, why not make it so it doesn't? Using a timer or llVolumeDetect, even llSensorRepeat can dramatically reduce the amount of computation required by the server and increase sim speeds for everyone.
- If you do use llSensorRepeat, think about how long an interval you really need. If you want to have an object look at an avatar, why not have it do llSensorRepeat every 10 seconds or so, then if it detects someone, change it to every 0.5 seconds. When your script no longer detects them, bump it back to 10 seconds.
- Keep your sensor range as small as it needs to be. If you only need to detect someone's presence within 5m, don't make your sensor radius 90m. (And if your sensor range is only 5m, consider using llVolumeDetect--not a sensor.)
- Use a minimum number of llListen calls in your script. Use one listen and utilize IF conditional statements in the listen event in favor of using multiple listens on the same channel using the llListen filters. If you do not need to keep a listen "open" at all times, remove it using llListenRemove or by changing state and re-add the listen only when necessary, such as when an avatar gives your object money or touches the object. If you need to communicate between scripts within the same prim or linked set, consider using llMessageLinked instead.
- Corollary to the previous suggestion: If you have 10 listen scripts in one object (for example, you have 10 scripts in the object that all need to listen), consider using one script to listen, and just use llMessageLinked to talk to the others.
- Also, if you have a lot of control scripts you want to 'say' commands to, consider having them listen on a channel besides 0. That way your scripts wont be parsing all the random chatter from normal conversation. Then use the channel message method. A side benefit is that no one will see your commands and you can bind them to keys using gestures.
- If you have a script that rezzes an object, then llSays text to that object and the two never again communicate, have them use llListenRemove or change state when they're done talking.
- If you're in a FOR loop or a long list of IF conditional statements and you've already completed your task, try using "else if" (when only 1 of the "if" conditions ever needs to be met), or use a return to get out of that event. Why bother running through all those extra iterations and/or comparisons? - This may also be the one place a jump statement is worth using; just don't jump anywhere other than the line of code immediately after your FOR or WHILE loop. - It's better to embed the FOR loop in a WHILE loop instead, actually.
- If you want to check a really large list of strings, (names, for example) against a single string, instead of using a FOR loop to check each list entry against the string, just use llListFindList.
- If you're trying to sort a list into alphabetical order for ease of searching, don't use llListSort and a character lookup table and compare substrings and all that crazy stuff. Just use llListFindList. Sure, it's impressive, but you don't need to reinvent functions.
- If you want to make your script "throttle back" depending on sim performance according to llGetRegionTimeDilation and llGetRegionFPS, don't use a timer with a short interval, as that can cause lag itself. You'll want the interval to be on the order of minutes, not seconds.
- Stuck trying to figure out channels for a script? Spamming several objects with the same llListen channel when you just don't have to? Try this.
- Also try llKey2Name for passing data between objects. It can be used anywhere in the sim!
- An advanced trick for Pros: Use llRemoteLoadScriptPin for passing functions in context. Major lag reduction can be done in this manner, provided you (1) have rights to/own all the objects in play and (2) know their pins.
- Don't put listeners in poseballs! If you need to communicate with a user, use touch_start or llDialog with a timeout.
- Whenever applicable, use llSleep in place of a timer. This works perfectly if your script has nothing to be doing in between triggers.
Lag Reduction Tips: Client-Side
- Use the smallest possible texture size, rounding to the nearest power-of-two size (128x128, 256x256, 512x512, etc). Also, textures don't have to be a perfect square--256x128 is a perfectly valid size. Note that SL will resize your non power-of-two images for you before uploading, but that it may not look anywhere as good as if you resized them yourself). Only upload PNGs or 32-bit TGAs if your texture actually requires alpha transparency; otherwise use 24-bit images. Remember, a texture scaled from 512x512 to 256x256 and then run through a sharpen filter will often look better in addition to requiring 1/4 the video memory.
- Use as few llTargetOmega calls at once as possible. While reducing server-side lag, it does still place a considerable amount of strain on your client.
- If you upload a texture that is not square (i.e., 256X128) it will look "stretched" in the texture viewer in the client. That's OK, when you place the texture on a prim, set the per-face settings so that it looks right.
- If you're not actually playing audio, don't even use llPreloadSound. Why bother forcing the client to download it if they're not even going to hear it? I've seen jukeboxes that preloaded 250 audio clips. That was just insane.
- Corollary: Don't preload audio you don't plan on playing. For example, if the preloaded audio is for a game, don't attempt to make it preload unless it looks like someone is actually about to play the game.
- Check that your scripts aren't forcing many full-primitive updates. To do this, activate the debug menu (Ctrl+Shift+D), then click "Show Updates". Full-primitive updates are highlighted with red cone-halos, terse primitive updates are highlighted with blue cone-halos, and primitive de-rez requests are highlighted with green cone-halos. Full-primitive updates force the server to re-transmit the entire status of the primitive to the client. These updates are also bandwith-intensive, so if a client recieves too many of these it may max out its bandwith and start dropping packets. (Note: "packet loss" literally means that packets are lost, whereas network latency, what we traditionally think of as "lag", only results in slow transfer speeds, not data loss.) Sadly, certain simple functions (such as llSetText, last time I checked) perform full-primitive updates every time they're used, so use them carefully.
Style Guide |
Simulator |
Server |
Client