Skip to content

Commit 38bd3d9

Browse files
committed
Rework the bench section.
1 parent 3186270 commit 38bd3d9

15 files changed

+83
-101
lines changed

bench.tex

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ \subsection{Comparing Algorithms in the \haskell Implementation}
4242
evaluation measures the performance of \code{star} on a non-sparse
4343
language and of {concatenation} applied to a finite and an infinite
4444
language.
45-
\item $\Rconcat{\Rcomplement{(\Rstar{a})}}{b}$: Finally, this regular
45+
\item $\Rconcat{\Rcomplement{(\Rstar{a})}}{b}$: This regular
4646
expression exercises the complement operation and tests the
4747
concatenation of a very large language,
4848
$P (w\in \Lang{\Rcomplement{(\Rstar a)}}) = 1$, to a much smaller
4949
language.
50+
\item $\Rintersect{\Rcomplement{(\Rstar{a})}}{\Rcomplement{(\Rstar{b})}}$:
51+
This regular expression applies {intersection} to two large languages
52+
and make use of the complement. Its goal is to measure the efficiency
53+
of set operations.
5054
\end{itemize}
5155

5256
\begin{figure}[!t]
@@ -58,30 +62,27 @@ \subsection{Comparing Algorithms in the \haskell Implementation}
5862

5963
In the evaluation, we consider five variants of the Haskell implementation.
6064
\begin{itemize}
61-
\item The \textbf{naive} implementation corresponds to the code developed by
62-
the end of Section~\ref{sec:motivation}. It transforms to and from
63-
segments on the fly and uses plain list indexing.
65+
\item \textbf{McIlroy} is our implementation of
66+
the algorithm by \citet{DBLP:journals/jfp/McIlroy99}.
6467
\item The \textbf{seg} implementation uses the infinite list-based segmented
65-
representation throughout (\cref{sec:segm-repr}). Moreover,
66-
it relies on maps and sparse indexing for concatenation and closure
67-
(\cref{sec:sparse-indexing}).
68+
representation throughout (\cref{sec:segm-repr}).
6869
\item The \textbf{segConv} implementation additionally
69-
applies the convolution approach presented in \cref{sec:convolution}.
70-
\item The \textbf{ref} implementation uses symbolic segments
71-
from \cref{sec:more-finite-repr} combined with
72-
maps and sparse indexing.
70+
applies the convolution approach (\cref{sec:convolution,sec:faster-closure}).
71+
% \item The \textbf{ref} implementation uses symbolic segments
72+
% from \cref{sec:more-finite-repr} combined with
73+
% maps and sparse indexing.
7374
\item The \textbf{refConv} implementation combines
74-
symbolic segments, sparse indexing, and the convolution approach.
75+
symbolic segments (\cref{sec:segm-repr,sec:more-finite-repr}) with the convolution approach.
7576
\end{itemize}
7677

