From df32877fa5b93affd07a9cef1dc7a7c1b51e4282 Mon Sep 17 00:00:00 2001 From: Amy Wilder <74995093+AmityWilder@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:57:37 -0400 Subject: [PATCH] Name magic numbers + minor cache performance Multiple magic numbers were present in array sizes, so I've given them constants for clarity. Additionally, the inverse square root of 2 was being calculated at runtime when it could easily be a constant itself. I've also made one of the if-statements branchless, making it hopefully easier for the optimizer to speed up that loop. Ideally it could use a call to `popcount`, but I'm not certain what version of C++ this is designed for and the bits header doesn't have portable support for that feature in older versions. I've also swapped the X and Y loops in `dct8x8` to improve cache performance, since the `input` is row-major. Doing this gives the loops better locality since it's just sprinting through the elements in order rather than jumping row by row and then back to start the next column. This will hopefully improve the cache hit-rate. Note that this is simply a speed improvement. The cache I'm referring to is the CPU's cache of the RAM, not the browser's cache of media. --- phash/phash.cpp | 54 ++++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/phash/phash.cpp b/phash/phash.cpp index 4a9305a..b06b407 100644 --- a/phash/phash.cpp +++ b/phash/phash.cpp @@ -13,24 +13,28 @@ const int WIDTH = 32; const int HEIGHT = 32; const int FRAME_SIZE = WIDTH * HEIGHT; +const int UCOUNT = 8; +const int VCOUNT = 8; +const int UVSIZE = UCOUNT * VCOUNT; const float PI = 3.14159265358979323846f; -float cos_table_x[32][8]; -float cos_table_y[32][8]; +const float ISQRT_2 = 0.70710678118654752440f; +float cos_table_x[WIDTH][UCOUNT]; +float cos_table_y[HEIGHT][VCOUNT]; // Compute 2D DCT on 32x32 input and return top-left 8x8 coefficients -void dct8x8(const uint8_t* input, float out[8][8]) { - for (int u = 0; u < 8; ++u) { - for (int v = 0; v < 8; ++v) { +void dct8x8(const uint8_t* input, float out[UCOUNT][VCOUNT]) { + for (int u = 0; u < UCOUNT; ++u) { + for (int v = 0; v < VCOUNT; ++v) { float sum = 0.0f; - for (int x = 0; x < WIDTH; ++x) { - for (int y = 0; y < HEIGHT; ++y) { + for (int y = 0; y < HEIGHT; ++y) { + for (int x = 0; x < WIDTH; ++x) { float pixel = static_cast(input[y * WIDTH + x]); sum += pixel * cos_table_x[x][u] * cos_table_y[y][v]; } } - float cu = (u == 0) ? (1.0f / std::sqrt(2.0f)) : 1.0f; - float cv = (v == 0) ? (1.0f / std::sqrt(2.0f)) : 1.0f; + float cu = (u == 0) ? ISQRT_2 : 1.0f; + float cv = (v == 0) ? ISQRT_2 : 1.0f; out[u][v] = 0.25f * cu * cv * sum; } } @@ -43,17 +47,19 @@ uint64_t compute_phash(const uint8_t* frame) { // Compute average of top-left 8x8 coefficients float total = 0.0f; - for (int u = 0; u < 8; ++u) - for (int v = 0; v < 8; ++v) + for (int u = 0; u < UCOUNT; ++u) { + for (int v = 0; v < VCOUNT; ++v) { total += dct[u][v]; - float avg = total / 64.0f; + } + } + float avg = total / UVSIZE; // Build 64-bit hash uint64_t hash = 0; - for (int u = 0; u < 8; ++u) { - for (int v = 0; v < 8; ++v) { + for (int u = 0; u < UCOUNT; ++u) { + for (int v = 0; v < VCOUNT; ++v) { hash <<= 1; - if (dct[u][v] > avg) hash |= 1; + hash |= dct[u][v] > avg; } } return hash; @@ -90,8 +96,9 @@ int main(int argc, char* argv[]) { // Load all hashes std::vector target_hashes; - for (int i = 2; i < argc; ++i) + for (int i = 2; i < argc; ++i) { target_hashes.push_back(parse_hex64(argv[i])); + } FILE* f = fopen(input_file, "rb"); if (!f) { @@ -100,13 +107,17 @@ int main(int argc, char* argv[]) { } // Precompute cosine tables - for (int x = 0; x < WIDTH; ++x) - for (int u = 0; u < 8; ++u) + for (int x = 0; x < WIDTH; ++x) { + for (int u = 0; u < UCOUNT; ++u) { cos_table_x[x][u] = std::cos((2*x + 1) * u * PI / 64.0f); + } + } - for (int y = 0; y < HEIGHT; ++y) - for (int v = 0; v < 8; ++v) + for (int y = 0; y < HEIGHT; ++y) { + for (int v = 0; v < VCOUNT; ++v) { cos_table_y[y][v] = std::cos((2*y + 1) * v * PI / 64.0f); + } + } uint8_t frame[FRAME_SIZE]; int frame_num = 0; @@ -116,9 +127,10 @@ int main(int argc, char* argv[]) { for (size_t i = 0; i < target_hashes.size(); ++i) { int dist = hamming(hash, target_hashes[i]); - if (dist <= 3 && target_hashes[i] != 0) + if (dist <= 3 && target_hashes[i] != 0) { std::cout << std::hex << std::setw(16) << std::setfill('0') << target_hashes[i] << " " << std::hex << hash << " " << std::dec << dist << "\n"; + } } frame_num++;