Untitled

 avatar
unknown
plain_text
9 months ago
3.7 kB
15
Indexable
#include "conv2d.h"

// Define constants for the padded image dimensions.
#define PADDING (KERNEL_SIZE / 2)
#define PADDED_SIZE (IMAGE_SIZE + 2 * PADDING)

/*
 * OPTIMIZE THIS FUNCTION
 */
void conv2d_optimised(
    int input[IMAGE_SIZE][IMAGE_SIZE],
    int output[IMAGE_SIZE][IMAGE_SIZE],
    int kernel[KERNEL_SIZE][KERNEL_SIZE]
) {
    // A static array avoids stack overflow and is auto-initialized to zero.
    static int padded_input[PADDED_SIZE][PADDED_SIZE];

    // ==================================================================
    // OPTIMIZATION: PADDING LOOP UNROLLED 8x WITH POINTERS
    // ==================================================================
    for (int y = 0; y < IMAGE_SIZE; y++) {
        // Pointers to the start of the source and destination rows.
        int *p_in = &input[y][0];
        int *p_pad = &padded_input[y + PADDING][PADDING];
        int x = 0;
        
        // Process 8 elements at a time using pointers.
        for (; x <= IMAGE_SIZE - 8; x += 8) {
            p_pad[x] = p_in[x];
            p_pad[x + 1] = p_in[x + 1];
            p_pad[x + 2] = p_in[x + 2];
            p_pad[x + 3] = p_in[x + 3];
            p_pad[x + 4] = p_in[x + 4];
            p_pad[x + 5] = p_in[x + 5];
            p_pad[x + 6] = p_in[x + 6];
            p_pad[x + 7] = p_in[x + 7];
        }
        // Cleanup for remaining elements.
        for (; x < IMAGE_SIZE; x++) {
            p_pad[x] = p_in[x];
        }
    }

#if KERNEL_SIZE == 3
    // Kernel Caching: Load kernel into local variables.
    const int k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2];
    const int k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2];
    const int k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2];

    for (int y = 0; y < IMAGE_SIZE; y++) {
        // Pointer Sliding for the three input rows.
        int *p_row0 = &padded_input[y][0];
        int *p_row1 = &padded_input[y + 1][0];
        int *p_row2 = &padded_input[y + 2][0];
        
        // ==================================================================
        // OPTIMIZATION: X-LOOP UNROLLED 2x WITH MULTIPLE SUMS (PIPELINING)
        // ==================================================================
        for (int x = 0; x < IMAGE_SIZE; x += 2) {
            // Independent accumulators to reduce data dependency and enable pipelining.
            int sum1 = 0;
            int sum2 = 0;

            // --- Unrolled Kernel Calculation for Pixel 1 (x) ---
            sum1 += p_row0[x] * k00 + p_row0[x + 1] * k01 + p_row0[x + 2] * k02;
            sum1 += p_row1[x] * k10 + p_row1[x + 1] * k11 + p_row1[x + 2] * k12;
            sum1 += p_row2[x] * k20 + p_row2[x + 1] * k21 + p_row2[x + 2] * k22;

            // --- Unrolled Kernel Calculation for Pixel 2 (x+1) ---
            sum2 += p_row0[x + 1] * k00 + p_row0[x + 2] * k01 + p_row0[x + 3] * k02;
            sum2 += p_row1[x + 1] * k10 + p_row1[x + 2] * k11 + p_row1[x + 3] * k12;
            sum2 += p_row2[x + 1] * k20 + p_row2[x + 2] * k21 + p_row2[x + 3] * k22;

            // Apply activation and store results.
            output[y][x] = (sum1 > 0) ? sum1 : 0;
            if (x + 1 < IMAGE_SIZE) {
                output[y][x + 1] = (sum2 > 0) ? sum2 : 0;
            }
        }
    }
#else
    // Fallback for other kernel sizes.
    for (int y = 0; y < IMAGE_SIZE; y++) {
        for (int x = 0; x < IMAGE_SIZE; x++) {
            int sum = 0;
            for (int ky = 0; ky < KERNEL_SIZE; ky++) {
                for (int kx = 0; kx < KERNEL_SIZE; kx++) {
                    sum += padded_input[y + ky][x + kx] * kernel[ky][kx];
                }
            }
            output[y][x] = (sum > 0) ? sum : 0;
        }
    }
#endif
}
Editor is loading...
Leave a Comment