When parallelizing on the CPU where one has 4 cores one would have a shared memory system, this  means that one does not need to use communication time on distributing data. Rather the read and writes can occur simultaniously to different addresses, and when it comes to calculations the work load can be distributed such that each core gets equal work that can be retrieved from the shared memory (L3 cache). On the system used in this implementation there is a 2 MB shared memory L3 cache that all the cores share. They have equally large level 2 caches that are local per core and have the size of 512 KB. This way when a portion of the data is sent to the shared memory it can be equally distributed and calculation in convolution are independent and therefore each core can perform calculations without having to wait. When all cores are done calculating, then the shared memory can be updated such the next 2 MB can be calculated. 

\begin{figure}[H]
	\centering
	\includegraphics[width=4cm]{report/method/multicpu.PNG}
	\caption{Image illustrating the amount of threads running when calculations are performed}
	\label{multicputhread}
\end{figure}

In this implementation OpenMP was used to parallelize the code. 4 threads where used since there are 4 cores such that each core can run a thread. The directive used was ''\#pragma omp parallel for'', which is the one that unrolls for-loops and dedicates separate instructions for each core to perform. It is also specified that the calculated variable be private in each thread. The calculated value for each pixel is stored in a temporary variable before inserted into the result array , and as such it is kept private for each thread such that they do not all write to the same variable. The filter on the other hand is placed in constant memory and is read by all.

\begin{figure}[H]
	\centering
	\includegraphics[width=10cm]{report/method/multicpu2.PNG}
	\caption{Image illustrating the work distribution on a Quad-core compared to a single-core}
	\label{multicputhread2}
\end{figure}

When it comes to optimizing caching and memory, the use of already optimized code for one core duplicated to several cores sharing memory is already optimal. See Figure \ref{multicputhread2} that explains how calculations are just distributed in the same optimal fashion. This means that other than making sure that some critical variables are kept private for each thread no other optimizations were made to the implementation. Given that reads and writes are done sequentially the expected speedup compared to running on one core should be close to 4. But since some parts of the code are non-parallel, like I/O, it should be a bit less. 
\newline

\lstset{frame=single,tabsize=2, language=Java, basicstyle=\footnotesize, numberstyle=\footnotesize, numbers=left, captionpos=b, breaklines=true, title={C code with OpenMP for three dimentional convolution}}
\begin{lstlisting}
float counter = len*len*len;
float imagevalue;
#pragma omp parallel for private(imagevalue, filter)
for (int i = 0; i < z; i++) {
	for (int j = 0; j < y; j++) {
		for (int k = 0; k < x; k++) {
			.
			.
			.
		}
	}
}
\end{lstlisting}