5-10% encoding speedup with faster trellis (-m 6)
mostly by:
- storing a single rd-score instead of cost / distortion separately
- evaluating terminal cost only once
- getting some invariants out of the loops
- more consts behind fewer variables
Change-Id: I79451f3fd1143d6537200fb8b90d0ba252809f8c
diff --git a/src/enc/quant.c b/src/enc/quant.c
index c975cee..8922b38 100644
--- a/src/enc/quant.c
+++ b/src/enc/quant.c
@@ -517,11 +517,10 @@
// Trellis
typedef struct {
- int prev; // best previous
- int level; // level
- int sign; // sign of coeff_i
- score_t cost; // bit cost
- score_t error; // distortion = sum of (|coeff_i| - level_i * Q_i)^2
+ int prev; // best previous node
+ int level; // level
+ int sign; // sign of coeff_i
+ score_t score; // partial RD score
const uint16_t* costs; // shortcut to cost tables
} Node;
@@ -582,8 +581,8 @@
// initialize source node.
n = first - 1;
for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
- NODE(n, m).cost = (ctx0 == 0) ? VP8BitCost(1, last_proba) : 0;
- NODE(n, m).error = max_error;
+ const score_t rate = (ctx0 == 0) ? VP8BitCost(1, last_proba) : 0;
+ NODE(n, m).score = RDScoreTrellis(lambda, rate, max_error);
NODE(n, m).costs = costs[VP8EncBands[first]][ctx0];
}
}
@@ -604,63 +603,59 @@
// test all alternate level values around level0.
for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
Node* const cur = &NODE(n, m);
- int delta_error, new_error;
- score_t cur_score = MAX_COST;
int level = level0 + m;
const int ctx = (level > 2) ? 2 : level;
const int band = VP8EncBands[n + 1];
- int last_pos_cost; // extra cost if last coeff's position is < 15
+ score_t base_score, last_pos_cost;
+ cur->score = MAX_COST;
+ if (level > MAX_LEVEL || level < 0) { // node is dead?
+ continue;
+ }
cur->sign = sign;
cur->level = level;
cur->costs = costs[band][ctx];
- if (level > MAX_LEVEL || level < 0) { // node is dead?
- cur->cost = MAX_COST;
- continue;
- }
- last_pos_cost = (n < 15) ? VP8BitCost(0, probas[band][ctx][0])
- : 0;
- // Compute delta_error = how much coding this level will
- // subtract as distortion to max_error
- new_error = coeff0 - level * Q;
- delta_error =
- kWeightTrellis[j] * (coeff0 * coeff0 - new_error * new_error);
+ // Compute extra rate cost if last coeff's position is < 15
+ last_pos_cost = (n < 15) ? VP8BitCost(0, probas[band][ctx][0]) : 0;
+
+ {
+ // Compute delta_error = how much coding this level will
+ // subtract to max_error as distortion.
+ // Here, distortion = sum of (|coeff_i| - level_i * Q_i)^2
+ const int new_error = coeff0 - level * Q;
+ const int delta_error =
+ kWeightTrellis[j] * (new_error * new_error - coeff0 * coeff0);
+ base_score = RDScoreTrellis(lambda, 0, delta_error);
+ }
// Inspect all possible non-dead predecessors. Retain only the best one.
for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) {
const Node* const prev = &NODE(n - 1, p);
- const uint16_t* const tcost = prev->costs;
- const score_t total_error = prev->error - delta_error;
- score_t cost, score;
-
- if (prev->cost >= MAX_COST) { // dead node?
- continue;
- }
-
- // Base cost of both terminal/non-terminal
- cost = prev->cost + VP8LevelCost(tcost, level);
-
- // Examine node assuming it's a non-terminal one.
- score = RDScoreTrellis(lambda, cost, total_error);
- if (score < cur_score) {
- cur_score = score;
- cur->cost = cost;
- cur->error = total_error;
- cur->prev = p;
- }
-
- // Now, record best terminal node (and thus best entry in the graph).
- if (level) {
- score = RDScoreTrellis(lambda, cost + last_pos_cost, total_error);
- if (score < best_score) {
- best_score = score;
- best_path[0] = n; // best eob position
- best_path[1] = m; // best level
- best_path[2] = p; // best predecessor
+ if (prev->score < MAX_COST) { // skip dead node
+ // Base cost of both terminal / non-terminal hypothesis
+ const score_t cost = VP8LevelCost(prev->costs, level);
+ // Examine node assuming it's a non-terminal one.
+ score_t score =
+ base_score + prev->score + RDScoreTrellis(lambda, cost, 0);
+ if (score < cur->score) {
+ cur->score = score;
+ cur->prev = p;
}
}
}
+ // Now, record best terminal node (and thus best entry in the graph).
+ if (cur->level != 0) {
+ const score_t last_pos_score =
+ RDScoreTrellis(lambda, last_pos_cost, 0);
+ const score_t score = cur->score + last_pos_score;
+ if (score < best_score) {
+ best_score = score;
+ best_path[0] = n; // best eob position
+ best_path[1] = cur->level - level0; // best node index ('m')
+ best_path[2] = cur->prev; // best predecessor
+ }
+ }
}
}