\documentclass[a4paper,12pt]{article}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[polish]{babel}
\usepackage{polski}
\usepackage[utf8]{inputenc}
\usepackage{indentfirst}
\usepackage{geometry}
\usepackage{array}
\usepackage[pdftex]{color,graphicx}
\usepackage{subfigure}
\usepackage{afterpage}
\usepackage{setspace}
\usepackage{color}
\usepackage{wrapfig}
\usepackage{listings}
\usepackage{datetime}

\renewcommand{\onehalfspacing}{\setstretch{1.6}}

\geometry{tmargin=2.5cm,bmargin=2.5cm,lmargin=2.5cm,rmargin=2.5cm}
\setlength{\parindent}{1cm}
\setlength{\parskip}{0mm}

\newenvironment{lista}{
\begin{itemize}
  \setlength{\itemsep}{1pt}
  \setlength{\parskip}{0pt}
  \setlength{\parsep}{0pt}
}{\end{itemize}}

\newcommand{\linia}{\rule{\linewidth}{0.4mm}}

\definecolor{lbcolor}{rgb}{0.95,0.95,0.95}
\lstset{
    backgroundcolor=\color{lbcolor},
    tabsize=4,
  language=C++,
  captionpos=b,
  tabsize=3,
  frame=lines,
  numbers=left,
  numberstyle=\tiny,
  numbersep=5pt,
  breaklines=true,
  showstringspaces=false,
  basicstyle=\footnotesize,
  identifierstyle=\color{magenta},
  keywordstyle=\color[rgb]{0,0,1},
  commentstyle=\color{Darkgreen},
  stringstyle=\color{red}
  }

\begin{document}

\noindent
\begin{tabular}{|c|p{11cm}|c|} \hline 
Grupa 1 & Piotr Dergun, Dominik Waśko & \ddmmyyyydate\today \tabularnewline
\hline 
\end{tabular}


\section*{Zadanie 5 - Rozmycie Gaussa w MPI}

Celem zadania jest wykonanie rozmycia obrazu za pomocą algorytmu  Gaussa o rozmiarze maski 5x5

\begin{lstlisting}
	for (i=0; i<ntasks; ++i)
	{
		chunk_order[i] = (i == 0) ? 0 : chunk_order[i-1] + chunk_sizes[i-1];
		chunk_sizes[i] = porcja*img.cols*3;
		
		if (i == ntasks-1)
			chunk_sizes[i] = (img.rows - porcja*(ntasks-1))*img.cols*3;
	}
	(...)
	MPI::COMM_WORLD.Gatherv(&img_part_out.data[0], porcja*img.cols*3, MPI::CHAR, &img_out.data[0], chunk_sizes, chunk_order, MPI::CHAR, 0);
\end{lstlisting}

Problem jest dzielony w ten sposób, że każdy z procesów dostaje określoną ilość wierszy do przetworzenia (zmienna porcja). Porcja jest obliczania jako stosunek liczby wierszy do liczby procesów. Ostatni proces może przetwarzać inną liczbę procesów, wyrażoną jako różnicę liczby wierszy i iloczynu pozostałych wątków oraz porcji. Ze względu na to, ostatni proces może mieć mniej lub więcej wierszy do liczenia. Do odsyłania nowych obrazów posłużono się funkcją MPI\_Gatherv, która pozwala scalać kawałki różnej długości.