7778
Performances are evaluated by iterating through the stream of words
78-
produced by the generator, forcing their evaluation.\footnote{In
79+
produced by the generator, forcing their evaluation\footnote{In
7980
Haskell, forcing is done using \lstinline{Control.DeepSeq}.}
8081
and recording the elapsed timed every 20 words.
8182
We stop the iteration after 5 seconds.
82-
The resulting graph plots the time (x-axis) against the number of words (y-axis) produced so far. The slope of the graph in indicates the generation speed of the plotted algorithm, high slope is correlated to high generation speed. \cref{bench:haskell:all} contains the results for the Haskell implementations.
83-
84-
Most algorithms generate between 3000 and 150000 words in the first
83+
The resulting graph plots the time (x-axis) against the number of words (y-axis) produced so far. The slope of the graph indicates the generation speed of the plotted algorithm, high slope is correlated to high generation speed. \cref{bench:haskell:all} contains the results for the Haskell implementations.
84+
85+
Most algorithms generate between $1.3\cdot10^3$ and $1.4\cdot10^6$ words in the first
8586
second, which seems more than sufficient for testing purposes.
8687
The \textbf{refConv} implementation
8788
which uses symbolic segments and convolutions is consistently in the
@@ -91,24 +92,23 @@ \subsection{Comparing Algorithms in the \haskell Implementation}
9192
This observation validates that the
9293
changes proposed in \cref{sec:improvements} actually lead to
9394
improvements.
94-
95-
Looking at each graph in more detail, we can make the following
95+
%
96+
Looking at each graph in detail, we can make the following
9697
remarks:
9798
\begin{itemize}[leftmargin=*]
9899
\item All implementations are equally fast on $\Rstar a$ except
99-
the naive implementation, which relies on list lookups without
100+
\textbf{McIlroy}, which relies on list lookups without
100101
sparse indexing.
101-
\item For $\Rstar{(\Rconcat{a}{\Rstar{b}})}$ and
102-
$\Rconcat{\Rcomplement{(\Rstar{a})}}{b}$, the graph of some implementations
102+
\item The graph of some implementations
103103
has the shape of ``skewed stairs''. We believe this phenomenon is due to
104104
insufficient laziness: when arriving at a new segment, part of the
105105
work is done eagerly which causes a plateau. When that part is done,
106106
the enumeration proceeds lazily. As laziness and GHC
107107
optimizations are hard to control, we did not attempt to correct this.
108-
\item $\Rstar{(\Rconcat{a}{\Rstar{b}})}$ demonstrates that sparse indexing
109-
does degrade performance when applying \code{star} to non-sparse languages.
110-
Using the convolution technique presented in \cref{sec:convolution} resolves this problem.
111-
\item The \textbf{ref} and \textbf{refConv} algorithms are
108+
\item $\Rstar{(\Rconcat{a}{\Rstar{b}})}$ demonstrates that
109+
the convolution technique presented in \cref{sec:convolution}
110+
leads to significant improvements when applying \code{star} to non-sparse languages.
111+
\item The \textbf{refConv} algorithm is
112112
significantly faster on $\Rconcat{\Rcomplement{(\Rstar{a})}}{b}$
113113
compared to \textbf{seg} and \textbf{segConv}. We have no good
114114
explanation for this behavior as the code is identical up to the
@@ -117,12 +117,15 @@ \subsection{Comparing Algorithms in the \haskell Implementation}
117117
is $\Lang{b}$, which is also represented finitely by
118118
\textbf{segConv} and should thus benefit from the convolution
119119
improvement in the same way as \textbf{refConv}.
120+
\item $\Rstar{(\Rconcat{a}{\Rstar{b}})}$ shows that all our algorithm have similar
121+
performance profiles on set-operation. They are also significantly
122+
faster than \textbf{McIlroy}.
120123
\end{itemize}
121124

122125

123126
\subsection{Comparing Data Structures in the \ocaml Implementation}
124127
\label{sec:bench:ocaml}
125-
\begin{figure}[!p]
128+
\begin{figure}[!tb]
126129
\centering
127130
\includegraphics[width=\linewidth]{measure/ocaml_all.png}
128131
\caption{Benchmark for the \ocaml implementation with various data-structures}
@@ -169,32 +172,10 @@ \subsection{Comparing Data Structures in the \ocaml Implementation}
169172
It seems that the linear cost of memoizing the thunk list and
170173
allocating the vectors
171174
is often higher than simply recomputing lists.
175+
\item $\Rstar{(\Rconcat{a}{\Rstar{b}})}$ shows that sorted enumerations and tries
176+
perform well on set-operations, even compared to strict sets.
172177
\end{itemize}
173178

174-
175-
176-
While the regular expressions presented previously do exercise
177-
\code{concatenation} and \code{star}, they do not exercise set
178-
operations. To test set operations on non-trivial segments (that are
179-
neither full nor empty), we consider the language of words with at
180-
least one $a$ and one $b$. This language can be built in two ways:
181-
$\Rintersect{\Rcomplement{(\Rstar{a})}}{\Rcomplement{(\Rstar{b})}}$
182-
and $\Rconcat{(\Runion{a\Rstar{a}b}{b\Rstar{b}a})}{\Rstar{\Sigma}}$.
183-
The first expression applies {intersection} to two large languages,
184-
the second expression takes the union of smaller languages, but uses a
185-
concatenation. The results are shown \cref{bench:ocaml:union}.
186-
Lazy and thunk lists, with or without
187-
memoization, perform well on unions and intersections but less so
188-
on concatenations. Performance of
189-
strict sets is surprisingly poor.
190-
Tries are very efficient on concatenations.
191-
192-
% \begin{figure}[!t]
193-
% \includegraphics[width=\linewidth]{measure/ocaml_union.png}
194-
% \caption{Benchmarking \texttt{union} in the \ocaml data-structures}
195-
% \label{bench:ocaml:union}
196-
% \end{figure}
197-
198179
\subsection{The Influence of Regular Expressions on Performance}
199180
\begin{figure*}[!tp]
200181
\centering

conclusions.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ \section{Conclusions and Future Work}
1313
Even though our implementations are not heavily optimized, our approach generates
1414
languages at a rate that is more than sufficient for testing
1515
purposes, between $1.3\cdot10^3$ and $1.4\cdot10^6$ strings per seconds.
16-
We can now to combine our generator with property based testing
16+
We can then combine our generator with property based testing
1717
to test regular expression parsers on randomly-generated regular expressions.
1818
While our approach eliminated the need for an oracle, the burden of correctness
1919
now lies on the language generator. We would like to implement our algorithm

measure/haskell_all.gnuplot

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# set terminal x11 size 1500,500 font 'Deja Vu Sans Mono,14' persist
44

5-
set terminal pngcairo transparent size 1000,1650 rounded font 'Deja Vu Sans,19'
5+
set terminal pngcairo transparent size 1000,2000 rounded font 'Deja Vu Sans,19'
66
set output 'haskell_all.png'
77

88
# set terminal tikz standalone size 15,6 textscale 0.5
@@ -29,7 +29,7 @@ set yrange [0:]
2929
# Put the legend at the bottom left of the plot
3030
set key left top
3131

32-
set multiplot layout 3,1 columnsfirst scale 1,1 spacing 1,1
32+
set multiplot layout 4,1 columnsfirst scale 1,1 spacing 1,1
3333

3434
set lmargin at screen 0.15; set rmargin at screen 0.98
3535
set tmargin 0.3
@@ -42,8 +42,9 @@ set style line 5 lt 1 lc rgb "#66a61e" lw 4 pt 7 ps 1.5 dt "_. "
4242
set style line 6 lt 1 lc rgb "#e6ab02" lw 4 pt 7 ps 1.5 dt ". "
4343
set style line 7 lt 1 lc rgb "#a6761d" lw 4 pt 7 ps 1.5 dt "-"
4444

45-
re = '(ab*)* a* ~(a*)b'
46-
algo = "naive ref refConv seg segConv"
45+
re = '(ab*)* a* ~(a*)b ~(a*)&~(b*)'
46+
file = "McIlroy segStar segConvStar refConvStar"
47+
algo = "McIlroy seg segConv refConv"
4748

4849
last = words(re)
4950

@@ -61,7 +62,7 @@ do for [i = 1:last] {
6162
unset key
6263
}
6364
set label 1 word(re,i) noenhanced center at graph 0.5,0.95 font ',21'
64-
plot for [j = 1:words(algo)] word(re,i)."_".word(algo,j)."_haskell.csv" using 2:($1/10000) title word(algo,j) noenhanced with lines ls j
65+
plot for [j = 1:words(algo)] word(re,i)."_".word(file,j)."_haskell.csv" using 2:($1/10000) title word(algo,j) noenhanced with lines ls j
6566
}
6667

