bug 115752 Eliminate 1 8k and 2 4k allocations per gif by using a

caching gif allocator that reuses previously allocated
memory. r=pavlov, sr=sfraser
This commit is contained in:
dp%netscape.com 2001-12-20 06:35:18 +00:00
Родитель b14a4b4d21
Коммит f25959a741
3 изменённых файлов: 177 добавлений и 11 удалений

Просмотреть файл

@ -76,6 +76,11 @@ mailing address.
#include "prlog.h"
#include "GIF2.h"
#include "nsCRT.h"
#include "nsGifAllocator.h"
#include "nsAutoLock.h"
// Global gif allocator
nsGifAllocator *gGifAllocator = nsnull;
#define HOWMANY(x, r) (((x) + ((r) - 1)) / (r))
#define ROUNDUP(x, r) (HOWMANY(x, r) * (r))
@ -447,6 +452,159 @@ PRBool gif_create(gif_struct **gs)
return PR_TRUE;
}
/*******************************************************************************
* Gif decoder allocator
*
* For every image that gets loaded, we allocate
* 4097 x 2 : gs->prefix
* 4097 x 1 : gs->suffix
* 4097 x 1 : gs->stack
* for lzw to operate on the data. These are held for a very short interval
* and freed. This allocator tries to keep one set of these around
* and reuses them; automatically fails over to use calloc/free when all
* buckets are full.
*/
void
nsGifAllocator::ClearBuckets()
{
nsAutoLock autolock(mLock);
for (PRUint32 i = 0; i < kNumBuckets; i++)
{
if (mMemBucket[i])
{
// If the bucket is in use, then we will leak that memory.
PR_ASSERT(!IsUsed(i));
if (!IsUsed(i))
{
free(mMemBucket[i]);
}
mMemBucket[i] = nsnull;
mSize[i] = 0;
}
}
// Clear the in use flag of all buckets
mFlag = 0;
}
void *
nsGifAllocator::Calloc(PRUint32 items, PRUint32 size)
{
PRUint32 totalsize = items * size;
PRInt32 freeAllocatedBucket = -1;
nsAutoLock autolock(mLock);
for (PRUint32 i = 0; i < kNumBuckets; i++) {
// if bucket is in use or has no memory, dont touch it.
if (IsUsed(i) || !mMemBucket[i])
continue;
// See if we have the memory already allocated. This is the
// most common case.
if (mSize[i] == totalsize) {
// Exact match. zero out memory, increase refcnt and return it
memset(mMemBucket[i], 0, totalsize);
MarkUsed(i);
return mMemBucket[i];
}
// Meanwhile, remember a free bucket that has enough memory
if (mSize[i] >= totalsize)
freeAllocatedBucket = i;
}
// See if we have an allocated bucket
if (freeAllocatedBucket >= 0) {
// Clear it, Mark it used and return ptr
// We need to clear only the size that was requested although
// the bucket may be larger
memset(mMemBucket[freeAllocatedBucket], 0, totalsize);
MarkUsed(freeAllocatedBucket);
return mMemBucket[freeAllocatedBucket];
}
// Make sure we are not holding on to a lock
autolock.unlock();
void *ptr = calloc(items, size);
// Reaquire the lock
autolock.lock();
// Find a free bucket and store allocation
for (i = 0; i < kNumBuckets; i++)
if (!mMemBucket[i]) {
// Found free slot. Store it
PR_ASSERT(!IsUsed(i));
mMemBucket[i] = ptr;
mSize[i] = totalsize;
MarkUsed(i);
return ptr;
}
#ifdef DEBUG_dp
// Warn if we are failing over to calloc and not storing it
// This says we have a misdesigned memory pool. The intent was
// once the pool was full, we would never fail over to calloc.
printf("0x%p - Calloc %d [%dx%d] - FAILOVER 0x%p Memory pool has sizes: ",
this, items*size, items, size, ptr);
for (i = 0; i < kNumBuckets; i++)
{
printf("%d ", mSize[i]);
}
printf("\n");
#endif
return ptr;
}
void nsGifAllocator::Free(void *ptr)
{
nsAutoLock autolock(mLock);
for (PRUint32 i = 0; i < kNumBuckets; i++)
{
if (mMemBucket[i] == ptr)
{
// Ah ha. One of the slots we allocated.
// Nothing to do. Mark it unused.
ClearUsed(i);
return;
}
}
// Release lock before calling free
autolock.unlock();
// Failover to free
free(ptr);
return;
}
static inline void *
gif_calloc(size_t n, size_t s)
{
if (!gGifAllocator)
gGifAllocator = new nsGifAllocator;
if (gGifAllocator)
return gGifAllocator->Calloc(n, s);
else
return calloc(n, s);
}
static inline void
gif_free(void *ptr)
{
if (!ptr)
return;
if (gGifAllocator)
gGifAllocator->Free(ptr);
else
free(ptr);
}
/*******************************************************************************
* setup for gif_struct decoding
*/
@ -883,14 +1041,12 @@ int gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
gs->codemask = (1 << gs->codesize) - 1;
gs->datum = gs->bits = 0;
if (!gs->prefix)
gs->prefix = (PRUint16 *)PR_Calloc(sizeof(PRUint16), MAX_BITS);
if (!gs->prefix)
gs->prefix = (PRUint16 *)gif_calloc(sizeof(PRUint16), MAX_BITS);
if (!gs->suffix)
gs->suffix = ( PRUint8 *)PR_Calloc(sizeof(PRUint8), MAX_BITS);
gs->suffix = ( PRUint8 *)gif_calloc(sizeof(PRUint8), MAX_BITS);
if (!gs->stack)
gs->stack = ( PRUint8 *)PR_Calloc(sizeof(PRUint8), MAX_BITS);
gs->stack = ( PRUint8 *)gif_calloc(sizeof(PRUint8), MAX_BITS);
if( !gs->prefix || !gs->suffix || !gs->stack)
{
/* complete from abort will free prefix & suffix */
@ -1020,6 +1176,8 @@ int gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
{
GIF_RGB* map;
int i;
printf("DEBUG: global_colormap - %d [%d x %d]\n", gs->global_colormap_size * sizeof(GIF_RGB),
gs->global_colormap_size, sizeof(GIF_RGB));
if(!(map = (GIF_RGB*)PR_Calloc(gs->global_colormap_size,
sizeof(GIF_RGB))))
@ -1684,10 +1842,10 @@ gif_destroy(gif_struct *gs)
gif_destroy_transparency(gs);
PR_FREEIF(gs->rowbuf);
PR_FREEIF(gs->prefix);
PR_FREEIF(gs->suffix);
PR_FREEIF(gs->stack);
PR_FREEIF(gs->hold);
gif_free(gs->prefix);
gif_free(gs->suffix);
gif_free(gs->stack);
gif_free(gs->hold);
/* Free the colormap that is not in use. The other one, if
* present, will be freed when the image container is

Просмотреть файл

@ -41,6 +41,7 @@
#include "nsIGenericFactory.h"
#include "nsISupports.h"
#include "nsCOMPtr.h"
#include "nsGifAllocator.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsGIFDecoder2)
@ -52,5 +53,12 @@ static nsModuleComponentInfo components[] =
nsGIFDecoder2Constructor, },
};
NS_IMPL_NSGETMODULE(nsGIFModule2, components)
// GIF module shutdown hook
static void PR_CALLBACK nsGifShutdown(nsIModule *module)
{
// Release cached buffers from zlib allocator
delete gGifAllocator;
}
NS_IMPL_NSGETMODULE_WITH_DTOR(nsGIFModule2, components, nsGifShutdown);

Просмотреть файл