\begin{lstlisting}
	int start_pos = taskid*porcja;
	int stop_pos = (taskid+1)*porcja;
	(...)
	for (i=start_pos; i<stop_pos; ++i)
	{
		q = 0;
		for (j=0; j<img.cols; ++j)
		{
			if (i<2 || i>img.rows-3 || j<2 || j>img.cols-3)
			{
				img_part_out.at<Vec3b>(p, q)[0] = img.at<Vec3b>(i, j)[0];
				img_part_out.at<Vec3b>(p, q)[1] = img.at<Vec3b>(i, j)[1];
				img_part_out.at<Vec3b>(p, q)[2] = img.at<Vec3b>(i, j)[2];
			}
			else
			{
				sumka_r = 0;
				sumka_g = 0;
				sumka_b = 0;
				m=i-2;
				for (k=0; k<RX; ++k,++m)
				{
					n=j-2;
					for (l=0; l<RY; ++l,++n)
					{
						sumka_b += ratio[k][l] * img.at<Vec3b>(m, n)[0]; 
						sumka_g += ratio[k][l] * img.at<Vec3b>(m, n)[1]; 
						sumka_r += ratio[k][l] * img.at<Vec3b>(m, n)[2]; 
					}	
				}
				img_part_out.at<Vec3b>(p, q).val[0] = sumka_b / suma_wag;
				img_part_out.at<Vec3b>(p, q).val[1] = sumka_g / suma_wag;
				img_part_out.at<Vec3b>(p, q).val[2] = sumka_r / suma_wag;
			}
			++q;
		}
		++p;
	}
\end{lstlisting}

Zmienne \textit{i} oraz \textit{j}  służą do iteracji po wszystkich  pikselach obrazu. W przypadkach brzegowych tzn. 2 piksele od krawędzi obrazu,  piksele są pozostawiane bez zmian. Jest to spowodowane tym, że w przypadku tych pikseli nie można zastosować maski 5x5. Wartość pozostałych pikseli jest zmieniana przy pomocy algorytmu Gaussa.

Do wczytania obrazu użyto biblioteki OpenCV. 

Zadanie zostało uruchomione dla obrazu o rozmiarze 2592x13824 px, na klastrze składającym się z 3 komputerów MacPro3,1 z dwoma procesorami Xeon E5462 oraz z 3 komputerów iMac14,2 z procesorem i5-4570. Taka konfiguracja daje w sumie 30 rdzeni bez Hyper Threading. Na potrzeby tego zadania wykorzystano jednak tylko 24, ponieważ pozostałe rdzenie były wykorzystywane do innych obliczeń. Obliczenia przeprowadzono tworząc od 1 do 25 procesów, dla każdej ilości wykonano 10 powtórzeń. Z otrzymanych wyników obliczono przyspieszenie oraz średni czas obliczania rozmycia. 

Rysunek 1 przedstawia wykres zależności przyspieszenia od ilości procesów. Można na nim zauważać że wzrost przyspieszenia uzyskuje się tylko do momentu gdy liczba procesów jest mniejsza lub równa ilości rdzeni. Powyżej 24 następuje już przetwarzanie współbieżne. Dla pewnych liczb procesów można zauważyć nierównomierne przyspieszanie, tzw. „schodki”. Jest to spowodowane tym, że synchronizacja sieciowa obejmuje kolejne maszyny, a czas obliczeń tak naprawdę jest czasem obliczeń najwolniejszej maszyny. W związku z tym, jeżeli któraś z maszyn chwilowo była zajęta przetwarzaniem innych informacji i zadanie cząstkowe na jakiejś maszynie liczyło się przez krótki okres czasu współbieżnie, zaważy to na stabilności czasów dla całego zadania. Rysunek 2. przedstawia wykres zależności czasu obliczeń od liczby procesów.  Można zobaczyć na nim, że powyżej 24 wątków czas obliczeń nie ulega już skróceniu. 

Z powyższych obserwacji wynika, że wykorzystanie większej liczby wątków pozwala znacząco skrócić czas wykonania programu. Jednak zwiększanie liczby wątków ponad to co oferuje procesor nie powoduje wzrostu wydajności i wymusza współbieżne liczenie.




\begin{figure}[!h]
	\centering
  \includegraphics[width=0.7\textwidth]{dane/przyspieszenie.jpg}
  \caption{Wykres zależności przyspieszenia obliczeń od liczby wykorzystanych wątków}
\end{figure}


\begin{figure}[!h]
	\centering
  \includegraphics[width=0.7\textwidth]{dane/czas.jpg}
  \caption{Wykres zależności czasu obliczeń od liczby wykorzystanych wątków}
\end{figure}



\end{document}