6768
unset multiplot

measure/haskell_all.png

25.1 KB
Loading

measure/haskell_csv.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ IFS=$'\n\t'
55
set -f #Disable globing
66

77

8-
ReBase=('a*' '(ab*)*' '~(a*)b')
8+
ReBase=('a*' '(ab*)*' '~(a*)b' '~(a*)&~(b*)')
99
ReMore=('a*' 'a*b' 'ba*' '(ab*)*' '~(a*)b' '((a|b)(a|b))*' '(1(01*0)*1|0)*' '~(a*)&~(b*)')
1010

11-
BackendBase=("naive" "ref" "seg" "refConv")
12-
BackendMore=("segConv")
11+
BackendBase=("McIlroy" "segStar" "segConvStar")
12+
BackendMore=("refConvStar")
1313

1414
function genH {
1515
file="$2_$1_haskell.csv"
1616
echo "Regex $2 on backend $1 to $file"
1717
re-generate-exe \
1818
--alphabet "ab" -s20 \
19-
-b "${1^}Star" \
19+
-b "${1^}" \
2020
"$2" > "$file"
2121
}
2222

@@ -38,5 +38,5 @@ go BackendMore ReMore
3838

3939
echo "Gnuploting to haskell_all.png!"
4040
gnuplot haskell_all.gnuplot
41-
echo "Gnuploting to haskell_langs.png!"
42-
gnuplot haskell_langs.gnuplot
41+
# echo "Gnuploting to haskell_langs.png!"
42+
# gnuplot haskell_langs.gnuplot

measure/haskell_langs.gnuplot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ set style line 6 lt 1 lc rgb "#e6ab02" lw 4 pt 7 ps 1.5 dt ". "
3939
set style line 7 lt 1 lc rgb "#a6761d" lw 4 pt 7 ps 1.5 dt "-"
4040

4141
re = 'a* a*b ba* (ab*)* ~(a*)b ((a|b)(a|b))* (1(01*0)*1|0)* ~(a*)&~(b*)'
42-
algo = "segConv"
42+
algo = "refConvStar"
4343

