diff options
Diffstat (limited to 'thesis')
-rw-r--r-- | thesis/main.tex | 40 | ||||
-rw-r--r-- | thesis/parts/design.tex | 76 |
2 files changed, 102 insertions, 14 deletions
diff --git a/thesis/main.tex b/thesis/main.tex index 21de962..d16e956 100644 --- a/thesis/main.tex +++ b/thesis/main.tex @@ -14,6 +14,32 @@ \newcommand{\code}{\texttt} \newcommand{\todo}[1]{\colorbox{yellow}{TODO: #1} \newline} +%% Code blocks +\usepackage{listings} +\usepackage{courier} +\definecolor{codegreen}{rgb}{0,0.6,0} +\definecolor{codegray}{rgb}{0.5,0.5,0.5} +\definecolor{codepurple}{rgb}{0.58,0,0.82} +\lstdefinestyle{mystyle}{ + commentstyle=\color{codegreen}, + keywordstyle=\color{magenta}, + numberstyle=\tiny\color{codegray}, + stringstyle=\color{codepurple}, + basicstyle=\ttfamily\footnotesize, + breakatwhitespace=false, + breaklines=true, + captionpos=b, + keepspaces=true, + numbers=left, + numbersep=5pt, + showspaces=false, + showstringspaces=false, + showtabs=false, + tabsize=4 +} + +\lstset{style=mystyle} + \begin{document} \begin{preliminary} @@ -50,25 +76,25 @@ from the Informatics Research Ethics committee. \end{preliminary} -\chapter{Introduction} +\chapter{Introduction} \label{chap:introduction} \input{parts/introduction} -\chapter{Background} +\chapter{Background} \label{chap:background} \input {parts/background} -\chapter{Design} +\chapter{Design} \label{chap:design} \input{parts/design} -\chapter{Implementation} +\chapter{Implementation} \label{chap:implementation} \input{parts/implementation} -\chapter{Results} +\chapter{Results} \label{chap:results} \input{parts/results} -\chapter{Analysis} +\chapter{Analysis} \label{chap:analysis} \input{parts/analysis} -\chapter{Conclusion} +\chapter{Conclusion} \label{chap:conclusion} \input{parts/conclusion} diff --git a/thesis/parts/design.tex b/thesis/parts/design.tex index 0a93836..03e281d 100644 --- a/thesis/parts/design.tex +++ b/thesis/parts/design.tex @@ -1,18 +1,80 @@ -\todo{Introduction} -\todo{Aims / expected input} +This chapter outlines the design of our container selection system (Candelabra), and the justification behind these design decisions. -\section{Overview of approach} +We first describe our aims and priorities for the system, and illustrate its usage with an example. + +We then provide an overview of the container selection process, and each part in it. + +\section{Aims \& Usage} + +We aim to create a program for container selection that can select based on both functional and non-functional requirements. +Flexibility is a high priority: It should be easy to add new container implementations, and to integrate our system into existing applications. +Our system should also be able to scale to larger programs, and remain convenient for developers to use. + +We chose to implement our system as a Rust CLI, and limit it to selecting containers for Rust programs. +We require the user to provide their own benchmarks, which should be representative of a typical application run. + +The user can specify their functional requirements by listing the required traits, and specifying properties that must always hold in a lisp-like language. +This part of the process is handled by Primrose\parencite{qin_primrose_2023}, with only minor modifications to integrate with the rest of our system. + +For example, take the below code from our test case based on the sieve of Eratosthenes (\code{src/tests/prime\_sieve} in the source artifacts). +Here we request two container types: \code{Sieve} and \code{Primes}. +The first must implement the \code{Container} and \code{Stack} traits, and must satisfy the \code{lifo} property. This property is defined at the top as only being applicable to \code{Stack}s, and requires that for any \code{x}, pushing \code{x} then popping from the container returns \code{x}. + +The second container type, \code{Primes}, must only implement the \code{Container} trait, and must satisfy the \code{ascending} property. +This property requires that at any point, for all consecutive \code{x, y} pairs in the container, \code{x $\leq$ y}. + +\begin{lstlisting} +/*SPEC* +property lifo<T> { + \c <: (Stack) -> (forall \x -> ((equal? (pop ((push c) x))) x)) +} + +property ascending<T> { + \c -> ((for-all-consecutive-pairs c) leq?) +} + + +type Sieve<S> = {c impl (Container, Stack) | (lifo c)} +type Primes<S> = {c impl (Container) | (ascending c)} +*ENDSPEC*/ +\end{lstlisting} + +Once we've specified our functional requirements and provided a benchmark (\code{src/tests/prime\_sieve/benches/main.rs}), we can simply run candelabra to select a container: + +\todo{Show selection process} + +Our tool integrates with Rust's packaging system (Cargo) to discover the information it needs about our project, then runs Primrose to find a list of implementations satsifying our functional requirements. + +Once it has this list, it estimates a 'cost' for each candidate, which is an upper bound on the total time taken for all container operations. +At this point, it also checks if an 'adaptive' container would be better, by checking if one implementation is better performing at a lower n, and another at a higher n. + +Finally, it picks the container with the minimum cost, and creates a new Rust file where the chosen container type is exported. + +Our tool requires little user intervention, integrates well with existing workflows, and the time it takes scales linearly with the number of container types in a given project. + +\section{Selection Process} + +We now describe the design of our selection process in detail, and justify our choices. + +As mentioned above, the first stage of our process is to satisfy functional requirements, which we do using code from Primrose\parencite{qin_primrose_2023}. +The exact internals are beyond the scope of this paper, but in brief this works by: +\begin{itemize} +\item Finding all implementations in the container library that implement all required traits +\item Translate any specified properties to a Rosette expression +\item For each implementation, model the behaviour of each operation in Rosette, and check that the required properties always hold +\end{itemize} + +We use the code provided with the Primrose paper, with minor modifications elaborated on in \ref{chap:implementation}. Once a list of functionally close enough implementations have been found, selection is done by: \begin{itemize} -\item Get a list of implementations that satisfy the program's functional requirements -\item Estimating the cost of each operation, for each implementation, for any given n value -\item Profiling the program to rank operation 'importance', +\item For each operation of each implementation, build a cost model which can estimate the 'cost' of that operation at any given container size $n$ +\item Profile the program, tracking operation frequency and container sizes \item Combining the two to create an estimate of the relative cost of each implementation \end{itemize} -\subsection{Cost Estimation} +\subsection{Cost Models} We use an approach similar to CollectionSwitch\parencite{costa_collectionswitch_2018}, which assumes that the main factor in how long an operation takes is the current size of the collection. |