add cut-off to arith coder probability update.

This speeds things up for long message, while not damaging
the stats too much for usual-sized cases

Change-Id: I3f45e28b771d701e2e1da11eb800de18c4ed12fc
This commit is contained in:
Pascal Massimino 2011-12-08 06:27:11 -08:00
parent 8666a93aae
commit b361eca1c5
3 changed files with 39 additions and 21 deletions

View File

@ -107,10 +107,12 @@ TCoder* TCoderNew(int max_symbol) {
const int num_nodes = max_symbol + 1; const int num_nodes = max_symbol + 1;
TCoder* c; TCoder* c;
uint8_t* memory; uint8_t* memory;
const int size = sizeof(*c) int size;
+ num_nodes * sizeof(*c->nodes_) if (max_symbol < 0 || max_symbol >= TCODER_MAX_SYMBOL) {
+ num_nodes * sizeof(*c->symbols_); return NULL;
if (max_symbol < 0) return NULL; }
size = sizeof(*c) + num_nodes * sizeof(*c->nodes_)
+ num_nodes * sizeof(*c->symbols_);
memory = (uint8_t*)malloc(size); memory = (uint8_t*)malloc(size);
if (memory == NULL) return NULL; if (memory == NULL) return NULL;
@ -142,7 +144,6 @@ static void ResetTree(TCoder* const c) {
assert(c); assert(c);
c->num_symbols_ = 0; c->num_symbols_ = 0;
c->total_coded_ = 0; c->total_coded_ = 0;
c->probaN_ = HALF_PROBA;
for (pos = 1; pos <= c->num_nodes_; ++pos) { for (pos = 1; pos <= c->num_nodes_; ++pos) {
ResetNode(&c->nodes_[pos], INVALID_SYMBOL); ResetNode(&c->nodes_[pos], INVALID_SYMBOL);
} }
@ -154,7 +155,6 @@ static void ResetSymbolMap(TCoder* const c) {
Symbol_t s; Symbol_t s;
assert(c); assert(c);
c->num_symbols_ = 0; c->num_symbols_ = 0;
c->probaN_ = HALF_PROBA;
for (s = 0; s < c->num_nodes_; ++s) { for (s = 0; s < c->num_nodes_; ++s) {
c->symbols_[s] = INVALID_POS; c->symbols_[s] = INVALID_POS;
} }
@ -253,12 +253,15 @@ static WEBP_INLINE int CalcProba(Count_t num, Count_t total,
static WEBP_INLINE void UpdateNodeProbas(TCoder* const c, int pos) { static WEBP_INLINE void UpdateNodeProbas(TCoder* const c, int pos) {
Node* const node = &c->nodes_[pos]; Node* const node = &c->nodes_[pos];
const Count_t total = TotalCount(node); const Count_t total = TotalCount(node);
node->probaS_ = CalcProba(node->countS_, total, MAX_PROBA, 0); if (total < COUNTER_CUT_OFF)
node->probaS_ = CalcProba(node->countS_, total, MAX_PROBA, 0);
if (!IsLeaf(c, pos)) { if (!IsLeaf(c, pos)) {
const Count_t total_count = node->count_; const Count_t total_count = node->count_;
const Count_t left_count = TotalCount(&c->nodes_[2 * pos]); if (total_count < COUNTER_CUT_OFF) {
node->probaL_ = const Count_t left_count = TotalCount(&c->nodes_[2 * pos]);
MAX_PROBA - CalcProba(left_count, total_count, MAX_PROBA, 0); node->probaL_ =
MAX_PROBA - CalcProba(left_count, total_count, MAX_PROBA, 0);
}
} }
} }
@ -266,31 +269,31 @@ static void UpdateProbas(TCoder* const c, int pos) {
for ( ; pos >= 1; pos >>= 1) { for ( ; pos >= 1; pos >>= 1) {
UpdateNodeProbas(c, pos); UpdateNodeProbas(c, pos);
} }
c->probaN_ = CalcProba(c->num_symbols_, c->total_coded_, HALF_PROBA - 1, 0);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
static void UpdateTree(TCoder* const c, int pos, Count_t incr) { static void UpdateTree(TCoder* const c, int pos) {
Node* node = &c->nodes_[pos]; Node* node = &c->nodes_[pos];
const int is_fresh_new_symbol = (node->countS_ == 0); const int is_fresh_new_symbol = (node->countS_ == 0);
assert(c); assert(c);
assert(pos >= 1 && pos <= c->num_nodes_); assert(pos >= 1 && pos <= c->num_nodes_);
assert(node->symbol_ != INVALID_SYMBOL); assert(node->symbol_ != INVALID_SYMBOL);
if (!c->frozen_ || is_fresh_new_symbol) { if (!(c->frozen_ || node->countS_ >= COUNTER_CUT_OFF) ||
is_fresh_new_symbol) {
const int starting_pos = pos; // save for later const int starting_pos = pos; // save for later
// Update the counters up the tree, possibly exchanging some nodes // Update the counters up the tree, possibly exchanging some nodes
node->countS_ += incr; ++node->countS_;
while (pos > 1) { while (pos > 1) {
Node* const parent = &c->nodes_[pos >> 1]; Node* const parent = &c->nodes_[pos >> 1];
parent->count_ += incr; ++parent->count_;
if (parent->countS_ < node->countS_) { if (parent->countS_ < node->countS_) {
ExchangeSymbol(c, pos); ExchangeSymbol(c, pos);
} }
pos >>= 1; pos >>= 1;
node = parent; node = parent;
} }
c->total_coded_ += incr; ++c->total_coded_;
UpdateProbas(c, starting_pos); // Update the probas along the modified path UpdateProbas(c, starting_pos); // Update the probas along the modified path
} }
} }
@ -339,10 +342,13 @@ void TCoderEncode(TCoder* const c, int s, VP8BitWriter* const bw) {
int pos; int pos;
const int is_new_symbol = (c->symbols_[s] == INVALID_POS); const int is_new_symbol = (c->symbols_[s] == INVALID_POS);
assert(c); assert(c);
assert(s >= 0 && s < c->num_nodes_);
if (!c->fixed_symbols_ && c->num_symbols_ < c->num_nodes_) { if (!c->fixed_symbols_ && c->num_symbols_ < c->num_nodes_) {
if (c->num_symbols_ > 0) { if (c->num_symbols_ > 0) {
if (bw != NULL) { if (bw != NULL) {
VP8PutBit(bw, is_new_symbol, c->probaN_); const int new_symbol_proba =
CalcProba(c->num_symbols_, c->total_coded_, HALF_PROBA - 1, 0);
VP8PutBit(bw, is_new_symbol, new_symbol_proba);
} }
} else { } else {
assert(is_new_symbol); assert(is_new_symbol);
@ -381,7 +387,7 @@ void TCoderEncode(TCoder* const c, int s, VP8BitWriter* const bw) {
assert(parent == pos); assert(parent == pos);
} }
} }
UpdateTree(c, pos, 1); UpdateTree(c, pos);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -396,7 +402,9 @@ int TCoderDecode(TCoder* const c, VP8BitReader* const br) {
// Check if we need to transmit the new symbol's value // Check if we need to transmit the new symbol's value
if (!c->fixed_symbols_ && c->num_symbols_ < c->num_nodes_) { if (!c->fixed_symbols_ && c->num_symbols_ < c->num_nodes_) {
if (c->num_symbols_ > 0) { if (c->num_symbols_ > 0) {
is_new_symbol = VP8GetBit(br, c->probaN_); const int new_symbol_proba =
CalcProba(c->num_symbols_, c->total_coded_, HALF_PROBA - 1, 0);
is_new_symbol = VP8GetBit(br, new_symbol_proba);
} else { } else {
is_new_symbol = 1; is_new_symbol = 1;
} }
@ -404,6 +412,10 @@ int TCoderDecode(TCoder* const c, VP8BitReader* const br) {
// Code either the raw value, or the path downward to its node. // Code either the raw value, or the path downward to its node.
if (is_new_symbol) { if (is_new_symbol) {
s = DecodeSymbol(br, c->num_nodes_); s = DecodeSymbol(br, c->num_nodes_);
if (s >= c->num_nodes_) {
br->eof_ = 1; // will make decoding abort.
return 0;
}
pos = NewNode(c, s); pos = NewNode(c, s);
} else { } else {
pos = 1; pos = 1;
@ -431,7 +443,7 @@ int TCoderDecode(TCoder* const c, VP8BitReader* const br) {
assert(pos == SymbolToNode(c, s)); assert(pos == SymbolToNode(c, s));
} }
assert(pos <= c->num_nodes_); assert(pos <= c->num_nodes_);
UpdateTree(c, pos, 1); UpdateTree(c, pos);
return s; return s;
} }

View File

@ -61,6 +61,8 @@ typedef struct TCoder TCoder;
// Creates a tree-coder capable of coding symbols in // Creates a tree-coder capable of coding symbols in
// the [0, max_symbol] range. Returns NULL in case of memory error. // the [0, max_symbol] range. Returns NULL in case of memory error.
// 'max_symbol' must be in the range [0, TCODER_MAX_SYMBOL)
#define TCODER_MAX_SYMBOL (1 << 24)
TCoder* TCoderNew(int max_symbol); TCoder* TCoderNew(int max_symbol);
// Re-initialize an existing object, make it ready for a new encoding or // Re-initialize an existing object, make it ready for a new encoding or
// decoding cycle. // decoding cycle.

View File

@ -37,6 +37,11 @@ typedef uint32_t Count_t; // TODO(skal): check overflow during coding.
#define MAX_PROBA 255 #define MAX_PROBA 255
#define HALF_PROBA 128 #define HALF_PROBA 128
// Limit the number of tree updates above which we freeze the probabilities.
// Mainly for speed reason.
// TODO(skal): could be a bitstream parameter?
#define COUNTER_CUT_OFF 16383
typedef struct { // ternary node. typedef struct { // ternary node.
Symbol_t symbol_; Symbol_t symbol_;
// Note: theoretically, one of this three field is redundant and could be // Note: theoretically, one of this three field is redundant and could be
@ -54,7 +59,6 @@ struct TCoder {
Count_t total_coded_; // total number of coded symbols Count_t total_coded_; // total number of coded symbols
int frozen_; // if true, frequencies are not updated int frozen_; // if true, frequencies are not updated
int fixed_symbols_; // if true, symbols are not updated int fixed_symbols_; // if true, symbols are not updated
int probaN_; // cached new-symbol probability
// constants: // constants:
int num_nodes_; // max number of symbols or nodes. Constant, > 0. int num_nodes_; // max number of symbols or nodes. Constant, > 0.