4444
plot for [i = 1:words(re)] word(re,i)."_".algo."_haskell.csv" using 2:($1/10000) title word(re,i) noenhanced with lines ls i

measure/haskell_langs.png

-5.38 KB
Loading

measure/langs.gnuplot

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ set multiplot layout 1,2 columnsfirst scale 1,1
4242
unset key
4343

4444
re = 'a* a*b ba* (ab*)* ~(a*)b ((a|b)(a|b))* (1(01*0)*1|0)* ~(a*)&~(b*)'
45-
algo="segConv"
45+
algo="refConvStar"
4646

47-
set title "Haskell with segConv"
47+
set title "Haskell with refConv"
4848
plot for [i = 1:words(re)] word(re,i)."_".algo."_haskell.csv" using 2:($1/10000) title word(re,i) noenhanced with lines ls i
4949

5050

measure/langs.png

5.49 KB
Loading

measure/ocaml_all.gnuplot

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# set terminal x11 size 1500,500 font 'Deja Vu Sans Mono,14' persist
44

5-
set terminal pngcairo transparent size 1000,2550 rounded font 'Deja Vu Sans,19'
5+
set terminal pngcairo transparent size 1000,2000 rounded font 'Deja Vu Sans,19'
66
set output 'ocaml_all.png'
77

88
# set terminal tikz standalone size 15,6 textscale 0.5
@@ -29,7 +29,7 @@ set yrange [0:]
2929
# Put the legend at the bottom left of the plot
3030
set key left top
3131

32-
set multiplot layout 5,1 columnsfirst scale 1,1 spacing 0,0
32+
set multiplot layout 4,1 columnsfirst scale 1,1 spacing 0,0
3333

3434
set lmargin at screen 0.15; set rmargin at screen 0.98
3535
set tmargin 0.3
@@ -42,7 +42,7 @@ set style line 5 lt 1 lc rgb "#66a61e" lw 4 pt 7 ps 1.5 dt "_. "
4242
set style line 6 lt 1 lc rgb "#e6ab02" lw 4 pt 7 ps 1.5 dt ". "
4343
set style line 7 lt 1 lc rgb "#a6761d" lw 4 pt 7 ps 1.5 dt "-"
4444

45-
re = '(ab*)* a* ~(a*)b ~(a*)&~(b*) (aa*b|bb*a)(a|b)*'
45+
re = '(ab*)* a* ~(a*)b ~(a*)&~(b*)'
4646
algo = "ThunkList ThunkListMemo LazyList StrictSet Trie"
4747

4848
last = words(re)

measure/ocaml_all.png

-65.7 KB
Loading

measure/ocaml_csv.sh

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ IFS=$'\n\t'
55
set -f #Disable globing
66

77

8-
ReBase=('a*' '(ab*)*' '~(a*)b' '~(a*)&~(b*)' '(aa*b|bb*a)(a|b)*')
9-
ReMore=('a*' 'a*b' 'ba*' '(ab*)*' '~(a*)b' '((a|b)(a|b))*' '(1(01*0)*1|0)*' '~(a*)&~(b*)' '(aa*b|bb*a)(a|b)*')
8+
ReBase=('a*' '(ab*)*' '~(a*)b' '~(a*)&~(b*)')
9+
ReMore=('a*' 'a*b' 'ba*' '(ab*)*' '~(a*)b' '((a|b)(a|b))*' '(1(01*0)*1|0)*' '~(a*)&~(b*)')
1010

1111
BackendBase=("ThunkListMemo" "ThunkList" "StrictSet" "Trie")
1212
BackendMore=("LazyList")
@@ -38,7 +38,5 @@ go BackendMore ReMore
3838

3939
echo "Gnuploting to ocaml_all.png!"
4040
gnuplot ocaml_all.gnuplot
41-
echo "Gnuploting to ocaml_langs.png!"
42-
gnuplot ocaml_langs.gnuplot
43-
echo "Gnuploting to ocaml_union.png!"
44-
gnuplot ocaml_union.gnuplot
41+
# echo "Gnuploting to ocaml_langs.png!"
42+
# gnuplot ocaml_langs.gnuplot

measure/ocaml_langs.png

-11.1 KB
Loading

ocaml.tex

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,11 @@ \subsection{Core Algorithm}
142142

