Bug 1084177 - Maintain only one zlib compression context per helper thread. r=njn

--HG--
extra : rebase_source : 3c4caab7e3c305418ea35292002597aae1dacb49
This commit is contained in:
Benjamin Peterson 2014-12-10 23:41:36 -05:00
parent 34b779d2f4
commit 0f7bbdc6a4
5 changed files with 89 additions and 52 deletions

View File

@ -1752,7 +1752,7 @@ ScriptSource::setSourceCopy(ExclusiveContext *cx, SourceBufferHolder &srcBuf,
}
SourceCompressionTask::ResultType
SourceCompressionTask::work()
SourceCompressionTask::work(Compressor &comp)
{
// Try to keep the maximum memory usage down by only allocating half the
// size of the string, first.
@ -1762,8 +1762,7 @@ SourceCompressionTask::work()
if (!compressed)
return OOM;
Compressor comp(reinterpret_cast<const unsigned char *>(ss->uncompressedChars()), inputBytes);
if (!comp.init())
if (!comp.prepare(reinterpret_cast<const unsigned char *>(ss->uncompressedChars()), inputBytes))
return OOM;
comp.setOutput((unsigned char *) compressed, firstSize);

View File

@ -6,8 +6,13 @@
#include "vm/Compression.h"
#include "mozilla/DebugOnly.h"
#include <zlib.h>
#include "js/Utility.h"
using mozilla::DebugOnly;
using namespace js;
static void *
@ -22,79 +27,91 @@ zlib_free(void *cx, void *addr)
js_free(addr);
}
Compressor::Compressor(const unsigned char *inp, size_t inplen)
: inp(inp),
inplen(inplen),
outbytes(0),
initialized(false)
{
MOZ_ASSERT(inplen > 0);
zs.opaque = nullptr;
zs.next_in = (Bytef *)inp;
zs.avail_in = 0;
zs.next_out = nullptr;
zs.avail_out = 0;
zs.zalloc = zlib_alloc;
zs.zfree = zlib_free;
}
Compressor::~Compressor()
{
if (initialized) {
int ret = deflateEnd(&zs);
int ret = deflateEnd(zs);
if (ret != Z_OK) {
// If we finished early, we can get a Z_DATA_ERROR.
MOZ_ASSERT(ret == Z_DATA_ERROR);
MOZ_ASSERT(uInt(zs.next_in - inp) < inplen || !zs.avail_out);
MOZ_ASSERT(uInt(zs->next_in - inp) < inplen || !zs->avail_out);
}
}
js_free(zs);
}
bool
Compressor::init()
Compressor::prepare(const unsigned char *inp_, size_t inplen_)
{
if (inplen >= UINT32_MAX)
return false;
// zlib is slow and we'd rather be done compression sooner
// even if it means decompression is slower which penalizes
// Function.toString()
int ret = deflateInit(&zs, Z_BEST_SPEED);
if (ret != Z_OK) {
MOZ_ASSERT(ret == Z_MEM_ERROR);
MOZ_ASSERT(inplen_ > 0);
if (inplen_ >= UINT32_MAX)
return false;
if (!zs) {
zs = js_pod_malloc<z_stream>();
if (!zs)
return false;
zs->zalloc = zlib_alloc;
zs->zfree = zlib_free;
}
initialized = true;
inp = inp_;
inplen = inplen_;
outbytes = 0;
zs->opaque = nullptr;
zs->next_in = (Bytef *)inp;
zs->avail_in = 0;
zs->next_out = nullptr;
zs->avail_out = 0;
if (initialized) {
DebugOnly<int> ret = deflateReset(zs);
MOZ_ASSERT(ret == Z_OK);
} else {
// zlib is slow and we'd rather be done compression sooner even if it
// means decompression is slower which penalizes Function.toString()
int ret = deflateInit(zs, Z_BEST_SPEED);
if (ret != Z_OK) {
MOZ_ASSERT(ret == Z_MEM_ERROR);
return false;
}
initialized = true;
}
return true;
}
void
Compressor::setOutput(unsigned char *out, size_t outlen)
{
MOZ_ASSERT(initialized);
MOZ_ASSERT(outlen > outbytes);
zs.next_out = out + outbytes;
zs.avail_out = outlen - outbytes;
zs->next_out = out + outbytes;
zs->avail_out = outlen - outbytes;
}
Compressor::Status
Compressor::compressMore()
{
MOZ_ASSERT(zs.next_out);
uInt left = inplen - (zs.next_in - inp);
MOZ_ASSERT(initialized);
MOZ_ASSERT(zs->next_out);
uInt left = inplen - (zs->next_in - inp);
bool done = left <= CHUNKSIZE;
if (done)
zs.avail_in = left;
else if (zs.avail_in == 0)
zs.avail_in = CHUNKSIZE;
Bytef *oldout = zs.next_out;
int ret = deflate(&zs, done ? Z_FINISH : Z_NO_FLUSH);
outbytes += zs.next_out - oldout;
zs->avail_in = left;
else if (zs->avail_in == 0)
zs->avail_in = CHUNKSIZE;
Bytef *oldout = zs->next_out;
int ret = deflate(zs, done ? Z_FINISH : Z_NO_FLUSH);
outbytes += zs->next_out - oldout;
if (ret == Z_MEM_ERROR) {
zs.avail_out = 0;
zs->avail_out = 0;
return OOM;
}
if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) {
MOZ_ASSERT(zs.avail_out == 0);
MOZ_ASSERT(zs->avail_out == 0);
return MOREOUTPUT;
}
MOZ_ASSERT_IF(!done, ret == Z_OK);

View File

@ -7,17 +7,19 @@
#ifndef vm_Compression_h
#define vm_Compression_h
#include <zlib.h>
#include "mozilla/NullPtr.h"
#include "jstypes.h"
struct z_stream_s; // from <zlib.h>
namespace js {
class Compressor
{
/* Number of bytes we should hand to zlib each compressMore() call. */
static const size_t CHUNKSIZE = 2048;
z_stream zs;
struct z_stream_s *zs;
const unsigned char *inp;
size_t inplen;
size_t outbytes;
@ -31,12 +33,22 @@ class Compressor
OOM
};
Compressor(const unsigned char *inp, size_t inplen);
Compressor()
: zs(nullptr),
initialized(false)
{}
~Compressor();
bool init();
/*
* Prepare the compressor to process the string |inp| of length
* |inplen|. Return false if initialization failed (usually OOM).
*
* The compressor may be reused for a different input by calling prepare()
* again.
*/
bool prepare(const unsigned char *inp, size_t inplen);
void setOutput(unsigned char *out, size_t outlen);
size_t outWritten() const { return outbytes; }
/* Compress some of the input. Return true if it should be called again. */
/* Compress some of the input. Return CONTINUE if it should be called again. */
Status compressMore();
};

View File

@ -484,8 +484,10 @@ GlobalHelperThreadState::finish()
{
if (threads) {
MOZ_ASSERT(CanUseExtraThreads());
for (size_t i = 0; i < threadCount; i++)
for (size_t i = 0; i < threadCount; i++) {
threads[i].destroy();
threads[i].~HelperThread();
}
js_free(threads);
}
@ -1201,7 +1203,7 @@ HelperThread::handleCompressionWorkload()
{
AutoUnlockHelperThreadState unlock;
compressionTask->result = compressionTask->work();
compressionTask->result = compressionTask->work(sourceCompressor);
}
compressionTask->helperThread = nullptr;

View File

@ -21,6 +21,7 @@
#include "frontend/TokenStream.h"
#include "jit/Ion.h"
#include "vm/Compression.h"
namespace js {
@ -306,6 +307,12 @@ struct HelperThread
/* Any source being compressed on this thread. */
SourceCompressionTask *compressionTask;
/*
* Compressor that is used for servicing SourceCompressionTasks on this
* thread.
*/
Compressor sourceCompressor;
/* Any GC state for background sweeping or allocating being performed. */
GCHelperState *gcHelperState;
@ -533,7 +540,7 @@ struct SourceCompressionTask
complete();
}
ResultType work();
ResultType work(Compressor &comp);
bool complete();
void abort() { abort_ = true; }
bool active() const { return !!ss; }