% !TeX spellcheck = cs_CZ
% !TeX root = ../BP_Prokopič_Martin_2021.tex

Samotné programování tvoří pouze malou část vývojového cyklu software. Aby byl výsledek využitelný, musíme si být jistí jeho správností na různých vstupech. Nejprve popíšeme, jak jsme pomocí dvou druhů automatizovaných testů ověřili korektnost naší implementace.

Každý z~popsaných vyhledávacích algoritmů má svoje výhody a~nevýhody, proto naše implementace v~sekci \ref{sec:compare} porovnáme z~hlediska rychlosti a~vhodnosti pro různé situace.

\section{Jednotkové testy}\label{sec:utests}

Knihovna \gls{alt} využívá pro správu a~spouštění automatizovaných testů open-source projekt \emph{Catch2}. Pomocí maker v~obyčejných C++ souborech se definují jednotlivé testované případy a~asserty. Nástroj navíc obsahuje makra pro generování vstupních dat, díky kterým jsou jednotkové testy přehlednější a~obsahují méně duplikovaného kódu.

Všechny třídy implementované v~praktické části práce jsme pokryli jednotkovými testy. Testujeme v~nich na ručně sestavených vstupech chování jednotlivých tříd v~izolaci. Snažili jsem se pokrýt všechny speciální případy, např. chování, pokud se na vstup algoritmů předá prázdný řetězec. Testujeme také, zda je při zavolání algoritmů s~nevalidními vstupy (např. neúplná pomocná data) vyhozena výjimka.

Jednotkové testy jsme umístili do podsložek \code{test-src} dle jmenného prostoru testované třídy v~jednotlivých modulech \gls{alt}. Testy můžeme poté spouštět pomocí programů \code{test\_alib2algo\_experimental} a~\code{test\_alib2data\_experimental}. \emph{Catch2} dokonce umožňuje jednotlivé testovací případy označit tagy, a~poté je pomocí argumentů testovacích programů filtrovat, viz obrázek \ref{fig:singletest}.

\begin{figure}\vspace{-1em}
\begin{lstlisting}
	./test_alib2algo_experimental "BWT Decode"
\end{lstlisting}\vspace{-0.5em}\caption{Příkaz pro spuštění vybraného jednotkového testu}\label{fig:singletest}
\end{figure}

\section{Integrační testy}\label{sec:itests}

Integrační testy se na rozdíl od jednotkových zaměřují na funkčnost tříd v~rámci větších celků. V~\gls{alt} mají formu \acrshort{aql} skriptů, které jsou buďto psané ručně (\code{test\_aqltests}), nebo automaticky generované pomocí C++ kódu (\code{test\_cppaqltests}). Prověřují tak celý projekt od parseru a~interpreteru jazyka \gls{aql}, přes registraci součástí, až po funkčnost zřetězení samotných algoritmů v~delších sekvencích (\emph{pipelines}). O~spouštění testů se opět stará framework \emph{Catch2}.

Implementované vyhledávací algoritmy jsme přidali do existujícího souhrnného testu přesných vyhledávacích algoritmů \code{ExactMatching}. Tento test porovnává výsledky algoritmů na připravených vstupech, a~posléze na náhodně generovaných řetězcích. Dále jsme přidali vlastní integrační test \code{BWT}, který ověřuje korektnost dekódování a~FM-indexu s~různými parametry (velikosti přihrádek a~podpřihrádek a~hustota řídkého sufixového pole). Testy provádíme nad abecedou pokrývající všechny možné hodnoty datového typu \code{char}, ověřujeme tak i~výběr ukončovacího symbolu a~řazení pomocí třídy \code{SymbolCompare}.

\section{Porovnání rychlosti algoritmů}\label{sec:compare}

V~této sekci srovnáme rychlost implementovaných algoritmů experimentálním měřením v~různých situacích. Využíváme modul \code{alib2measure} od Ing. Radovana Červeného \cite{cerveny16}, který zjednodušuje měření času a~paměti spotřebované při běhu algoritmů a~jejich částí. Výsledky pak poskytuje ve formě stromu, např. vrchol volání implementovaného algoritmu \method{BWT-Boyer-Moore} ze sekce \ref{sec:bmmod} obsahuje syny reprezentující výpočet pravidel \emph{bad-character shift} a~\emph{good-suffix shift} pro daný vzorek, a~poté syna pro samotné jádro algoritmu.

\subsection{Testovací prostředí}

Všechna měření jsme prováděli na počítači s~procesorem Intel Core i7-8550U, 16 GB RAM a~systémem Ubuntu 20.04. Pro kompilaci \acrshort{alt} jsme použili \acrshort{gcc} 10.2.0 dostupný z~repozitáře operačního systému a~knihovnu jsme zkompilovali v~režimu \emph{Release} (se zapnutými optimalizacemi). Implementované algoritmy jsme volali z~modulu \code{bwtperftest} přilinkovaného ke knihovně \acrshort{alt}. Měřili jsme tzv. \emph{wall-clock} čas (dobu výpočtu pozorovanou uživatelem) a~všechna měření jsme spouštěli 20krát a~výsledky zprůměrovali, abychom snížili vliv náhodného výběru vzorků, ostatních běžících procesů a~systému.