143143
An enumeration is represented by a function which takes a unit argument and returns
144144
a node. A node, in turn, is either \code{Nothing} or a \code{Cons} of an
145-
element and the tail of the sequence. The empty enumeration, for instance, is
146-
represented as \code{fun () -> Nothing}.
147-
This representation is lazy, fast, lightweight, and almost as easy to
148-
manipulate as regular lists.
145+
element and the tail of the sequence.
146+
% The empty enumeration, for instance, is
147+
% represented as \code{fun () -> Nothing}.
148+
% This representation is lazy, fast, lightweight, and almost as easy to
149+
% manipulate as regular lists.
149150
% \footnote{See
150151
% \url{https://github.com/ocaml/ocaml/pull/1002} for a long discussion
151152
% on the topic.}
@@ -156,7 +157,7 @@ \subsection{Core Algorithm}
156157
As an example, the implementation of language union is shown below.
157158
The trailing unit argument \code{()} drive the evaluation of the
158159
sequence lazily. With this definition, \code{union s1 s2} cause no
159-
evaluation before is applied to \code{()}.
160+
evaluation before it is applied to \code{()}.
160161
\begin{lstlisting}
161162
let rec union s1 s2 () = match s1(), s2() with
162163
| Nothing, x | x, Nothing -> x
@@ -230,12 +231,12 @@ \subsection{Data Structures}
230231
data structures for segments. We present several possibilities before
231232
comparing their performance.
232233

233-
\subsubsection{Ordered enumerations}
234+
\paragraph{Ordered enumerations}
234235

235236
Ordered enumerations, represented by thunk-lists, make
236237
for a light-weight set representation.
237-
To use an order, we require a comparison and an
238-
\code{append} function on words. The \code{OrderedMonoid} signature
238+
To use an order, we require \code{compare} and
239+
\code{append} on words. The \code{OrderedMonoid} signature
239240
captures these requirements. \autoref{code:thunklist} shows the
240241
resulting functor \code{ThunkList}.
241242

@@ -288,14 +289,15 @@ \subsubsection{Ordered enumerations}
288289
\label{code:thunklist}
289290
\end{figure}
290291

291-
\subsubsection{Transience and Memoization}
292+
\paragraph{Transience and Memoization}
292293

293294
During concatenation and star, we iterate over segments multiple times.
294295
As thunk lists are transient, iterating multiple times over the same list
295296
will compute it multiple times. To avoid this recomputation, we can implement memoization
296-
over thunk lists by pushing the elements in a growing vector as they are
297-
computed. Before evaluating a new thunk, we first check if it is already available
298-
in the vector. Otherwise, evaluate it, push it into the vector and return it.
297+
over thunk lists by using a growing vector as cache.
298+
% pushing the elements in a growing vector as they are
299+
% computed. Before evaluating a new thunk, we first check if it is already available
300+
% in the vector. Otherwise, evaluate it, push it into the vector and return it.
299301
% \begin{lstlisting}
300302
% let memoize f =
301303
% let r = CCVector.create () in
@@ -318,21 +320,21 @@ \subsubsection{Transience and Memoization}
318320
\code{ThunkList} where memoization is the identity and \code{ThunkListMemo}
319321
with the implementation described above.
320322

321-
\subsubsection{Lazy Lists}
323+
\paragraph{Lazy Lists}
322324

323-
\ocaml also supports regular lazy lists using the builtin \code{Lazy.t} type:
324-
325-
\begin{lstlisting}
326-
type 'a node =
327-
| Nil
328-
| Cons of 'a * 'a lazylist
329-
type 'a lazylist = 'a node Lazy.t
330-
\end{lstlisting}
331-
332-
We implemented a \code{LazyList} functor which is identical to the
325+
\ocaml also supports regular lazy lists using the builtin \code{Lazy.t} type.
326+
%
327+
% \begin{lstlisting}
328+
% type 'a node =
329+
% | Nil
330+
% | Cons of 'a * 'a lazylist
331+
% type 'a lazylist = 'a node Lazy.t
332+
% \end{lstlisting}
333+
%
334+
We implemented a \code{LazyList} functor which is identical to
333335
\code{ThunkList} but uses lazy lists.
334336

335-
\subsubsection{Strict Sets}
337+
\paragraph{Strict Sets}
336338

337339
As the main operations on segments are set operations, one might
338340
expect a set implementation to perform well. We implemented segments as sets
@@ -342,7 +344,7 @@ \subsubsection{Strict Sets}
342344
the n-way merge and the product.
343345
%, which can be implemented using folds and unions.
344346

345-
\subsubsection{Tries}
347+
\paragraph{Tries}
346348

347349
Tries \cite{Fredkin1960} are prefix trees where each branch is labeled
348350
with a character and each node may contain a value. Tries are commonly used

0 commit comments

Comments
 (0)