Merge pull request #190 from szabadka/master

Reduce command buffer memory usage.
This commit is contained in:
szabadka 2015-10-01 17:25:00 +02:00
commit cd2538b0a7
4 changed files with 30 additions and 18 deletions

View File

@ -81,7 +81,7 @@ class ZopfliCostModel {
size_t position,
const uint8_t* ringbuffer,
size_t ringbuffer_mask) {
std::vector<float> literal_cost(num_bytes);
std::vector<float> literal_cost(num_bytes + 1);
EstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,
ringbuffer, &literal_cost[0]);
literal_costs_.resize(num_bytes + 1);

View File

@ -40,6 +40,9 @@ namespace brotli {
static const int kMinQualityForBlockSplit = 4;
static const int kMinQualityForContextModeling = 5;
static const int kMinQualityForOptimizeHistograms = 4;
// For quality 1 there is no block splitting, so we buffer at most this much
// literals and commands.
static const int kMaxNumDelayedSymbols = 0x2fff;
void RecomputeDistancePrefixes(Command* cmds,
size_t num_commands,
@ -110,9 +113,8 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
int ringbuffer_bits = std::max(params_.lgwin + 1, params_.lgblock + 1);
ringbuffer_ = new RingBuffer(ringbuffer_bits, params_.lgblock);
// Allocate command buffer.
cmd_buffer_size_ = std::max(1 << 18, 1 << params_.lgblock);
commands_ = new brotli::Command[cmd_buffer_size_];
commands_ = 0;
cmd_alloc_size_ = 0;
// Initialize last byte with stream header.
if (params_.lgwin == 16) {
@ -145,7 +147,7 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
BrotliCompressor::~BrotliCompressor() {
delete[] storage_;
delete[] commands_;
free(commands_);
delete ringbuffer_;
delete hashers_;
}
@ -155,10 +157,10 @@ void BrotliCompressor::CopyInputToRingBuffer(const size_t input_size,
ringbuffer_->Write(input_buffer, input_size);
input_pos_ += input_size;
// Erase a few more bytes in the ring buffer to make hashing not
// depend on uninitialized data. This makes compression deterministic
// and it prevents uninitialized memory warnings in Valgrind. Even
// without erasing, the output would be valid (but nondeterministic).
// TL;DR: If needed, initialize 7 more bytes in the ring buffer to make the
// hashing not depend on uninitialized data. This makes compression
// deterministic and it prevents uninitialized memory warnings in Valgrind.
// Even without erasing, the output would be valid (but nondeterministic).
//
// Background information: The compressor stores short (at most 8 bytes)
// substrings of the input already read in a hash table, and detects
@ -186,7 +188,7 @@ void BrotliCompressor::CopyInputToRingBuffer(const size_t input_size,
// subsequent rounds data in the ringbuffer would be affected.
if (pos <= ringbuffer_->mask()) {
// This is the first time when the ring buffer is being written.
// We clear 3 bytes just after the bytes that have been copied from
// We clear 7 bytes just after the bytes that have been copied from
// the input buffer.
//
// The ringbuffer has a "tail" that holds a copy of the beginning,
@ -195,9 +197,9 @@ void BrotliCompressor::CopyInputToRingBuffer(const size_t input_size,
// in this tail (where index may be larger than mask), so that
// we have exactly defined behavior and don't read un-initialized
// memory. Due to performance reasons, hashing reads data using a
// LOAD32, which can go 3 bytes beyond the bytes written in the
// LOAD64, which can go 7 bytes beyond the bytes written in the
// ringbuffer.
memset(ringbuffer_->start() + pos, 0, 3);
memset(ringbuffer_->start() + pos, 0, 7);
}
}
@ -227,6 +229,17 @@ bool BrotliCompressor::WriteBrotliData(const bool is_last,
return false;
}
// Theoretical max number of commands is 1 per 2 bytes.
size_t newsize = num_commands_ + bytes / 2 + 1;
if (newsize > cmd_alloc_size_) {
// Reserve a bit more memory to allow merging with a next block
// without realloc: that would impact speed.
newsize += bytes / 4;
cmd_alloc_size_ = newsize;
commands_ =
static_cast<Command*>(realloc(commands_, sizeof(Command) * newsize));
}
CreateBackwardReferences(bytes, last_processed_pos_, data, mask,
max_backward_distance_,
params_.quality,
@ -238,16 +251,12 @@ bool BrotliCompressor::WriteBrotliData(const bool is_last,
&num_commands_,
&num_literals_);
// For quality 1 there is no block splitting, so we buffer at most this much
// literals and commands.
static const int kMaxNumDelayedSymbols = 0x2fff;
int max_length = std::min<int>(mask + 1, 1 << kMaxInputBlockBits);
if (!is_last && !force_flush &&
(params_.quality >= kMinQualityForBlockSplit ||
(num_literals_ + num_commands_ < kMaxNumDelayedSymbols)) &&
num_commands_ + (input_block_size() >> 1) < cmd_buffer_size_ &&
input_pos_ + input_block_size() <= last_flush_pos_ + max_length) {
// Everything will happen later.
// Merge with next input block. Everything will happen later.
last_processed_pos_ = input_pos_;
*out_size = 0;
return true;
@ -638,4 +647,5 @@ int BrotliCompressWithCustomDictionary(size_t dictsize, const uint8_t* dict,
return true;
}
} // namespace brotli

View File

@ -151,7 +151,7 @@ class BrotliCompressor {
int hash_type_;
size_t input_pos_;
RingBuffer* ringbuffer_;
size_t cmd_buffer_size_;
size_t cmd_alloc_size_;
Command* commands_;
int num_commands_;
int num_literals_;
@ -187,6 +187,7 @@ int BrotliCompressWithCustomDictionary(size_t dictsize, const uint8_t* dict,
BrotliParams params,
BrotliIn* in, BrotliOut* out);
} // namespace brotli
#endif // BROTLI_ENC_ENCODE_H_

View File

@ -675,6 +675,7 @@ struct Hashers {
}
}
H1* hash_h1;
H2* hash_h2;
H3* hash_h3;