It is unnecessary, as we already have the entries sorted by probability and therefore implicitly by length. All we need on top of that to build the tree is the number of entries of a given length. Doing so gives a 3.6% speedup of ff_mjpeg_encode_huffman_close() here; it also saves about 640B of .text here. The new code puts values with higher probability to the left of the tree. The old code did not and therefore the FATE checksums needed to be updated. Due to MJPEG's 0xFF unescaping file sizes as well as file checksums needed to be updated; the decoded picture hashes stayed the same. Given that codes on the left of the tree have on average fewer bits set than codes on the right, the file sizes mostly improve (all except vsynth3-mjpeg-444).
Showing the changes to libavcodec/tests (1 of 22 files). Show all
Unified · Split
| @@ -34,10 +34,9 @@ · 33 unmodified lines | ||
| 34 | 34 | static int check_lengths(int L, const int *probs, int nprobs, |
| 35 | 35 | int expected_length, const uint8_t expected_len_counts[/* L + 1 */]) |
| 36 | 36 | { |
| 37 | − HuffTable lengths[256]; | |
| 38 | 37 | PTable val_counts[256]; |
| 39 | − uint8_t len_counts[17] = { 0 }; | |
| 40 | − int actual_length = 0, i, j, k, prob, length; | |
| 38 | + uint8_t len_counts[17]; | |
| 39 | + int actual_length = 0, i; | |
| 41 | 40 | int ret = 0; |
| 42 | 41 | av_assert0(nprobs <= 256); |
| 43 | 42 | av_assert0(L < FF_ARRAY_ELEMS(len_counts)); |
| @@ -46,20 +45,12 @@ static int check_lengths(int L, const int *probs, int nprobs, · 2 unmodified lines | ||
| 46 | 45 | val_counts[i] = (PTable){.value = i, .prob = probs[i]}; |
| 47 | 46 | } |
| 48 | 47 | |
| 49 | − mjpegenc_huffman_compute_bits(val_counts, lengths, nprobs, L); | |
| 48 | + mjpegenc_huffman_compute_bits(val_counts, len_counts, nprobs, L); | |
| 50 | 49 | |
| 51 | − for (int i = 0; i < nprobs; ++i) { | |
| 52 | − if (lengths[i].length > L) { | |
| 53 | − fprintf(stderr, | |
| 54 | − "Len %d of element %d of Huffman table with %d elements exceeds max length %d\n", | |
| 55 | − lengths[i].length, i, nprobs, L); | |
| 56 | − return 1; | |
| 57 | − } | |
| 58 | − len_counts[lengths[i].length]++; | |
| 59 | − } | |
| 60 | 50 | // Test that the lengths can be made part of a complete, prefix-free tree: |
| 61 | − unsigned code = 0; | |
| 51 | + unsigned code = 0, count = 0; | |
| 62 | 52 | for (int i = 1; i <= L; ++i) { |
| 53 | + count += len_counts[i]; | |
| 63 | 54 | code <<= 1; |
| 64 | 55 | code += len_counts[i]; |
| 65 | 56 | } |
| @@ -67,22 +58,36 @@ static int check_lengths(int L, const int *probs, int nprobs, · 1 unmodified line | ||
| 67 | 58 | fprintf(stderr, "Huffman tree overdetermined/invalid\n"); |
| 68 | 59 | ret = 1; |
| 69 | 60 | } |
| 70 | − | |
| 71 | − for (i = 0; i < nprobs; i++) { | |
| 72 | − // Find the value's prob and length | |
| 73 | − for (j = 0; j < nprobs; j++) | |
| 74 | − if (val_counts[j].value == i) break; | |
| 75 | − for (k = 0; k < nprobs; k++) | |
| 76 | − if (lengths[k].code == i) break; | |
| 77 | − if (!(j < nprobs && k < nprobs)) return 1; | |
| 78 | − prob = val_counts[j].prob; | |
| 79 | − length = lengths[k].length; | |
| 80 | − | |
| 81 | − if (prob) { | |
| 82 | − actual_length += prob * length; | |
| 61 | + if (count != nprobs) { | |
| 62 | + fprintf(stderr, "Total count %u does not match expected value %d\n", | |
| 63 | + count, nprobs); | |
| 64 | + ret = 1; | |
| 65 | + } | |
| 66 | + // Test that the input values have been properly ordered. | |
| 67 | + for (unsigned i = 0; i < count; ++i) { | |
| 68 | + if (val_counts[i].prob != probs[val_counts[i].value]) { | |
| 69 | + fprintf(stderr, "PTable not properly reordered\n"); | |
| 70 | + ret = 1; | |
| 71 | + } | |
| 72 | + if (i && val_counts[i - 1].prob > val_counts[i].prob) { | |
| 73 | + fprintf(stderr, "PTable not order ascendingly: [%u] = %d > [%u] = %d\n", | |
| 74 | + i - 1, val_counts[i - 1].prob, i, val_counts[i].prob); | |
| 75 | + ret = 1; | |
| 76 | + } | |
| 77 | + unsigned j; | |
| 78 | + for (j = 0; j < count; ++j) | |
| 79 | + if (val_counts[j].value == i) | |
| 80 | + break; | |
| 81 | + if (j >= count) { | |
| 82 | + fprintf(stderr, "Element %u missing after sorting\n", i); | |
| 83 | + ret = 1; | |
| 83 | 84 | } |
| 84 | − | |
| 85 | − if (length > L || length < 1) return 1; | |
| 85 | + } | |
| 86 | + for (int len = L, j = 0; len; --len) { | |
| 87 | + int prob = 0; | |
| 88 | + for (int end = j + len_counts[len]; j < end; ++j) | |
| 89 | + prob += val_counts[j].prob; | |
| 90 | + actual_length += prob * len; | |
| 86 | 91 | } |
| 87 | 92 | // Check that the total length is optimal |
| 88 | 93 | if (actual_length != expected_length) ret = 1; |
| @@ -141,7 +146,10 @@ static const uint8_t len_counts_sat[] = { · 52 unmodified lines | ||
| 141 | 146 | // http://guru.multimedia.cx/small-tasks-for-ffmpeg/ |
| 142 | 147 | int main(int argc, char **argv) |
| 143 | 148 | { |
| 144 | − int i, ret = 0; | |
| 149 | + enum { | |
| 150 | + MAX_LEN = 3, | |
| 151 | + }; | |
| 152 | + int ret = 0; | |
| 145 | 153 | // Probabilities of symbols 0..4 |
| 146 | 154 | PTable val_counts[] = { |
| 147 | 155 | {.value = 0, .prob = 1}, |
| @@ -151,30 +159,33 @@ int main(int argc, char **argv) · 3 unmodified lines | ||
| 151 | 159 | {.value = 4, .prob = 21}, |
| 152 | 160 | }; |
| 153 | 161 | // Expected code lengths for each symbol |
| 154 | − static const HuffTable expected[] = { | |
| 155 | − {.code = 0, .length = 3}, | |
| 156 | − {.code = 1, .length = 3}, | |
| 157 | − {.code = 2, .length = 3}, | |
| 158 | − {.code = 3, .length = 3}, | |
| 159 | − {.code = 4, .length = 1}, | |
| 162 | + static const uint8_t expected[MAX_LEN + 1] = { | |
| 163 | + [1] = 1, [3] = 4, | |
| 160 | 164 | }; |
| 161 | 165 | // Actual code lengths |
| 162 | − HuffTable distincts[5]; | |
| 166 | + uint8_t len_counts[MAX_LEN + 1]; | |
| 163 | 167 | |
| 164 | 168 | // Build optimal huffman tree using an internal function, to allow for |
| 165 | 169 | // smaller-than-normal test cases. This mutates val_counts by sorting. |
| 166 | − mjpegenc_huffman_compute_bits(val_counts, distincts, | |
| 167 | − FF_ARRAY_ELEMS(distincts), 3); | |
| 170 | + mjpegenc_huffman_compute_bits(val_counts, len_counts, | |
| 171 | + FF_ARRAY_ELEMS(val_counts), MAX_LEN); | |
| 168 | 172 | |
| 169 | − for (i = 0; i < FF_ARRAY_ELEMS(distincts); i++) { | |
| 170 | − if (distincts[i].code != expected[i].code || | |
| 171 | − distincts[i].length != expected[i].length) { | |
| 173 | + for (unsigned i = 1; i < FF_ARRAY_ELEMS(len_counts); i++) { | |
| 174 | + if (len_counts[i] != expected[i]) { | |
| 172 | 175 | fprintf(stderr, |
| 173 | 176 | "Built huffman does not equal expectations. " |
| 174 | − "Expected: code %d probability %d, " | |
| 175 | − "Actual: code %d probability %d\n", | |
| 176 | − expected[i].code, expected[i].length, | |
| 177 | − distincts[i].code, distincts[i].length); | |
| 177 | + "Expected: %d codes of length %u, " | |
| 178 | + "Actual: %d codes of length %u\n", | |
| 179 | + (int)expected[i], i, | |
| 180 | + (int)len_counts[i], i); | |
| 181 | + ret = 1; | |
| 182 | + } | |
| 183 | + } | |
| 184 | + for (unsigned i = 1; i < FF_ARRAY_ELEMS(val_counts); ++i) { | |
| 185 | + if (val_counts[i - 1].prob > val_counts[i].prob) { | |
| 186 | + fprintf(stderr, "Probability table not ordered ascendingly. " | |
| 187 | + "val_counts[%u] == %d, val_counts[%u] == %d\n", | |
| 188 | + i - 1, val_counts[i - 1].prob, i, val_counts[i].prob); | |
| 178 | 189 | ret = 1; |
| 179 | 190 | } |
| 180 | 191 | } |