Sidenote: Optimizing our code

What will this section cover?

As you may have noticed our algorithm is now really slow. This is due to the calculation of Pearson Correlation Coefficients. We can speed this up by a lot if we notice a couple of things.

  1. \(\sqrt{a}\sqrt{b}=\sqrt{a b}\). Since square rooting is a very expensive operation, we can optimize the calculation of \(\sigma_x \sigma_y\) from two square root calculations to one.
  2. We are recalculating a lot of averages and standard deviations. We can pre-compute these averages and standard deviations and fetch them instead of recomputing them.
  3. Since we only care about the maximum correlation coefficient and have no interest in the value itself, we can stop doing any factorization.

In the following code we have created a function which applies these three optimized functions when calculating the correlation coefficients.

point_means = np.mean(traces, axis=0, dtype=np.float64)
point_mean_diff = traces - point_means

point_mean_diff_squared_sum = np.sum(np.power(point_mean_diff, 2), axis=0)

def optimized_calculate_correlation_coefficients(subkey, subkey_index):
    # Declare a numpy for the hypothetical power usage
    hypothetical_power = np.zeros(num_traces)

    for trace_index in range(0, num_traces):
        hypothetical_power[trace_index] = hypothetical_power_usage(
            subkey,
            textins[trace_index][subkey_index]
        )

    hypothetical_power_mean = np.mean(hypothetical_power, dtype=np.float64)
    hypothetical_power_mean_diff = hypothetical_power - hypothetical_power_mean

    hypothetical_power_mean_diff_sum_squared = np.sum(
        np.power(hypothetical_power_mean_diff, 2)
    )

    # We are going to the determine correlations between each trace point
    # and the hypothetical power usage. This will save all those coefficients
    point_correlation = np.zeros(num_points)

    # Loop through all points and determine their correlation coefficients
    for point_index in range(0, num_points):
        point_correlation[point_index] = np.sum(
            hypothetical_power_mean_diff *

            # Look at the individual traces points for every trace
            point_mean_diff[:, point_index]
        ) / np.sqrt(hypothetical_power_mean_diff_sum_squared *
                point_mean_diff_squared_sum[point_index])

    return point_correlation

After optimizing the calculations of the Pearson correlation coefficients, our crack.py script should run a lot faster.