Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

OpenClient: Cache operation discussion

Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
01-14-2007 16:01
It appears the new 'preview' client uses different cache system -- utilizes the native filesystem to create folders and sub-folders with files stored individually, rather than single large file as it is atm.
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
01-15-2007 04:37
Ack, they beat the open-source community to it and didn't mention anything! That's a bit cheap of them :P

This is for textures only, but since they've already done most of the work it probably wouldn't take much to do the same to sounds and maybe animations (anything that's bigger than a prim).
The blog post reads:
From: Steve Linden
** Unlimited texture cache size

Which is sounds a bit odd, does that mean this new texture cache will never cull old items? Or does it cull them by time rather than cache size?
_____________________
Computer (Mac Pro):
2 x Quad Core 3.2ghz Xeon
10gb DDR2 800mhz FB-DIMMS
4 x 750gb, 32mb cache hard-drives (RAID-0/striped)
NVidia GeForce 8800GT (512mb)
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
01-15-2007 09:34
From: Haravikk Mistral
Which is sounds a bit odd, does that mean this new texture cache will never cull old items? Or does it cull them by time rather than cache size?

It just means you can manually specify cache size limit which can be greater than 1gb it was capped at up to now. (i think the new limit is something like 10 gb according to the blog comments, not sure)
Scalar Tardis
SL Scientist/Engineer
Join date: 5 Nov 2005
Posts: 249
01-16-2007 00:02
It's too early to get upset about this, but I wonder what would happen if the following code were disabled? :)


In viewer.cpp:

CODE
void cleanup_app()
{
//flag all elements as needing to be destroyed immediately
// to ensure shutdown order
[....SNIP....]
// delete some of the files left around in the cache.
remove_cache_files("*.wav");
remove_cache_files("*.tmp");
remove_cache_files("*.lso");
remove_cache_files("*.out");
remove_cache_files("*.dsf");
remove_cache_files("*.bodypart");
remove_cache_files("*.clothing");

llinfos << "Cache files removed" << llendflush;


CODE
// helper function for cleanup_app
void remove_cache_files(const char* file_mask)
{
char mask[LL_MAX_PATH];
sprintf(mask, "%s%s", gDirUtilp->getDirDelimiter().c_str(), file_mask);
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "").c_str(), mask);
}
Scalar Tardis
SL Scientist/Engineer
Join date: 5 Nov 2005
Posts: 249
01-16-2007 00:22
LL should see this coming.... people brought up on LSL now delving into full C/C++.

So, I've just waded into a crash course on maps and multimaps. Oh, it's not biggie really. LSL has something like maps except we call them lists. :)