\subsection{Rychlost algoritmů v~závislosti na délce vzorku}

Jako první se zaměříme na vliv délky vzorku na rychlost implementovaných vyhledávacích algoritmů. Vstupním řetězcem pro nás byl transformovaný soubor \code{alice29.txt} z~Cantenbury Corpus \cite{bell02corpus}, který obsahuje zhruba 150~KB anglického textu. Testovali jsme postupně vzorky délek $m = 1, \dots, 100$, které se ve vstupu vyskytují alespoň jednou. U~krátkých vzorků je tak rychlost ovlivněna i~počtem výskytů. Výsledek jsme zachytili v~obrázku \ref{fig:bypatlen}.

\begin{figure}
	\centering
	\input{figures/bypatlen.pgf}
	\caption{Graf závislosti času na délce hledaného vzorku}\label{fig:bypatlen}
\end{figure}

Můžeme si všimnout, že výkon algoritmů využívajících binární vyhledávání na délce vzorku prakticky nezávisí. Naopak vidíme, že pro hledání krátkých vzorků není FM-index příliš vhodný. Důvodem je, že pro získání pozice každého výskytu v~původním textu musíme volat operaci \method{Locate}. Krátké vzorky se v~textu průměrně vyskytují častěji, a~proto je celkový čas potřebný pro běh algoritmu vysoký (v~testovaném případě pro $m = 1$ trvá hledání okolo 150~ms).

\subsection{Rychlost algoritmů v~závislosti na počtu vzorků}

V~sekci \ref{sec:approaches} jsme popsali tři kategorie algoritmů pro vyhledávání v~transformovaném textu. Všechny implementované algoritmy kromě FM-indexu potřebují před samotným vyhledáváním vypočítat pro vstup $L$ určitá pomocná data. Ať už se jedná o~dekódování textu $T$ nebo o~naplnění pomocných polí, stačí tento krok provést pouze jednou nezávisle na počtu vyhledávaných vzorků.

Na obrázku \ref{fig:timeline} uvádíme časovou osu jednotlivých fází algoritmů při vyhledávání jednoho náhodně vybraného vzorku délky 10 v~transformovaném souboru \code{alice29.txt}. Vidíme, že příprava pomocných dat (vyžadující lineární čas) trvá mnohanásobně déle, než samotné vyhledávání a~i~při volbě jiných délek textu tak bude tato složka vždy převažovat. Obrázek \ref{fig:bynumpat} zachycuje formou grafu vývoj celkové rychlosti algoritmů v~závislosti na počtu vyhledávaných vzorků.

\begin{figure}
	\centering
	\input{figures/timeline.pgf}\glsadd{bcs}\glsadd{gss}
	\caption{Časová osa fází vyhledávacích algoritmů pro jeden vzorek}\label{fig:timeline}
\end{figure}

\begin{figure}
	\centering
	\input{figures/bynumpat.pgf}
	\caption{Graf závislosti času na počtu vyhledávaných vzorků}\label{fig:bynumpat}
\end{figure}

Můžeme pozorovat, že při vyhledávání méně než 6 vzorků je rychlejší modifikovaný algoritmus Boyer-Moore pracující přímo s~transformovaným textem. Bližším zkoumáním jsme dospěli k~závěru, že je to způsobené režií konstrukce \code{string::LinearString} s~dekódovaným textem. Lepší rychlosti ale snadno dosáhneme pomocí algoritmů využívajících binárního vyhledávání (\method{Binary-Search} a~\method{SA-Binary-Search}). Zjednodušený FM-index podával díky předpočítaným datům nejlepší výsledky, ale za cenu vyšší potřebné paměti na uložení textu.

\subsection{Vliv velikosti přihrádek na vlastnosti zjednodušeného FM-indexu}

Při konstrukci FM-indexu musíme volbou velikosti přihrádek a~podpřihrádek hledat kompromis mezi rychlostí operací a~paměťovou náročností indexu. Tuto závislost jsme experimentálně změřili a~zachytili ji grafem na obrázku \ref{fig:fmparams}. Hledali jsme 50 náhodně zvolených vzorků délky 10 v~transformovaném souboru \code{alice29.txt}. Velikost přihrádky $l_{sb}$ jsme volili jako 16násobek velikosti podpřihrádky $l_b$ uvedené na vodorovné ose.

\begin{figure}[h]
	\centering
	\input{figures/fmparams.pgf}
	\caption{Graf závislosti rychlosti operace \method{Count} a~spotřeby paměti na velikosti podpřihrádek FM-indexu (velikost textu je cca 190~KB)}\label{fig:fmparams}
\end{figure}

Díky nepřímé úměře mezi velikostí podpřihrádky a~potřebnou pamětí pro uložení FM-indexu nemá smysl volit $l_b$ větší, než zhruba 2~000. Vzhledem k~tomu, že potřebný čas pro operaci \method{Count} roste lineárně, ideální kompromis rychlosti a~spotřeby paměti najdeme okolo 1~000, což souhlasí s~výsledky v~\cite{ferragina01}. Obdobně časová složitost operace \method{Locate} je nepřímo úměrná hustotě označených řádků, a~ta přímo úměrně ovlivňuje velikost potřebného úložiště pro index.
