Untitled

 avatar
unknown
plain_text
9 months ago
3.3 kB
12
Indexable
/*
 * ===========================================================================
 * OPTIMISATION 3: LOOP UNROLLING & REGISTER CACHING
 * ===========================================================================
 * This code reverts to baseline and implements the third strategy:
 * - Kernel Caching: Loads kernel values into local variables to avoid
 * repeated memory fetches inside the loops[cite: 42, 43].
 * - Loop Unrolling: The inner x-loop is unrolled by a factor of 2,
 * processing two output pixels per iteration to reduce loop overhead
 * and increase instruction-level parallelism[cite: 44, 46].
 * - NOTE: Retains original 'double' math and boundary checks to isolate
 * the effect of these specific techniques.
 * ===========================================================================
 */
void conv2d_optimised(
    int input[IMAGE_SIZE][IMAGE_SIZE],
    int output[IMAGE_SIZE][IMAGE_SIZE],
    int kernel[KERNEL_SIZE][KERNEL_SIZE]
) {
    int offset = KERNEL_SIZE / 2;

    // Kernel Caching: Load kernel into local variables (simulating registers).
    // This example is hardcoded for a 3x3 kernel as discussed in the report[cite: 41, 42].
    // Note: This path would ideally be taken only if KERNEL_SIZE == 3.
    const int k00 = kernel[0][0]; const int k01 = kernel[0][1]; const int k02 = kernel[0][2];
    const int k10 = kernel[1][0]; const int k11 = kernel[1][1]; const int k12 = kernel[1][2];
    const int k20 = kernel[2][0]; const int k21 = kernel[2][1]; const int k22 = kernel[2][2];

    for (int y = 0; y < IMAGE_SIZE; y++) {
        // Loop Unrolling: process two pixels per iteration (x and x+1).
        for (int x = 0; x < IMAGE_SIZE; x += 2) {
            // Accumulators for the two pixels being calculated.
            double sum1 = 0.0;
            double sum2 = 0.0;

            // Manual 3x3 kernel loop for both pixels.
            for (int ky = 0; ky < 3; ky++) {
                int img_y = y + ky - offset;
                // Check row validity once.
                if (img_y >= 0 && img_y < IMAGE_SIZE) {
                    for (int kx = 0; kx < 3; kx++) {
                        int img_x1 = x + kx - offset;
                        int img_x2 = (x + 1) + kx - offset;
                        int k_val = kernel[ky][kx];

                        // Process pixel 1 (output[y][x])
                        if (img_x1 >= 0 && img_x1 < IMAGE_SIZE) {
                            sum1 += (double)input[img_y][img_x1] * k_val;
                        }

                        // Process pixel 2 (output[y][x+1])
                        if ((x + 1) < IMAGE_SIZE && img_x2 >= 0 && img_x2 < IMAGE_SIZE) {
                           sum2 += (double)input[img_y][img_x2] * k_val;
                        }
                    }
                }
            }

            // Store result for the first pixel.
            short temp1 = (short)sum1;
            int final_val1 = (int)temp1;
            output[y][x] = (final_val1 > 0) ? final_val1 : 0;

            // Store result for the second pixel, ensuring we don't write out of bounds.
            if ((x + 1) < IMAGE_SIZE) {
                short temp2 = (short)sum2;
                int final_val2 = (int)temp2;
                output[y][x+1] = (final_val2 > 0) ? final_val2 : 0;
            }
        }
    }
}
Editor is loading...
Leave a Comment