Except there's one problem. What does "->" mean in C/C++?? I can't exactly search Google for "->". :(

CODE
protected:
LLMutex* mDataMutex;

typedef std::map<LLVFSFileSpecifier, LLVFSFileBlock*> fileblock_map;
fileblock_map mFileBlocks;

typedef std::multimap<S32, LLVFSBlock*> blocks_length_map_t;
blocks_length_map_t mFreeBlocksByLength;
typedef std::multimap<U32, LLVFSBlock*> blocks_location_map_t;
blocks_location_map_t mFreeBlocksByLocation;

// Get a pointer to the next free block (by location).
blocks_location_map_t::iterator next_free_it =
mFreeBlocksByLocation.lower_bound(block->mLocation);
This appears to be defining a map iterator named next_free_it with the lower_bound value of (block->mLocation) (???) in map mFreeBlocksByLocation.

CODE
  mFreeBlocksByLocation.insert(
next_free_it, blocks_location_map_t::value_type(
block->mLocation, block));
And this seems to say to insert the value "next_free_it" and something from map "blocks_location_map_t" into map "mFreeBlocksByLocation"

Where's the C/C++ forum on here? :)
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
01-16-2007 04:36
iirc -> in C refers to a structure variable. A structure being a special variable like an object which contains several different variables. So somewhere you have a structure definition for whatever the block variable is, which has a variable called mLocation inside it, so to reference it you do block->mLocation
_____________________
Computer (Mac Pro):
2 x Quad Core 3.2ghz Xeon
10gb DDR2 800mhz FB-DIMMS
4 x 750gb, 32mb cache hard-drives (RAID-0/striped)
NVidia GeForce 8800GT (512mb)
Scalar Tardis
SL Scientist/Engineer
Join date: 5 Nov 2005
Posts: 249
01-16-2007 09:25
Ah, good. I've been looking for the struct, since that probably contains the full layout of how data is arranged in the Index and Data files. Another clue to backtrack through the source. :)


Though if these maps are used to hold the entire VFS file block list and free block list when the client is running, that may limit the cache performance by how much free memory you have.

If you don't have much system memory (a gig or less), a smaller VFS cache may actually be better for you since that means the file and free block maps will use less of your system's memory.

Imagine caching your hard drive's entire FAT and directory structure in memory at all times when your computer starts up. Hmm, why am I constantly using 900 megs of RAM when playing Solitaire? ;)

.
Draco18s Majestic
Registered User
Join date: 19 Sep 2005
Posts: 2,744
01-16-2007 17:39
From: Scalar Tardis
Except there's one problem. What does "->" mean in C/C++?? I can't exactly search Google for "->". :(


It's either [structure variable]->[variable of structure]
OR a pointer to such an object.
CODE

struct Thing {
int var;
Thing *next;
Thing *prev;
}
...
Thing one;
Thing two;
one->var = 9;
one->next = *two;
two->prev = *one;

so, one->next is the variable two, which is a structure and one->var is a variable that is an integer. one->next->var would be the var of structure two. one->next->prev would be the same as one.
(Note, I kinda suck at pointers and forget if *[var] is the pointer to var or if it's &[var].)
Scalar Tardis
SL Scientist/Engineer
Join date: 5 Nov 2005
Posts: 249
01-16-2007 20:12
I'm poking at some other stuff in the cache folder.

Purpose of .SLC files

Each of these is a cache of all objects in a simulator. When you enter a sim, it looks for a corresponding .SLC and creates it if it does not exist.

indra\newview\llviewerregion.cpp:
CODE
	sprintf(filename, "%s%sobjects_%d_%d.slc", 
gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),
gDirUtilp->getDirDelimiter().c_str(),
U32(mHandle>>32)/REGION_WIDTH_UNITS,
U32(mHandle)/REGION_WIDTH_UNITS );


Sims are referred to by X-Y coordinates:
- "objects_(X-Offset)_(Y-Offset).slc"

And so this is how the client locally keeps track of all objects in each sim.
Scalar Tardis
SL Scientist/Engineer
Join date: 5 Nov 2005
Posts: 249
01-16-2007 20:32
Purpose of .DSF cache files:

This is a Decoded Sound File. Apparantly ogg-vorbis is taking too much time to decode on the fly, so the client is decoding all OGGs into WAVs and storing this raw wave in the cache folder.

A DSF is simply a WAV with an unfamiliar extension. Rename to WAV and you can play it with any audio player. The name of the DSF is its UUID in Second Life.


It's a little weird because the compressed OGG is also being stored in the VFS. It pulls the OGG out of the VFS and decodes it into a WAV. Then when you quit the client, it deletes all the DSF's, and re-decodes them next time you run the client.


An example fragment from lindra\llaudio\llaudiodecodemgr.cpp:
CODE
BOOL LLVorbisDecodeState::initDecode()
{
ov_callbacks vfs_callbacks;
vfs_callbacks.read_func = vfs_read;
vfs_callbacks.seek_func = vfs_seek;
vfs_callbacks.close_func = vfs_close;
vfs_callbacks.tell_func = vfs_tell;

//llinfos << "Initing decode from vfile: " << mUUID << llendl;

mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND);
if (!mInFilep || !mInFilep->getSize())
{
llwarns << "unable to open vorbis source vfile for reading" << llendl;
delete mInFilep;
mInFilep = NULL;
return FALSE;
}

int r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks);
if(r < 0)
{
llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << llendl;
return(FALSE);
}

size_t size_guess = (size_t)ov_pcm_total(&mVF, -1);
vorbis_info* vi = ov_info(&mVF, -1);
size_guess *= vi->channels;
size_guess *= 2;
size_guess += 2048;
mWAVBuffer.reserve(size_guess);
mWAVBuffer.resize(wav_header_size);

{
// write the .wav format header
//"RIFF"
mWAVBuffer[0] = 0x52;
mWAVBuffer[1] = 0x49;
mWAVBuffer[2] = 0x46;
mWAVBuffer[3] = 0x46;
[.........]
Blakar Ogre
Registered User
Join date: 18 Mar 2006
Posts: 209
01-22-2007 04:23
There are apparently many people wondering why one would prefer a cache file over storing things using the standard FS. For general clarity I'll try to list a few reasons. Personally I'm interested in the cache SL uses too as I see it as a typical part where we could get nice performance improvements.

Some reasons why not to use the standard FS:
- The FS tends to use blocks which fixed sizes leading to waste of storage space. For example: Your OS may store files in blocks of 4KB. If you store 10.000 files of 2KB you are storing 20MB but physically it's 40MB.

Off course with current disk sizes this may be neglectable.


- The FS tends to store a lot of irrelevant data about your files. It'll store when it was accessed, created, modified ... It'll store security information of all kinds. This creates tons of overhead and wastes space. So even though the FS of your OS has been worked upon for years it can still be easily beat in performance by something simple because a lot of overhead can be cut out. A typical example on OS level is Unix where you can mount filesystems while instructing the OS to forget about storing access times. During heavy loads this is extremely useful.

At the moment I've no idea how much traffic the VFS sees so I've no idea whether the overhead is something to worry about.

- Overhead of OS calls. If you use seperate files you'll need to open/close them a lot. As described above the OS will maintain loads of data during these calls and hence you'll see a lot of overhead. If you keep everything in 1 file and the index in memory you've a lot less OS overhead.



One alternative could actually be storage in a DB. Some DB's can be geared towards this kind of application and would be able to deliver nice performance.

At first though the focus should be on improving the current VFS. As a start we'd need statistics. How many files are stored, how many requests does the VFS get ... Once that's done we can see what to improve. For example the LRU replacement might nuke your cache at times but statistics should show whether it actually does.

By the time the current VFS is tuned we'd have all the data required to make a sane decision on what could be the most performant solution.
Draco18s Majestic
Registered User
Join date: 19 Sep 2005
Posts: 2,744
01-23-2007 11:46
That's why I said to store textures in the OS FS and prims and other data in the VFS. Textures are nearly garunteed to be of a size to waste the least amount of space on the HD.
Prim data is all the same size--as far as I've come to be able to tell--so when a prim doesn't fit into the VFS it can delete one prim to make room, as opposed to a texture which might delete 14 things in random places freeing up random free space before it finds a chunk it fits in. The OS FS is better at this, combining free space and making it available all the time.

If you wanted to be tricky about it, you could store anything that its size is a certain ratio of the OS FS block size (say, 500% or more) to the OS FS and anything that's under that (where the amount of waste relative to the size of the file is significant) into the VFS.
Hara Surya
Registered User
Join date: 25 Sep 2006
Posts: 1
04-12-2007 23:28
I'm sorry, I understand there is an underlying purpose, but to me this is a simple memory leak written as "function". I have 2GB ram and I still throw off to swap simply because SL ends up using more than 1.75GB! Relogging fixes it, but I can play other games for hours and not have to reload it, but SL it could be as often as every 15 minutes in a busy sim.

The block size argument for not writing to disk would make sense except hard drives are typically HUGE anymore. As long as a disk cache limit is high enough, then the argument is moot. (Or throw all of the small files into a tarball kept in memory, and the large files on disk.)

But mostly it's that the game simply uses too much memory, seeing how it seems the VFS is the cause (and 1.75GB is too big), why have it at all?
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
04-13-2007 04:55
While I can understand using a VFS for primitives, what doesn't make sense is why we just have ONE. What's stopping us from having a VFS for every simulator we visit? This way if each one is 1gb, and we set a 10gb limit we just delete the oldest/last-opened one when we visit a new simulator so it can get its 1gb slice.

Then we can have a big-ass texture cache, or a texture-cache for each simulator using the same principle, when a sim's VFS gets canned, so does it's textures folder. This way we instantly have huge increase in cache capacity, better caching of frequently visited locations, and no overhead/slow-down because we only have to open the cache required for the current simulator, all the others can be ignored. This way we have the same size/performance cache, but we have lots of them so if you visit five sims frequently, you can store cache data for all five.
_____________________
Computer (Mac Pro):
2 x Quad Core 3.2ghz Xeon
10gb DDR2 800mhz FB-DIMMS
4 x 750gb, 32mb cache hard-drives (RAID-0/striped)
NVidia GeForce 8800GT (512mb)
1 2