Speed-up the calculation in av1_set_ssim_rdmult()
In the parent version, geom_mean_of_scale is calculated
in the function av1_set_ssim_rdmult() using log() and exp().
In this CL, geom_mean_of_scale computation is simplified by
avoiding log() and exp() operations.
For AVIF still-image encode,
Encode Time
cpu-used Reduction(%)
6 0.140
7 0.353
8 0.131
9 0.372
STATS_CHANGED possible, but not seen
Change-Id: I935e9a5fd14d43092887e971094669f59047c2d4
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c
index 80b0f3c..3b8a8c0 100644
--- a/av1/encoder/encodeframe_utils.c
+++ b/av1/encoder/encodeframe_utils.c
@@ -31,8 +31,19 @@
const int num_brows = (mi_size_high[bsize] + num_mi_h - 1) / num_mi_h;
int row, col;
double num_of_mi = 0.0;
- double geom_mean_of_scale = 0.0;
+ double geom_mean_of_scale = 1.0;
+ // To avoid overflow of 'geom_mean_of_scale', bsize_base must be at least
+ // BLOCK_8X8.
+ //
+ // For bsize=BLOCK_128X128 and bsize_base=BLOCK_8X8, the loop below would
+ // iterate 256 times. Considering the maximum value of
+ // cpi->ssim_rdmult_scaling_factors (see av1_set_mb_ssim_rdmult_scaling()),
+ // geom_mean_of_scale can go up to 4.8323^256, which is within DBL_MAX
+ // (maximum value a double data type can hold). If bsize_base is modified to
+ // BLOCK_4X4 (minimum possible block size), geom_mean_of_scale can go up
+ // to 4.8323^1024 and exceed DBL_MAX, resulting in data overflow.
+ assert(bsize_base >= BLOCK_8X8);
assert(cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM);
for (row = mi_row / num_mi_w;
@@ -41,11 +52,11 @@
col < num_cols && col < mi_col / num_mi_h + num_bcols; ++col) {
const int index = row * num_cols + col;
assert(cpi->ssim_rdmult_scaling_factors[index] != 0.0);
- geom_mean_of_scale += log(cpi->ssim_rdmult_scaling_factors[index]);
+ geom_mean_of_scale *= cpi->ssim_rdmult_scaling_factors[index];
num_of_mi += 1.0;
}
}
- geom_mean_of_scale = exp(geom_mean_of_scale / num_of_mi);
+ geom_mean_of_scale = pow(geom_mean_of_scale, (1.0 / num_of_mi));
*rdmult = (int)((double)(*rdmult) * geom_mean_of_scale + 0.5);
*rdmult = AOMMAX(*rdmult, 0);
diff --git a/av1/encoder/encoder_utils.c b/av1/encoder/encoder_utils.c
index 3181b78..abbd95f 100644
--- a/av1/encoder/encoder_utils.c
+++ b/av1/encoder/encoder_utils.c
@@ -1328,10 +1328,22 @@
// Curve fitting with an exponential model on all 16x16 blocks from the
// midres dataset.
var = 67.035434 * (1 - exp(-0.0021489 * var)) + 17.492222;
+
+ // As per the above computation, var will be in the range of
+ // [17.492222, 84.527656], assuming the data type is of infinite
+ // precision. The following assert conservatively checks if var is in the
+ // range of [17.0, 85.0] to avoid any issues due to the precision of the
+ // relevant data type.
+ assert(var > 17.0 && var < 85.0);
cpi->ssim_rdmult_scaling_factors[index] = var;
log_sum += log(var);
}
}
+
+ // As log_sum holds the geometric mean, it will be in the range
+ // [17.492222, 84.527656]. Hence, in the below loop, the value of
+ // cpi->ssim_rdmult_scaling_factors[index] would be in the range
+ // [0.2069, 4.8323].
log_sum = exp(log_sum / (double)(num_rows * num_cols));
for (int row = 0; row < num_rows; ++row) {