Merge pull request #97 from szabadka/master

Faster encoding for low quality settings.
This commit is contained in:
szabadka 2015-05-07 17:36:37 +02:00
commit fa2c6df4f5
3 changed files with 83 additions and 66 deletions

View File

@ -118,7 +118,7 @@ void CreateBackwardReferences(size_t num_bytes,
int delayed_backward_references_in_row = 0;
for (;;) {
--max_length;
int best_len_2 = quality < 4 ? std::min(best_len - 1, max_length) : 0;
int best_len_2 = quality < 5 ? std::min(best_len - 1, max_length) : 0;
int best_len_code_2 = 0;
int best_dist_2 = 0;
double best_score_2 = min_score;
@ -182,7 +182,7 @@ void CreateBackwardReferences(size_t num_bytes,
distance_code = 3;
} else if (best_dist == dist_cache[3]) {
distance_code = 4;
} else if (quality > 1 && best_dist >= 6) {
} else if (quality > 3 && best_dist >= 6) {
for (int k = 4; k < kNumDistanceShortCodes; ++k) {
int idx = kDistanceCacheIndex[k];
int candidate = dist_cache[idx] + kDistanceCacheOffset[k];
@ -356,19 +356,33 @@ void CreateBackwardReferences(size_t num_bytes,
commands, num_commands, num_literals);
break;
case 8:
CreateBackwardReferences<Hashers::H8, true, true>(
CreateBackwardReferences<Hashers::H8, false, false>(
num_bytes, position, ringbuffer, ringbuffer_mask,
literal_cost, literal_cost_mask, max_backward_limit, base_min_score,
quality, hashers->hash_h8.get(), dist_cache, last_insert_len,
commands, num_commands, num_literals);
break;
case 9:
CreateBackwardReferences<Hashers::H9, true, false>(
CreateBackwardReferences<Hashers::H9, false, false>(
num_bytes, position, ringbuffer, ringbuffer_mask,
literal_cost, literal_cost_mask, max_backward_limit, base_min_score,
quality, hashers->hash_h9.get(), dist_cache, last_insert_len,
commands, num_commands, num_literals);
break;
case 10:
CreateBackwardReferences<Hashers::H11Text, true, true>(
num_bytes, position, ringbuffer, ringbuffer_mask,
literal_cost, literal_cost_mask, max_backward_limit, base_min_score,
quality, hashers->hash_h11_text.get(), dist_cache, last_insert_len,
commands, num_commands, num_literals);
break;
case 11:
CreateBackwardReferences<Hashers::H11Font, true, false>(
num_bytes, position, ringbuffer, ringbuffer_mask,
literal_cost, literal_cost_mask, max_backward_limit, base_min_score,
quality, hashers->hash_h11_font.get(), dist_cache, last_insert_len,
commands, num_commands, num_literals);
break;
default:
break;
}

View File

@ -38,6 +38,9 @@
namespace brotli {
static const double kMinUTF8Ratio = 0.75;
static const int kMinQualityForBlockSplit = 4;
static const int kMinQualityForContextModeling = 5;
static const int kMinQualityForOptimizeHistograms = 4;
int ParseAsUTF8(int* symbol, const uint8_t* input, int size) {
// ASCII
@ -149,7 +152,7 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
params_.lgwin = kMaxWindowBits;
}
if (params_.lgblock == 0) {
params_.lgblock = params_.quality == 1 ? 14 : 16;
params_.lgblock = params_.quality < kMinQualityForBlockSplit ? 14 : 16;
if (params_.quality >= 9 && params_.lgwin > params_.lgblock) {
params_.lgblock = std::min(21, params_.lgwin);
}
@ -199,19 +202,10 @@ BrotliCompressor::BrotliCompressor(BrotliParams params)
dist_cache_[3] = 16;
// Initialize hashers.
switch (params_.quality) {
case 0:
case 1: hash_type_ = 1; break;
case 2:
case 3: hash_type_ = 2; break;
case 4: hash_type_ = 3; break;
case 5:
case 6: hash_type_ = 4; break;
case 7: hash_type_ = 5; break;
case 8: hash_type_ = 6; break;
case 9: hash_type_ = 7; break;
default: // quality > 9
hash_type_ = (params_.mode == BrotliParams::MODE_TEXT) ? 8 : 9;
if (params_.quality <= 9) {
hash_type_ = params_.quality;
} else {
hash_type_ = (params_.mode == BrotliParams::MODE_TEXT) ? 10 : 11;
}
hashers_->Init(hash_type_);
if (params_.mode == BrotliParams::MODE_TEXT &&
@ -330,7 +324,7 @@ bool BrotliCompressor::WriteBrotliData(const bool is_last,
// literals and commands.
static const int kMaxNumDelayedSymbols = 0x2fff;
if (!is_last && !force_flush &&
(params_.quality > 1 ||
(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_ + mask + 1) {
@ -359,7 +353,7 @@ void DecideOverLiteralContextModeling(const uint8_t* input,
int* literal_context_mode,
int* num_literal_contexts,
const int** literal_context_map) {
if (quality <= 3 || length < 64) {
if (quality < kMinQualityForContextModeling || length < 64) {
return;
}
// Simple heuristics to guess if the data is UTF8 or not. The goal is to
@ -453,7 +447,7 @@ bool BrotliCompressor::WriteMetaBlockInternal(const bool is_last,
num_direct_distance_codes,
distance_postfix_bits);
}
if (params_.quality == 1) {
if (params_.quality < kMinQualityForBlockSplit) {
if (!StoreMetaBlockTrivial(data, last_flush_pos_, bytes, mask, is_last,
commands_.get(), num_commands_,
&storage_ix,
@ -492,7 +486,7 @@ bool BrotliCompressor::WriteMetaBlockInternal(const bool is_last,
params_.enable_context_modeling,
&mb);
}
if (params_.quality >= 3) {
if (params_.quality >= kMinQualityForOptimizeHistograms) {
OptimizeHistograms(num_direct_distance_codes,
distance_postfix_bits,
&mb);

View File

@ -107,7 +107,7 @@ inline double BackwardReferenceScoreUsingLastDistance(double average_cost,
//
// This is a hash map of fixed size (kBucketSize). Starting from the
// given index, kBucketSweep buckets are used to store values of a key.
template <int kBucketBits, int kBucketSweep>
template <int kBucketBits, int kBucketSweep, bool kUseDictionary>
class HashLongestMatchQuickly {
public:
HashLongestMatchQuickly() {
@ -216,8 +216,6 @@ class HashLongestMatchQuickly {
*best_distance_out = backward;
*best_score_out = BackwardReferenceScore(average_cost, len, backward);
return true;
} else {
return false;
}
} else {
uint32_t *bucket = buckets_ + key;
@ -250,38 +248,39 @@ class HashLongestMatchQuickly {
}
}
}
if (!match_found && num_dict_matches_ >= (num_dict_lookups_ >> 7)) {
++num_dict_lookups_;
const uint32_t key = Hash<14, 4>(&ring_buffer[cur_ix_masked]) << 1;
const uint16_t v = kStaticDictionaryHash[key];
if (v > 0) {
const int len = v & 31;
const int dist = v >> 5;
const int offset = kBrotliDictionaryOffsetsByLength[len] + len * dist;
if (len <= max_length) {
const int matchlen =
FindMatchLengthWithLimit(&ring_buffer[cur_ix_masked],
&kBrotliDictionary[offset], len);
if (matchlen == len) {
const size_t backward = max_backward + dist + 1;
const double score = BackwardReferenceScore(average_cost,
len, backward);
if (best_score < score) {
++num_dict_matches_;
best_score = score;
best_len = len;
*best_len_out = best_len;
*best_len_code_out = best_len;
*best_distance_out = backward;
*best_score_out = best_score;
return true;
}
}
if (kUseDictionary && !match_found &&
num_dict_matches_ >= (num_dict_lookups_ >> 7)) {
++num_dict_lookups_;
const uint32_t key = Hash<14, 4>(&ring_buffer[cur_ix_masked]) << 1;
const uint16_t v = kStaticDictionaryHash[key];
if (v > 0) {
const int len = v & 31;
const int dist = v >> 5;
const int offset = kBrotliDictionaryOffsetsByLength[len] + len * dist;
if (len <= max_length) {
const int matchlen =
FindMatchLengthWithLimit(&ring_buffer[cur_ix_masked],
&kBrotliDictionary[offset], len);
if (matchlen == len) {
const size_t backward = max_backward + dist + 1;
const double score = BackwardReferenceScore(average_cost,
len, backward);
if (best_score < score) {
++num_dict_matches_;
best_score = score;
best_len = len;
*best_len_out = best_len;
*best_len_code_out = best_len;
*best_distance_out = backward;
*best_score_out = best_score;
return true;
}
}
}
}
return match_found;
}
return match_found;
}
private:
@ -363,19 +362,19 @@ class HashLongestMatch {
double start_cost_diff4 = 0.0;
double start_cost_diff3 = 0.0;
double start_cost_diff2 = 0.0;
if (kUseCostModel) {
start_cost_diff4 = literal_cost == NULL ? 0 :
if (kUseCostModel && literal_cost != NULL) {
start_cost_diff4 =
literal_cost[cur_ix & literal_cost_mask] +
literal_cost[(cur_ix + 1) & literal_cost_mask] +
literal_cost[(cur_ix + 2) & literal_cost_mask] +
literal_cost[(cur_ix + 3) & literal_cost_mask] -
4 * average_cost;
start_cost_diff3 = literal_cost == NULL ? 0 :
start_cost_diff3 =
literal_cost[cur_ix & literal_cost_mask] +
literal_cost[(cur_ix + 1) & literal_cost_mask] +
literal_cost[(cur_ix + 2) & literal_cost_mask] -
3 * average_cost + 0.3;
start_cost_diff2 = literal_cost == NULL ? 0 :
start_cost_diff2 =
literal_cost[cur_ix & literal_cost_mask] +
literal_cost[(cur_ix + 1) & literal_cost_mask] -
2 * average_cost + 1.2;
@ -457,6 +456,7 @@ class HashLongestMatch {
*best_len_code_out = best_len;
*best_distance_out = backward;
match_found = true;
break; // The score can never get better since backward increases.
}
}
}
@ -589,15 +589,20 @@ class HashLongestMatch {
};
struct Hashers {
typedef HashLongestMatchQuickly<16, 1> H1;
typedef HashLongestMatchQuickly<17, 4> H2;
typedef HashLongestMatch<14, 4, 4, 4, false, false> H3;
typedef HashLongestMatch<14, 5, 4, 4, false, false> H4;
typedef HashLongestMatch<15, 6, 4, 10, false, false> H5;
typedef HashLongestMatch<15, 7, 4, 10, false, false> H6;
typedef HashLongestMatch<15, 8, 4, 16, false, false> H7;
typedef HashLongestMatch<15, 8, 4, 16, true, true> H8;
typedef HashLongestMatch<15, 8, 2, 16, true, false> H9;
// For kBucketSweep == 1, enabling the dictionary lookup makes compression
// a little faster (0.5% - 1%) and it compresses 0.15% better on small text
// and html inputs.
typedef HashLongestMatchQuickly<16, 1, true> H1;
typedef HashLongestMatchQuickly<16, 2, false> H2;
typedef HashLongestMatchQuickly<16, 4, false> H3;
typedef HashLongestMatchQuickly<17, 4, true> H4;
typedef HashLongestMatch<14, 4, 4, 4, false, false> H5;
typedef HashLongestMatch<14, 5, 4, 4, false, false> H6;
typedef HashLongestMatch<15, 6, 4, 10, false, false> H7;
typedef HashLongestMatch<15, 7, 4, 10, false, false> H8;
typedef HashLongestMatch<15, 8, 4, 16, false, false> H9;
typedef HashLongestMatch<15, 8, 4, 16, true, true> H11Text;
typedef HashLongestMatch<15, 8, 2, 16, true, false> H11Font;
void Init(int type) {
switch (type) {
@ -610,12 +615,14 @@ struct Hashers {
case 7: hash_h7.reset(new H7); break;
case 8: hash_h8.reset(new H8); break;
case 9: hash_h9.reset(new H9); break;
case 10: hash_h11_text.reset(new H11Text); break;
case 11: hash_h11_font.reset(new H11Font); break;
default: break;
}
}
void SetStaticDictionary(const StaticDictionary *dict) {
if (hash_h8.get() != NULL) hash_h8->SetStaticDictionary(dict);
if (hash_h11_text.get() != NULL) hash_h11_text->SetStaticDictionary(dict);
}
std::unique_ptr<H1> hash_h1;
@ -627,6 +634,8 @@ struct Hashers {
std::unique_ptr<H7> hash_h7;
std::unique_ptr<H8> hash_h8;
std::unique_ptr<H9> hash_h9;
std::unique_ptr<H11Text> hash_h11_text;
std::unique_ptr<H11Font> hash_h11_font;
};
} // namespace brotli