Untitled

 avatar
unknown
plain_text
9 months ago
5.1 kB
14
Indexable
/*
 * ===========================================================================
 * OPTIMISATIONS 1 & 2 COMBINED: INTEGER CONVERSION + IMAGE PADDING
 * ===========================================================================
 * This code combines the first two optimisation strategies:
 * 1. Image Padding: A padded input array is created to eliminate the need
 * for boundary-checking 'if' statements in the main loops[cite: 31].
 * 2. Integer Conversion: All floating-point 'double' operations are replaced
 * with 'int' to leverage native hardware speed and avoid software
 * emulation. Function logic is inlined.
 * ===========================================================================
 */
void conv2d_optimised(
    int input[IMAGE_SIZE][IMAGE_SIZE],
    int output[IMAGE_SIZE][IMAGE_SIZE],
    int kernel[KERNEL_SIZE][KERNEL_SIZE]
) {
    const int pad = KERNEL_SIZE / 2;
    const int PADDED_SIZE = IMAGE_SIZE + 2 * pad;
    int padded_input[PADDED_SIZE][PADDED_SIZE];

    // Initialize the padded array with zeros.
    for (int y = 0; y < PADDED_SIZE; y++) {
        for (int x = 0; x < PADDED_SIZE; x++) {
            padded_input[y][x] = 0;
        }
    }

    // Copy the original image into the center of the padded array[cite: 34].
    for (int y = 0; y < IMAGE_SIZE; y++) {
        for (int x = 0; x < IMAGE_SIZE; x++) {
            padded_input[y + pad][x + pad] = input[y][x];
        }
    }

    // Main convolution loop is now branch-free and uses integer math.
    for (int y = 0; y < IMAGE_SIZE; y++) {
        for (int x = 0; x < IMAGE_SIZE; x++) {
            // Use an integer accumulator.
            int sum = 0;

            for (int ky = 0; ky < KERNEL_SIZE; ky++) {
                for (int kx = 0; kx < KERNEL_SIZE; kx++) {
                    // Access the padded array directly with no boundary checks[cite: 35].
                    sum += padded_input[y + ky][x + kx] * kernel[ky][kx];
                }
            }

            // Apply activation function.
            if (sum > 0) {
                output[y][x] = sum;
            } else {
                output[y][x] = 0;
            }
        }
    }
}

/*
 * ===========================================================================
 * OPTIMISATIONS 1, 2, & 3 COMBINED: FINAL VERSION
 * ===========================================================================
 * This code combines all three optimisation strategies for maximum performance:
 * 1. Image Padding: Eliminates branching in the inner loops.
 * 2. Integer Conversion: Uses fast, native integer arithmetic.
 * 3. Micro-Optimization: Adds Kernel Caching, Loop Unrolling, and Pointer
 * Sliding to increase ILP and reduce memory traffic[cite: 40, 64].
 * NOTE: This implementation is specialized for KERNEL_SIZE = 3.
 * ===========================================================================
 */
void conv2d_optimised(
    int input[IMAGE_SIZE][IMAGE_SIZE],
    int output[IMAGE_SIZE][IMAGE_SIZE],
    int kernel[KERNEL_SIZE][KERNEL_SIZE]
) {
    const int pad = KERNEL_SIZE / 2;
    const int PADDED_SIZE = IMAGE_SIZE + 2 * pad;
    int padded_input[PADDED_SIZE][PADDED_SIZE] = {{0}};

    // Copy the original image into the center of the padded array.
    for (int y = 0; y < IMAGE_SIZE; y++) {
        for (int x = 0; x < IMAGE_SIZE; x++) {
            padded_input[y + pad][x + pad] = input[y][x];
        }
    }

    // Kernel Caching: Load 3x3 kernel into local variables (registers).
    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: Set pointers to the start of each relevant row.
        int *p_row0 = &padded_input[y][0];
        int *p_row1 = &padded_input[y + 1][0];
        int *p_row2 = &padded_input[y + 2][0];

        // Loop Unrolling: Process two output pixels per iteration.
        for (int x = 0; x < IMAGE_SIZE; x += 2) {
            // Integer accumulators for two concurrent calculations.
            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;
            // Ensure we don't write past the end of the row if IMAGE_SIZE is odd.
            if (x + 1 < IMAGE_SIZE) {
                output[y][x + 1] = (sum2 > 0) ? sum2 : 0;
            }
        }
    }
}






Editor is loading...
Leave a Comment