From fc9182428d0ee630696a93b698d93f101c067b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= <contact@tobast.fr> Date: Mon, 25 Sep 2023 17:00:07 +0200 Subject: [PATCH] CesASMe: brutal paper import. Not compiling yet. --- manuscrit/50_CesASMe/00_intro.tex | 141 ++++++++ manuscrit/50_CesASMe/05_related_works.tex | 56 +++ manuscrit/50_CesASMe/10_bench_gen.tex | 109 ++++++ manuscrit/50_CesASMe/15_harness.tex | 87 +++++ manuscrit/50_CesASMe/20_evaluation.tex | 213 +++++++++++ manuscrit/50_CesASMe/25_results_analysis.tex | 338 ++++++++++++++++++ manuscrit/50_CesASMe/30_future_works.tex | 106 ++++++ manuscrit/50_CesASMe/99_conclusion.tex | 2 + manuscrit/50_CesASMe/main.tex | 8 + manuscrit/50_CesASMe/overview.tex | 82 +++++ manuscrit/assets/imgs/50_CesASMe/.gitignore | 1 + .../imgs/50_CesASMe/nomemdeps_boxplot.pdf | Bin 0 -> 19306 bytes .../50_CesASMe/overall_analysis_boxplot.pdf | Bin 0 -> 24211 bytes .../50_CesASMe/results_comparability_hist.pdf | Bin 0 -> 15567 bytes 14 files changed, 1143 insertions(+) create mode 100644 manuscrit/50_CesASMe/00_intro.tex create mode 100644 manuscrit/50_CesASMe/05_related_works.tex create mode 100644 manuscrit/50_CesASMe/10_bench_gen.tex create mode 100644 manuscrit/50_CesASMe/15_harness.tex create mode 100644 manuscrit/50_CesASMe/20_evaluation.tex create mode 100644 manuscrit/50_CesASMe/25_results_analysis.tex create mode 100644 manuscrit/50_CesASMe/30_future_works.tex create mode 100644 manuscrit/50_CesASMe/99_conclusion.tex create mode 100644 manuscrit/50_CesASMe/overview.tex create mode 100644 manuscrit/assets/imgs/50_CesASMe/.gitignore create mode 100644 manuscrit/assets/imgs/50_CesASMe/nomemdeps_boxplot.pdf create mode 100644 manuscrit/assets/imgs/50_CesASMe/overall_analysis_boxplot.pdf create mode 100644 manuscrit/assets/imgs/50_CesASMe/results_comparability_hist.pdf diff --git a/manuscrit/50_CesASMe/00_intro.tex b/manuscrit/50_CesASMe/00_intro.tex new file mode 100644 index 0000000..8e70c60 --- /dev/null +++ b/manuscrit/50_CesASMe/00_intro.tex @@ -0,0 +1,141 @@ +\begin{abstract} + A variety of code analyzers, such as \iaca, \uica, \llvmmca{} or + \ithemal{}, strive to statically predict the throughput of a computation + kernel. Each analyzer is based on its own simplified CPU model + reasoning at the scale of an isolated basic block. + Facing this diversity, evaluating their strengths and + weaknesses is important to guide both their usage and their enhancement. + + We argue that reasoning at the scale of a single basic block is not + always sufficient and that a lack of context can mislead analyses. We present \tool, a fully-tooled + solution to evaluate code analyzers on C-level benchmarks. It is composed of a + benchmark derivation procedure that feeds an evaluation harness. We use it to + evaluate state-of-the-art code analyzers and to provide insights on their + precision. We use \tool's results to show that memory-carried data + dependencies are a major source of imprecision for these tools. +\end{abstract} + +\section{Introduction}\label{sec:intro} + +At a time when software is expected to perform more computations, faster and in +more constrained environments, tools that statically predict the resources (and +in particular the CPU resources) they consume are very useful to guide their +optimization. This need is reflected in the diversity of binary or assembly +code analyzers following the deprecation of \iaca~\cite{iaca}, which Intel has +maintained through 2019. Whether it is \llvmmca{}~\cite{llvm-mca}, +\uica{}~\cite{uica}, \ithemal~\cite{ithemal} or \gus~\cite{phd:gruber}, all these tools strive to extract various +performance metrics, including the number of CPU cycles a computation kernel will take +---~which roughly translates to execution time. +In addition to raw measurements (relying on hardware counters), these model-based analyses provide +higher-level and refined data, to expose the bottlenecks and guide the +optimization of a given code. This feedback is useful to experts optimizing +computation kernels, including scientific simulations and deep-learning +kernels. + +An exact throughput prediction would require a cycle-accurate simulator of the +processor, based on microarchitectural data that is most often not publicly +available, and would be prohibitively slow in any case. These tools thus each +solve in their own way the challenge of modeling complex CPUs while remaining +simple enough to yield a prediction in a reasonable time, ending up with +different models. For instance, on the following x86-64 basic block computing a +general matrix multiplication, +\begin{minipage}{0.95\linewidth} +\begin{lstlisting}[language={[x86masm]Assembler}] +movsd (%rcx, %rax), %xmm0 +mulsd %xmm1, %xmm0 +addsd (%rdx, %rax), %xmm0 +movsd %xmm0, (%rdx, %rax) +addq $8, %rax +cmpq $0x2260, %rax +jne 0x16e0 +\end{lstlisting} +\end{minipage} + +\noindent\llvmmca{} predicts 1.5 cycles, \iaca{} and \ithemal{} predict 2 cycles, while \uica{} +predicts 3 cycles. One may wonder which tool is correct. + + +The obvious solution to assess their predictions is to compare them to an +actual measure. However, as these tools reason at the basic block level, this +is not as trivially defined as it would seem. Take for instance the following +kernel: + +\begin{minipage}{0.90\linewidth} +\begin{lstlisting}[language={[x86masm]Assembler}] +mov (%rax, %rcx, 1), %r10 +mov %r10, (%rbx, %rcx, 1) +add $8, %rcx +\end{lstlisting} +\end{minipage} + +\input{overview} + +\noindent{}At first, it looks like an array copy from location \reg{rax} to +\reg{rbx}. Yet, if before the loop, \reg{rbx} is initialized to +\reg{rax}\texttt{+8}, there is a read-after-write dependency between the first +instruction and the second instruction at the previous iteration; which makes +the throughput drop significantly. As we shall see in +Section~\ref{ssec:bhive_errors}, \emph{without proper context, a basic +block's throughput is not well-defined}. + +To recover the context of each basic block, we reason instead at the scale of +a C source code. This +makes the measures unambiguous: one can use hardware counters to measure the +elapsed cycles during a loop nest. This requires a suite of benchmarks, in C, +that both is representative of the domain studied, and wide enough to have a +good coverage of the domain. However, this is not in itself sufficient to +evaluate static tools: on the preceding matrix multiplication kernel, counters +report 80,059 elapsed cycles ---~for the total loop. +This number compares hardly to \llvmmca{}, \iaca{}, \ithemal{}, and \uica{} +basic block-level predictions seen above. + +A common practice to make these numbers comparable is to renormalize them to +instructions per cycles (IPC). Here, \llvmmca{} reports an IPC of +$\frac{7}{1.5}~=~4.67$, \iaca{} and \ithemal{} report an IPC of +$\frac{7}{2}~=~3.5$, and \uica{} reports an IPC of $\frac{7}{3}~=~2.3$. In this +case, the measured IPC is 3.45, which is closest to \iaca{} and \ithemal. Yet, +IPC is a metric for microarchitectural load, and \textit{tells nothing about a +kernel's efficiency}. Indeed, the static number of instructions is affected by +many compiler passes, such as scalar evolution, strength reduction, register +allocation, instruction selection\ldots{} Thus, when comparing two compiled +versions of the same code, IPC alone does not necessarily point to the most +efficient version. For instance, a kernel using SIMD instructions will use +fewer instructions than one using only scalars, and thus exhibit a lower or +constant IPC; yet, its performance will unquestionably increase. + +The total cycles elapsed to solve a given problem, on the other +hand, is a sound metric of the efficiency of an implementation. We thus +instead \emph{lift} the predictions at basic-block level to a total number of +cycles. In simple cases, this simply means multiplying the block-level +prediction by the number of loop iterations; however, this bound might not +generally be known. More importantly, the compiler may apply any number of +transformations: unrolling, for instance, changes this number. Control flow may +also be complicated by code versioning. + +%In the general case, instrumenting the generated code to obtain the number of +%occurrences of the basic block yields accurate results. + +\bigskip + +In this article, we present a fully-tooled solution to evaluate and compare the +diversity of static throughput predictors. Our tool, \tool, solves two main +issues in this direction. In Section~\ref{sec:bench_gen}, we describe how +\tool{} generates a wide variety of computation kernels stressing different +parameters of the architecture, and thus of the predictors' models, while +staying close to representative workloads. To achieve this, we use +Polybench~\cite{polybench}, a C-level benchmark suite representative of +scientific computation workloads, that we combine with a variety of +optimisations, including polyhedral loop transformations. +In Section~\ref{sec:bench_harness}, we describe how \tool{} is able to +evaluate throughput predictors on this set of benchmarks by lifting their +predictions to a total number of cycles that can be compared to a hardware +counters-based measure. A +high-level view of \tool{} is shown in Figure~\ref{fig:contrib}. + +In Section~\ref{sec:exp_setup}, we detail our experimental setup and assess our +methodology. In Section~\ref{sec:results_analysis}, we compare the predictors' results and +analyze the results of \tool{}. + In addition to statistical studies, we use \tool's results +to investigate analyzers' flaws. We show that code +analyzers do not always correctly model data dependencies through memory +accesses, substantially impacting their precision. diff --git a/manuscrit/50_CesASMe/05_related_works.tex b/manuscrit/50_CesASMe/05_related_works.tex new file mode 100644 index 0000000..2abed1b --- /dev/null +++ b/manuscrit/50_CesASMe/05_related_works.tex @@ -0,0 +1,56 @@ +\section{Related works} + +The static throughput analyzers studied rely on a variety of models. +\iaca~\cite{iaca}, developed by Intel and now deprecated, is closed-source and +relies on Intel's expertise on their own processors. +The LLVM compiling ecosystem, to guide optimization passes, maintains models of many +architectures. These models are used in the LLVM Machine Code Analyzer, +\llvmmca~\cite{llvm-mca}, to statically evaluate the performance of a segment +of assembly. +Independently, Abel and Reineke used an automated microbenchmark generation +approach to generate port mappings of many architectures in +\texttt{uops.info}~\cite{nanobench, uopsinfo} to model processors' backends. +This work was continued with \uica~\cite{uica}, extending this model with an +extensive frontend description. +Following a completely different approach, \ithemal~\cite{ithemal} uses a deep +neural network to predict basic blocks throughput. To obtain enough data to +train its model, the authors also developed \bhive~\cite{bhive}, a profiling +tool working on basic blocks. + +Another static tool, \osaca~\cite{osaca2}, provides lower- and +upper-bounds to the execution time of a basic block. As this kind of +information cannot be fairly compared with tools yielding an exact throughput +prediction, we exclude it from our scope. + +All these tools statically predict the number of cycles taken by a piece of +assembly or binary that is assumed to be the body of an infinite ---~or +sufficiently large~--- loop in steady state, all its data being L1-resident. As +discussed by \uica's authors~\cite{uica}, hypotheses can subtly vary between +analyzers; \eg{} by assuming that the loop is either unrolled or has control +instructions, with non-negligible impact. Some of the tools, \eg{} \ithemal, +necessarily work on a single basic block, while some others, \eg{} \iaca, work +on a section of code delimited by markers. However, even in the second case, +the code is assumed to be \emph{straight-line code}: branch instructions, if +any, are assumed not taken. + +\smallskip + +Throughput prediction tools, however, are not all static. +\gus~\cite{phd:gruber} dynamically predicts the throughput of a whole program +region, instrumenting it to retrieve the exact events occurring through its +execution. This way, \gus{} can more finely detect bottlenecks by +sensitivity analysis, at the cost of a significantly longer run time. + +\smallskip + +The \bhive{} profiler~\cite{bhive} takes another approach to basic block +throughput measurement: by mapping memory at any address accessed by a basic +block, it can effectively run and measure arbitrary code without context, often +---~but not always, as we discuss later~--- yielding good results. + +\smallskip + +The \anica{} framework~\cite{anica} also attempts to evaluate throughput +predictors by finding examples on which they are inaccurate. \anica{} starts +with randomly generated assembly snippets, and refines them through a process +derived from abstract interpretation to reach general categories of problems. diff --git a/manuscrit/50_CesASMe/10_bench_gen.tex b/manuscrit/50_CesASMe/10_bench_gen.tex new file mode 100644 index 0000000..c2e0c23 --- /dev/null +++ b/manuscrit/50_CesASMe/10_bench_gen.tex @@ -0,0 +1,109 @@ +\section{Generating microbenchmarks}\label{sec:bench_gen} + +Our framework aims to generate \emph{microbenchmarks} relevant to a specific +domain. +A microbenchmark is a code that is as simplified as possible to expose the +behaviour under consideration. +The specified computations should be representative of the considered domain, +and at the same time they should stress the different aspects of the +target architecture ---~which is modeled by code analyzers. + +In practice, a microbenchmark's \textit{computational kernel} is a simple +\texttt{for} loop, whose +body contains no loops and whose bounds are statically known. +A \emph{measure} is a number of repetitions $n$ of this computational +kernel, $n$ being an user-specified parameter. +The measure may be repeated an arbitrary number of times to improve +stability. + +Furthermore, such a microbenchmark should be a function whose computation +happens without leaving the L1 cache. +This requirement helps measurements and analyses to be +undisturbed by memory accesses, but it is also a matter of comparability. +Indeed, most of the static analyzers make the assumption that the code under +consideration is L1-resident; if it is not, their results are meaningless, and +can not be compared with an actual measurement. + +The generation of such microbenchmarks is achieved through four distinct +components, whose parameter variations are specified in configuration files~: +a benchmark suite, C-to-C loop nest optimizers, a constraining utility +and a C-to-binary compiler. + +\subsection{Benchmark suite}\label{ssec:bench_suite} +Our first component is an initial set of benchmarks which materializes +the human expertise we intend to exploit for the generation of relevant codes. +The considered suite must embed computation kernels +delimited by ad-hoc \texttt{\#pragma}s, +whose arrays are accessed +directly (no indirections) and whose loops are affine. +These constraints are necessary to ensure that the microkernelification phase, +presented below, generates segfault-free code. + +In this case, we use Polybench~\cite{polybench}, a suite of 30 +benchmarks for polyhedral compilation ---~of which we use only 26. The +\texttt{nussinov}, \texttt{ludcmp} and \texttt{deriche} benchmarks are +removed because they are incompatible with PoCC (introduced below). The +\texttt{lu} benchmark is left out as its execution alone takes longer than all +others together, making its dynamic analysis (\eg{} with \gus) impractical. +In addition to the importance of linear algebra within +it, one of its important features is that it does not include computational +kernels with conditional control flow (\eg{} \texttt{if-then-else}) +---~however, it does includes conditional data flow, using the ternary +conditional operator of C. + +\subsection{C-to-C loop nest optimizers}\label{ssec:loop_nest_optimizer} +Loop nest optimizers transform an initial benchmark in different ways (generate different +\textit{versions} of the same benchmark), varying the stress on +resources of the target architecture, and by extension the models on which the +static analyzers are based. + +In this case, we chose to use the +\textsc{Pluto}~\cite{pluto} and PoCC~\cite{pocc} polyhedral compilers, to easily access common loop nest optimizations~: register tiling, tiling, +skewing, vectorization/simdization, loop unrolling, loop permutation, +loop fusion. +These transformations are meant to maximize variety within the initial +benchmark suite. Eventually, the generated benchmarks are expected to +highlight the impact on performance of the resulting behaviours. +For instance, \textit{skewing} introduces non-trivial pointer arithmetics, +increasing the pressure on address computation units~; \textit{loop unrolling}, +among many things, opens the way to register promotion, which exposes dependencies +and alleviates load-store units~; +\textit{vectorization} stresses SIMD units and decreases +pressure on the front-end~; and so on. + +\subsection{Constraining utility}\label{ssec:kernelify} + +A constraining utility transforms the code in order to respect an arbitrary number of non-functional +properties. +In this case, we apply a pass of \emph{microkernelification}: we +extract a computational kernel from the arbitrarily deep and arbitrarily +long loop nest generated by the previous component. +The loop chosen to form the microkernel is the one considered to be +the \textit{hottest}; the \textit{hotness} of a loop being obtained by +multiplying the number of arithmetic operations it contains by the number of +times it is iterated. This metric allows us to prioritize the parts of the +code that have the greatest impact on performance. + +At this point, the resulting code can +compute a different result from the initial code; +for instance, the composition of tiling and +kernelification reduces the number of loop iterations. +Indeed, our framework is not meant to preserve the +functional semantics of the benchmarks. +Our goal is only to generate codes that are relevant from the point of view of +performance analysis. + +\subsection{C-to-binary compiler}\label{ssec:compile} + +A C-to-binary compiler varies binary optimization options by +enabling/disabling auto-vectorization, extended instruction +sets, \textit{etc}. We use \texttt{gcc}. + +\bigskip + +Eventually, the relevance of the microbenchmarks set generated using this approach +derives not only from initial benchmark suite and the relevance of the +transformations chosen at each +stage, but also from the combinatorial explosion generated by the composition +of the four stages. In our experimental setup, this yields up to 144 +microbenchmarks per benchmark of the original suite. diff --git a/manuscrit/50_CesASMe/15_harness.tex b/manuscrit/50_CesASMe/15_harness.tex new file mode 100644 index 0000000..da55ae7 --- /dev/null +++ b/manuscrit/50_CesASMe/15_harness.tex @@ -0,0 +1,87 @@ +\section{Benchmarking harness}\label{sec:bench_harness} + +To compare full-kernel cycle measurements to throughput predictions on +individual basic blocks, we lift predictions by adding the weighted basic block +predictions: + +\[ +\text{lifted\_pred}(\mathcal{K}) = + \sum_{b \in \operatorname{BBs}(\mathcal{K})} + \operatorname{occurences}(b) \times \operatorname{pred}(b) +\] + +Our benchmarking harness works in three successive stages. It first +extracts the basic blocks constituting a computation kernel, and instruments it +to retrieve their respective occurrences in the original context. It then runs +all the studied tools on each basic block, while also running measures on the +whole computation kernel. Finally, the block-level results are lifted to +kernel-level results thanks to the occurrences previously measured. + +\subsection{Basic block extraction}\label{ssec:bb_extr} + +Using the Capstone disassembler~\cite{tool:capstone}, we split the assembly +code at each control flow instruction (jump, call, return, \ldots) and each +jump site. + +To accurately obtain the occurrences of each basic block in the whole kernel's +computation, +we then instrument it with \texttt{gdb} by placing a break +point at each basic block's first instruction in order to count the occurrences +of each basic block between two calls to the \perf{} counters\footnote{We +assume the program under analysis to be deterministic.}. While this +instrumentation takes about 50 to 100$\times$ more time than a regular run, it +can safely be run in parallel, as the performance results are discarded. + +\subsection{Throughput predictions and measures}\label{ssec:throughput_pred_meas} + +The harness leverages a variety of tools: actual CPU measurement; the \bhive{} +basic block profiler~\cite{bhive}; \llvmmca~\cite{llvm-mca}, \uica~\cite{uica} +and \iaca~\cite{iaca}, which leverage microarchitectural +models to predict a block's throughput; \ithemal~\cite{ithemal}, a machine +learning model; and \gus~\cite{phd:gruber}, a dynamic analyzer based on \qemu{} +that works at the whole binary level. + +The execution time of the full kernel is measured using Linux +\perf~\cite{tool:perf} CPU counters around the full computation kernel. The +measure is repeated four times and the smallest is kept; this ensures that the +cache is warm and compensates for context switching or other measurement +artifacts. \gus{} instruments the whole function body. The other tools included +all work at basic block level; these are run on each basic block of each +benchmark. + +We emphasize the importance, throughout the whole evaluation chain, to keep the +exact same assembled binary. Indeed, recompiling the kernel from source +\emph{cannot} be assumed to produce the same assembly kernel. This is even more +important in the presence of slight changes: for instance, inserting \iaca{} +markers at the C-level ---~as is intended~--- around the kernel \emph{might} +change the compiled kernel, if only for alignment regions. We argue that, in +the case of \iaca{} markers, the problem is even more critical, as those +markers prevent a binary from being run by overwriting registers with arbitrary +values. This forces a user to run and measure a version which is different from +the analyzed one. In our harness, we circumvent this issue by adding markers +directly at the assembly level, editing the already compiled version. Our +\texttt{gdb} instrumentation procedure also respects this principle of +single-compilation. As \qemu{} breaks the \perf{} interface, we have to run +\gus{} with a preloaded stub shared library to be able to instrument binaries +containing calls to \perf. + +\subsection{Prediction lifting and filtering}\label{ssec:harness_lifting} + +We finally lift single basic block predictions to a whole-kernel cycle +prediction by summing the block-level results, weighted by the occurrences of +the basic block in the original context (formula above). If an analyzer fails +on one of the basic blocks of a benchmark, the whole benchmark is discarded for +this analyzer. + +In the presence of complex control flow, \eg{} with conditionals inside loops, +our approach based on basic block occurrences is arguably less precise than an +approach based on paths occurrences, as we have less information available +---~for instance, whether a branch is taken with a regular pattern, whether we +have constraints on register values, etc. We however chose this block-based +approach, as most throughput prediction tools work a basic block-level, and are +thus readily available and can be directly plugged into our harness. + +Finally, we control the proportion of cache misses in the program's execution +using \texttt{Cachegrind}~\cite{valgrind} and \gus; programs that have more +than 15\,\% of cache misses on a warm cache are not considered L1-resident and +are discarded. diff --git a/manuscrit/50_CesASMe/20_evaluation.tex b/manuscrit/50_CesASMe/20_evaluation.tex new file mode 100644 index 0000000..755fe76 --- /dev/null +++ b/manuscrit/50_CesASMe/20_evaluation.tex @@ -0,0 +1,213 @@ +\section{Experimental setup and evaluation}\label{sec:exp_setup} + +Running the harness described above provides us with 3500 +benchmarks ---~after filtering out non-L1-resident +benchmarks~---, on which each throughput predictor is run. We make the full +output of our tool available in our artifact. Before analyzing these results in +Section~\ref{sec:results_analysis}, we evaluate the relevance of the +methodology presented in Section~\ref{sec:bench_harness} to make the tools' +predictions comparable to baseline hardware counter measures. + +\subsection{Experimental environment} + +The experiments presented in this paper were all realized on a Dell PowerEdge +C6420 machine from the Grid5000 cluster~\cite{grid5000}, equipped with 192\,GB +of DDR4 SDRAM ---~only a small fraction of which was used~--- and two Intel +Xeon Gold 6130 CPUs (x86-64, Skylake microarchitecture) with 16 cores each. + +The experiments themselves were run inside a Docker environment very close to +our artifact, based on Debian Bullseye. Care was taken to disable +hyperthreading to improve measurements stability. For tools whose output is +based on a direct measurement (\perf, \bhive), the benchmarks were run +sequentially on a single core with no experiments on the other cores. No such +care was taken for \gus{} as, although based on a dynamic run, its prediction +is purely function of recorded program events and not of program measures. All +other tools were run in parallel. + +We use \llvmmca{} \texttt{v13.0.1}, \iaca{} \texttt{v3.0-28-g1ba2cbb}, \bhive{} +at commit \texttt{5f1d500}, \uica{} at commit \texttt{9cbbe93}, \gus{} at +commit \texttt{87463c9}, \ithemal{} at commit \texttt{b3c39a8}. + +\subsection{Comparability of the results} + +We define the relative error of a time prediction +$C_\text{pred}$ (in cycles) with respect to a baseline $C_\text{baseline}$ as +\[ + \operatorname{err} = \frac{\left| C_\text{pred} - C_\text{baseline} + \right|}{C_\text{baseline}} +\] + +We assess the comparability of the whole benchmark, measured with \perf{}, to +lifted block-based results by measuring the statistical distribution of the +relative error of two series: the predictions made by \bhive, and the series of +the best block-based prediction for each benchmark. + +We single out \bhive{} as it is the only tool able to \textit{measure} +---~instead of predicting~--- an isolated basic block's timing. This, however, is +not sufficient: as discussed later in Section~\ref{ssec:bhive_errors}, \bhive{} +is not able to yield a result for about $40\,\%$ of the benchmarks, and is +subject to large errors in some cases. For this purpose, we also consider, for +each benchmark, the best block-based prediction: we argue that if, for most +benchmarks, at least one of these predictors is able to yield a satisfyingly +accurate result, then the lifting methodology is sound in practice. + +The result of this analysis is presented in Table~\ref{table:exp_comparability} +and in Figure~\ref{fig:exp_comparability}. The results are in a range +compatible with common results of the field, as seen \eg{} in~\cite{uica} +reporting Mean Absolute Percentage Error (MAPE, corresponding to the +``Average'' row) of about 10-15\,\% in many cases. While lifted \bhive's +average error is driven high by large errors on certain benchmarks, +investigated later in this article, its median error is still comparable to the +errors of state-of-the-art tools. From this, we conclude that lifted cycle +measures and predictions are consistent with whole-benchmark measures; and +consequently, lifted predictions can reasonably be compared to one another. + +\begin{figure} + \centering + \includegraphics[width=\linewidth]{figs/results_comparability_hist.pdf} + \caption{Relative error distribution \wrt{} \perf}\label{fig:exp_comparability} +\end{figure} + +\begin{table} + \centering + \caption{Relative error statistics \wrt{} \perf}\label{table:exp_comparability} + \begin{tabular}{l r r} + \toprule + & \textbf{Best block-based} & \textbf{BHive} \\ + \midrule + Datapoints & 3500 & 2198 \\ + Errors & 0 & 1302 \\ + & (0\,\%) & (37.20\,\%) \\ + Average (\%) & 11.60 & 27.95 \\ + Median (\%) & 5.81 & 7.78 \\ + Q1 (\%) & 1.99 & 3.01 \\ + Q3 (\%) & 15.41 & 23.01 \\ + \bottomrule + \end{tabular} +\end{table} + + +\begin{table*}[!htbp] + \centering + \caption{Bottleneck reports from the studied tools}\label{table:coverage} + + \begin{tabular}{l | r r r | r r r | r r r} + \toprule + & \multicolumn{3}{c|}{\textbf{Frontend}} + & \multicolumn{3}{c|}{\textbf{Ports}} + & \multicolumn{3}{c}{\textbf{Dependencies}} \\ + & \textbf{yes} & \textbf{no} & \textbf{disagr.} & \textbf{yes} & \textbf{no} & \textbf{disagr.} & \textbf{yes} & \textbf{no} & \textbf{disagr.} \\ + + \midrule +2mm & 34 & 61 & 25.8 \% & 25 & 13 & 70.3 \% & 18 & 29 & 63.3 \% \\ +3mm & 44 & 61 & 18.0 \% & 30 & 13 & 66.4 \% & 23 & 37 & 53.1 \% \\ +atax & 13 & 72 & 41.0 \% & 25 & 17 & 70.8 \% & 23 & 30 & 63.2 \% \\ +bicg & 19 & 59 & 45.8 \% & 25 & 25 & 65.3 \% & 21 & 37 & 59.7 \% \\ +doitgen & 51 & 25 & 40.6 \% & 36 & 30 & 48.4 \% & 17 & 22 & 69.5 \% \\ +mvt & 27 & 53 & 33.3 \% & 9 & 18 & 77.5 \% & 7 & 32 & 67.5 \% \\ +gemver & 62 & 13 & 39.5 \% & 2 & 48 & 59.7 \% & 1 & 28 & 76.6 \% \\ +gesummv & 16 & 69 & 41.0 \% & 17 & 23 & 72.2 \% & 24 & 28 & 63.9 \% \\ +syr2k & 51 & 37 & 38.9 \% & 8 & 42 & 65.3 \% & 19 & 34 & 63.2 \% \\ +trmm & 69 & 27 & 25.0 \% & 16 & 30 & 64.1 \% & 15 & 30 & 64.8 \% \\ +symm & 0 & 121 & 11.0 \% & 5 & 20 & 81.6 \% & 9 & 5 & 89.7 \% \\ +syrk & 54 & 46 & 30.6 \% & 12 & 42 & 62.5 \% & 20 & 48 & 52.8 \% \\ +gemm & 42 & 41 & 42.4 \% & 30 & 41 & 50.7 \% & 16 & 57 & 49.3 \% \\ +gramschmidt & 48 & 52 & 21.9 \% & 16 & 20 & 71.9 \% & 24 & 39 & 50.8 \% \\ +cholesky & 24 & 72 & 33.3 \% & 0 & 19 & 86.8 \% & 5 & 14 & 86.8 \% \\ +durbin & 49 & 52 & 29.9 \% & 0 & 65 & 54.9 \% & 2 & 39 & 71.5 \% \\ +trisolv & 53 & 84 & 4.9 \% & 6 & 22 & 80.6 \% & 4 & 16 & 86.1 \% \\ +jacobi-1d & 18 & 78 & 33.3 \% & 66 & 9 & 47.9 \% & 0 & 13 & 91.0 \% \\ +heat-3d & 32 & 8 & 72.2 \% & 26 & 0 & 81.9 \% & 0 & 0 & 100.0 \% \\ +seidel-2d & 0 & 112 & 22.2 \% & 32 & 0 & 77.8 \% & 0 & 0 & 100.0 \% \\ +fdtd-2d & 52 & 22 & 47.1 \% & 20 & 41 & 56.4 \% & 0 & 40 & 71.4 \% \\ +jacobi-2d & 6 & 31 & 73.6 \% & 24 & 61 & 39.3 \% & 0 & 44 & 68.6 \% \\ +adi & 12 & 76 & 21.4 \% & 40 & 0 & 64.3 \% & 0 & 0 & 100.0 \% \\ +correlation & 18 & 36 & 51.8 \% & 19 & 30 & 56.2 \% & 23 & 45 & 39.3 \% \\ +covariance & 39 & 36 & 37.5 \% & 4 & 34 & 68.3 \% & 19 & 53 & 40.0 \% \\ +floyd-warshall & 74 & 16 & 29.7 \% & 16 & 24 & 68.8 \% & 20 & 8 & 78.1 \% \\ +\textbf{Total} & 907 & 1360 & 35.2 \% & 509 & 687 & 65.8 \% & 310 & 728 & 70.3 \% \\ +\bottomrule + \end{tabular} +\end{table*} + +\subsection{Relevance and representativity (bottleneck +analysis)}\label{ssec:bottleneck_diversity} + +The results provided by our harness are only relevant to evaluate the parts of +the tools' models that are stressed by the benchmarks generated; it is hence +critical that our benchmark generation procedure in Section~\ref{sec:bench_gen} +yields diverse results. This should be true by construction, as the various +polyhedral compilation techniques used stress different parts of the +microarchitecture. + +To assess this, we study the generated benchmarks' bottlenecks, \ie{} +architectural resources on which a release of pressure improves execution time. +Note that a saturated resource is not necessarily a bottleneck: a code that +uses \eg{} 100\,\% of the arithmetics units available for computations outside +of the critical path, at a point where a chain of dependencies is blocking, +will not run faster if the arithmetics operations are removed; hence, hardware +counters alone are not sufficient to find bottlenecks. + +However, some static analyzers report the bottlenecks they detect. To unify +their results and keep things simple, we study three general kinds of +bottlenecks. + +\begin{itemize} +\item{} \emph{Frontend:} the CPU's frontend is not able to issue + micro-operations to the backend fast enough. \iaca{} and \uica{} are + able to detect this. +\item{} \emph{Ports:} at least one of the backend ports has too much work; + reducing its pressure would accelerate the computation. + \llvmmca, \iaca{} and \uica{} are able to detect this. +\item{} \emph{Dependencies:} there is a chain of data dependencies slowing + down the computation. + \llvmmca, \iaca{} and \uica{} are able to detect this. +\end{itemize} + +For each source benchmark from Polybench and each type of bottleneck, we report +in Table~\ref{table:coverage} the number of derived benchmarks on which all the +tools agree that the bottleneck is present or absent. We also report the +proportion of cases in which the tools failed to agree. We analyze those +results later in Section~\ref{ssec:bottleneck_pred_analysis}. + +As we have no source of truth indicating whether a bottleneck is effectively +present in a microbenchmark, we adopt a conservative approach, and consider +only the subset of the microbenchmarks on which the tools agree on the status +of all three resources; for those, we have a good confidence on the bottlenecks +reported. Obviously, this approach is limited, because it excludes +microbenchmarks that might be worth considering, and is most probably subject +to selection bias. + +Of the 3,500 microbenchmarks we have generated, 261 (7.5\,\%) are the subject +of the above-mentioned consensus. This sample is made up of microbenchmarks +generated from 21 benchmarks ---~\ie{} for 5 benchmarks, none of the derived +microbenchmarks reached a consensus among the tools~---, yielding a wide +variety of calculations, including floating-point arithmetic, pointer +arithmetic or Boolean arithmetic. Of these, 200 (76.6\,\%) are bottlenecked on +the CPU front-end, 19 (7,3\,\%) on back-end ports, and 81 (31.0\,\%) on latency +introduced by dependencies. As mentioned above, this distribution +probably does not transcribe the distribution among the 3,500 original +benchmarks, as the 261 were not uniformly sampled. However, we argue that, as +all categories are represented in the sample, the initial hypothesis that the +generated benchmarks are diverse and representative is confirmed ---~thanks to +the transformations described in Section~\ref{sec:bench_gen}. + +\subsection{Carbon footprint} + +Generating and running the full suite of benchmarks required about 30h of +continuous computation on a single machine. During the experiments, the power +supply units reported a near-constant consumption of about 350W. The carbon +intensity of the power grid for the region where the experiment was run, at the +time of the experiments, was of about 29g\coeq/kWh~\cite{electricitymaps}. + +The electricity consumed directly by the server thus amounts to about +10.50\,kWh. Assuming a Power Usage Efficiency of 1.5, the total electricity +consumption roughly amounts to 15.75\,kWh, or about 450\,g\coeq. + +A carbon footprint estimate of the machine's manufacture itself was conducted +by the manufacturer~\cite{poweredgeC6420lca}. Additionally accounting for the +extra 160\,GB of DDR4 SDRAM~\cite{meta_ACT}, the hardware manufacturing, +transport and end-of-life is evaluated to 1,266\,kg\coeq. In 2023, this +computation cluster's usage rate was 35\,\%. Assuming 6 years of product life, +30h of usage represents about 2,050\,g\coeq{}. The whole experiment thus amounts to +2.5\,kg\coeq. diff --git a/manuscrit/50_CesASMe/25_results_analysis.tex b/manuscrit/50_CesASMe/25_results_analysis.tex new file mode 100644 index 0000000..5a37fe2 --- /dev/null +++ b/manuscrit/50_CesASMe/25_results_analysis.tex @@ -0,0 +1,338 @@ +\section{Results analysis}\label{sec:results_analysis} + +The raw complete output from our benchmarking harness ---~roughly speaking, a +large table with, for each benchmark, a cycle measurement, cycle count for each +throughput analyzer, the resulting relative error, and a synthesis of the +bottlenecks reported by each tool~--- enables many analyses that, we believe, +could be useful both to throughput analysis tool developers and users. Tool +designers can draw insights on their tool's best strengths and weaknesses, and +work towards improving them with a clearer vision. Users can gain a better +understanding of which tool is more suited for each situation. + +\subsection{Throughput results}\label{ssec:overall_results} + +\begin{table*} + \centering + \caption{Statistical analysis of overall results}\label{table:overall_analysis_stats} + \begin{tabular}{l r r r r r r r r r} + \toprule +\textbf{Bencher} & \textbf{Datapoints} & \textbf{Failures} & \textbf{(\%)} & +\textbf{MAPE} & \textbf{Median} & \textbf{Q1} & \textbf{Q3} & \textbf{K. tau} & \textbf{Time (CPU$\cdot$h)}\\ +\midrule +BHive & 2198 & 1302 & (37.20\,\%) & 27.95\,\% & 7.78\,\% & 3.01\,\% & 23.01\,\% & 0.81 & 1.37\\ +llvm-mca & 3500 & 0 & (0.00\,\%) & 36.71\,\% & 27.80\,\% & 12.92\,\% & 59.80\,\% & 0.57 & 0.96 \\ +UiCA & 3500 & 0 & (0.00\,\%) & 29.59\,\% & 18.26\,\% & 7.11\,\% & 52.99\,\% & 0.58 & 2.12 \\ +Ithemal & 3500 & 0 & (0.00\,\%) & 57.04\,\% & 48.70\,\% & 22.92\,\% & 75.69\,\% & 0.39 & 0.38 \\ +Iaca & 3500 & 0 & (0.00\,\%) & 30.23\,\% & 18.51\,\% & 7.13\,\% & 57.18\,\% & 0.59 & 1.31 \\ +Gus & 3500 & 0 & (0.00\,\%) & 20.37\,\% & 15.01\,\% & 7.82\,\% & 30.59\,\% & 0.82 & 188.04 \\ +\bottomrule + \end{tabular} +\end{table*} + +The error distribution of the relative errors, for each tool, is presented as a +box plot in Figure~\ref{fig:overall_analysis_boxplot}. Statistical indicators +are also given in Table~\ref{table:overall_analysis_stats}. We also give, for +each tool, its Kendall's tau indicator~\cite{kendall1938tau}: this indicator, +used to evaluate \eg{} uiCA~\cite{uica} and Palmed~\cite{palmed}, measures how +well the pair-wise ordering of benchmarks is preserved, $-1$ being a full +anti-correlation and $1$ a full correlation. This is especially useful when one +is not interested in a program's absolute throughput, but rather in comparing +which program has a better throughput. + +\begin{figure} + \includegraphics[width=\linewidth]{figs/overall_analysis_boxplot.pdf} + \caption{Statistical distribution of relative errors}\label{fig:overall_analysis_boxplot} +\end{figure} + + +These results are, overall, significantly worse than what each tool's article +presents. We attribute this difference mostly to the specificities of +Polybench: being composed of computation kernels, it intrinsically stresses the +CPU more than basic blocks extracted out of the Spec benchmark suite. This +difference is clearly reflected in the experimental section of the Palmed +article~\cite{palmed}: the accuracy of most tools is worse on Polybench than on +Spec, often by more than a factor of two. + +As \bhive{} and \ithemal{} do not support control flow instructions +(\eg{} \texttt{jump} instructions), those had +to be removed from the blocks before analysis. While none of these tools, apart +from \gus{} ---~which is dynamic~---, is able to account for branching costs, +these two analyzers were also unable to account for the front- and backend cost +of the control flow instructions themselves as well ---~corresponding to the +$TP_U$ mode introduced by \uica~\cite{uica}, while others +measure $TP_L$. + + +\subsection{Understanding \bhive's results}\label{ssec:bhive_errors} + +The error distribution of \bhive{} against \perf{}, plotted right in +Figure~\ref{fig:exp_comparability}, puts forward irregularities in \bhive's +results. Since \bhive{} is based on measures ---~instead of predictions~--- +through hardware counters, an excellent accuracy is expected. Its lack of +support for control flow instructions can be held accountable for a portion of +this accuracy drop; our lifting method, based on block occurrences instead of +paths, can explain another portion. We also find that \bhive{} fails to produce +a result in about 40\,\% of the kernels explored ---~which means that, for those +cases, \bhive{} failed to produce a result on at least one of the constituent +basic blocks. In fact, this is due to the difficulties we mentioned in +Section \ref{sec:intro} related to the need to reconstruct the context of each +basic block \textit{ex nihilo}. + +The basis of \bhive's method is to run the code to be measured, unrolled a +number of times depending on the code size, with all memory pages but the +code unmapped. As the code tries to access memory, it will raise segfaults, +caught by \bhive's harness, which allocates a single shared-memory page, filled +with a repeated constant, that it will map wherever segfaults occur before +restarting the program. +The main causes of \bhive{} failure are bad code behaviour (\eg{} control flow +not reaching the exit point of the measure if a bad jump is inserted), too many +segfaults to be handled, or a segfault that occurs even after mapping a page at +the problematic address. + +The registers are also initialized, at the beginning of the measurement, to the +fixed constant \texttt{0x2324000}. We show through two examples that this +initial value can be of crucial importance. + +The following experiments are executed on an Intel(R) Xeon(R) Gold 6230R CPU +(Cascade Lake), with hyperthreading disabled. + +\paragraph{Imprecise analysis} we consider the following x86-64 kernel. + +\begin{minipage}{0.95\linewidth} +\begin{lstlisting}[language={[x86masm]Assembler}] + vmulsd (%rax), %xmm3, %xmm0 + vmovsd %xmm0, (%r10) +\end{lstlisting} +\end{minipage} + +When executed with all the general purpose registers initialized to the default +constant, \bhive{} reports 9 cycles per iteration, since \reg{rax} and +\reg{r10} hold the same value, inducing a read-after-write dependency between +the two instructions. If, however, \bhive{} is tweaked to initialize \reg{r10} +to a value that aliases (\wrt{} physical addresses) with the value in +\reg{rax}, \eg{} between \texttt{0x10000} and \texttt{0x10007} (inclusive), it +reports 19 cycles per iteration instead; while a value between \texttt{0x10008} +and \texttt{0x1009f} (inclusive) yields the expected 1 cycle ---~except for +values in \texttt{0x10039}-\texttt{0x1003f} and +\texttt{0x10079}-\texttt{0x1007f}, yielding 2 cycles as the store crosses a +cache line boundary. + +In the same way, the value used to initialize the shared memory page can +influence the results whenever it gets loaded into registers. + +\vspace{0.5em} + +\paragraph{Failed analysis} some memory accesses will always result in an +error; for instance, it is impossible to \texttt{mmap} at an address lower +than \texttt{/proc/sys/vm/mmap\_min\_addr}, defaulting to \texttt{0x10000}. Thus, +with equal initial values for all registers, the following kernel would fail, +since the second operation attempts to load at address 0: + +\begin{minipage}{0.95\linewidth} +\begin{lstlisting}[language={[x86masm]Assembler}] + subq %r11, %r10 + movq (%r10), %rax +\end{lstlisting} +\end{minipage} + +Such errors can occur in more circumvoluted ways. The following x86-64 kernel, +for instance, is extracted from a version of the \texttt{durbin} +kernel\footnote{\texttt{durbin.pocc.noopt.default.unroll8.MEDIUM.kernel21.s} +in the full results}. + +\begin{minipage}{0.95\linewidth} +\begin{lstlisting}[language={[x86masm]Assembler}] + vmovsd 0x10(%r8, %rcx), %xmm6 + subl %eax, %esi + movslq %esi, %rsi + vfmadd231sd -8(%r9, %rsi, 8), \ + %xmm6, %xmm0 +\end{lstlisting} +\end{minipage} + +Here, \bhive{} fails to measure the kernel when run with the general purpose +registers initialized to the default constant at the 2\textsuperscript{nd} +occurrence of the unrolled loop body, failing to recover from an error at the +\texttt{vfmadd231sd} instruction with the \texttt{mmap} strategy. Indeed, after +the first iteration the value in \reg{rsi} becomes null, then negative at the +second iteration; thus, the second occurrence of the last instruction fetches +at address \texttt{0xfffffffff0a03ff8}, which is in kernel space. This +microkernel can be benchmarked with BHive \eg{} by initializing \reg{rax} to 1. + +Some other microkernels fail in a similar way when trying to access addresses +that are not a virtual address in \emph{canonical form} space for x86-64 with +48 bits virtual addresses, as defined in Section~3.3.7.1 of Intel's Software +Developer's Manual~\cite{ref:intel64_software_dev_reference_vol1} and +Section~5.3.1 of the AMD64 Architecture Programmer's +Manual~\cite{ref:amd64_architecture_dev_reference_vol2}. Others still fail with +accesses relative to the instruction pointer, as \bhive{} read-protects the +unrolled microkernel's instructions page. + +\subsection{Bottleneck prediction}\label{ssec:bottleneck_pred_analysis} + +We introduced in Section~\ref{ssec:bottleneck_diversity} earlier that some of +the tools studied are also able to report suspected bottlenecks for the +evaluated program, whose results are presented in Table~\ref{table:coverage}. +This feature might be even more useful than raw throughput predictions to the +users of these tools willing to optimize their program, as they strongly hint +towards what needs to be enhanced. + +In the majority of the cases studied, the tools are not able to agree on the +presence or absence of a type of bottleneck. Although it might seem that the +tools are performing better on frontend bottleneck detection, it must be +recalled that only two tools (versus three in the other cases) are reporting +frontend bottlenecks, thus making it easier for them to agree. + +\begin{table} + \centering + \caption{Diverging bottleneck prediction per tool}\label{table:bottleneck_diverging_pred} + \begin{tabular}{l r r r r} + \toprule + \textbf{Tool} + & \multicolumn{2}{c}{\textbf{Ports}} + & \multicolumn{2}{c}{\textbf{Dependencies}} \\ + \midrule + \llvmmca{} & 567 & (24.6 \%) & 1032 & (41.9 \%) \\ + \uica{} & 516 & (22.4 \%) & 530 & (21.5 \%) \\ + \iaca{} & 1221 & (53.0 \%) & 900 & (36.6 \%) \\ + \bottomrule + \end{tabular} +\end{table} + +The Table~\ref{table:bottleneck_diverging_pred}, in turn, breaks down the cases +on which three tools disagree into the number of times one tool makes a +diverging prediction ---~\ie{} the tool predicts differently than the two +others. In the case of ports, \iaca{} is responsible for half of the +divergences ---~which is not sufficient to conclude that the prediction of the +other tools is correct. In the case of dependencies, however, there is no clear +outlier, even though \uica{} seems to fare better than others. + +In no case one tool seems to be responsible for the vast majority of +disagreements, which could hint towards it failing to predict correctly this +bottleneck. In the absence of a source of truth indicating whether a bottleneck +is effectively present, and with no clear-cut result for (a subset of) tool +predictions, we cannot conclude on the quality of the predictions from each +tool for each kind of bottleneck. + +\subsection{Impact of dependency-boundness}\label{ssec:memlatbound} + +\begin{table*} + \centering + \caption{Statistical analysis of overall results, without latency bound + through memory-carried dependencies rows}\label{table:nomemdeps_stats} + \begin{tabular}{l r r r r r r r r r} + \toprule +\textbf{Bencher} & \textbf{Datapoints} & \textbf{Failures} & \textbf{(\%)} & +\textbf{MAPE} & \textbf{Median} & \textbf{Q1} & \textbf{Q3} & \textbf{K. tau}\\ +\midrule +BHive & 1365 & 1023 & (42.84\,\%) & 34.07\,\% & 8.62\,\% & 4.30\,\% & 24.25\,\% & 0.76\\ +llvm-mca & 2388 & 0 & (0.00\,\%) & 27.06\,\% & 21.04\,\% & 9.69\,\% & 32.73\,\% & 0.79\\ +UiCA & 2388 & 0 & (0.00\,\%) & 18.42\,\% & 11.96\,\% & 5.42\,\% & 23.32\,\% & 0.80\\ +Ithemal & 2388 & 0 & (0.00\,\%) & 62.66\,\% & 53.84\,\% & 24.12\,\% & 81.95\,\% & 0.40\\ +Iaca & 2388 & 0 & (0.00\,\%) & 17.55\,\% & 12.17\,\% & 4.64\,\% & 22.35\,\% & 0.82\\ +Gus & 2388 & 0 & (0.00\,\%) & 23.18\,\% & 20.23\,\% & 8.78\,\% & 32.73\,\% & 0.83\\ +\bottomrule + \end{tabular} +\end{table*} + +An overview of the full results table (available in our artifact) hints towards +two main tendencies: on a significant number of rows, the static tools +---~thus leaving \gus{} and \bhive{} apart~---, excepted \ithemal, often yield +comparatively bad throughput predictions \emph{together}; and many of these +rows are those using the \texttt{O1} and \texttt{O1autovect} compilation +setting (\texttt{gcc} with \texttt{-O1}, plus vectorisation options for the +latter). + +To confirm the first observation, we look at the 30\,\% worst benchmarks ---~in +terms of MAPE relative to \perf~--- for \llvmmca, \uica{} and \iaca{} +---~yielding 1050 rows each. All of these share 869 rows (82.8\,\%), which we +call \textit{jointly bad rows}. + +Among these 869 jointly bad rows, we further find that respectively 342 +(39.4\,\%) and 337 (38.8\,\%) are compiled using the \texttt{O1} and +\texttt{O1autovect}, totalling to 679 (78.1\,\%) of \texttt{O1}-based rows, +against 129 (14.8\,\%) for \texttt{default} and 61 (7.0\,\%) for +\texttt{O3nosimd}. This result is significant enough to be used as a hint to +investigate the issue. + +\begin{figure} + \includegraphics[width=\linewidth]{figs/nomemdeps_boxplot.pdf} + \caption{Statistical distribution of relative errors, with and without + pruning latency bound through memory-carried dependencies rows}\label{fig:nomemdeps_boxplot} +\end{figure} + + +Insofar as our approach maintains a strong link between the basic blocks studied and +the source codes from which they are extracted, it is possible to identify the +high-level characteristics of the concerned microbenchmarks. +In the overwhelming majority (97.5\,\%) of those jointly bad rows, the tools predicted +fewer cycles than measured, meaning that a bottleneck is either missed or +underestimated. +Manual investigation of a few simple benchmarks (no polyhedral transformation +applied, \texttt{O1} mode, not unrolled) further hints towards dependencies: +for instance, the \texttt{gemver} benchmark, which is \emph{not} among the +badly predicted benchmarks, has this kernel: + +\begin{minipage}{0.95\linewidth} +\begin{lstlisting}[language={[ANSI]C}] +for(c3) + A[c1][c3] += u1[c1] * v1[c3] + + u2[c1] * v2[c3]; +\end{lstlisting} +\end{minipage} + +while the \texttt{atax} benchmark, which is among the badly predicted ones, has +this kernel: + +\begin{minipage}{0.95\linewidth} +\begin{lstlisting}[language=c] +for(c3) + tmp[c1] += A[c1][c3] * x[c3]; +\end{lstlisting} +\end{minipage} + +The first one exhibits no obvious dependency-boundness, while the second, +accumulating on \texttt{tmp[c1]} (independent of the iteration variable) lacks +in instruction-level parallelism. Among the simple benchmarks (as described +above), 8 are in the badly predicted list, all of which exhibit a +read-after-write data dependency to the preceding iteration. + +Looking at the assembly code generated for those in \texttt{O1} modes, it +appears that the dependencies exhibited at the C level are compiled to +\emph{memory-carried} dependencies: the read-after-write happens for a given +memory address, instead of for a register. This kind of dependency, prone to +aliasing and dependent on the values of the registers, is hard to infer for a +static tool and is not supported by the analyzers under scrutiny in the general +case; it could thus reasonably explain the results observed. + +There is no easy way, however, to know for certain which of the 3500 benchmarks +are latency bound: no hardware counter reports this. We investigate this +further using \gus's sensitivity analysis: in complement of the ``normal'' +throughput estimation of \gus, we run it a second time, disabling the +accounting for latency through memory dependencies. By construction, this second measurement should be +either very close to the first one, or significantly below. We then assume a +benchmark to be latency bound due to memory-carried dependencies when it is at +least 40\,\% faster when this latency is disabled; there are 1112 (31.8\,\%) such +benchmarks. + +Of the 869 jointly bad rows, 745 (85.7\,\%) are declared latency +bound through memory-carried dependencies by \gus. We conclude that the main +reason for these jointly badly predicted benchmarks is that the predictors +under scrutiny failed to correctly detect these dependencies. + +In Section~\ref{ssec:overall_results}, we presented in +Figure~\ref{fig:overall_analysis_boxplot} and +Table~\ref{table:overall_analysis_stats} general statistics on the tools +on the full set of benchmarks. We now remove the 1112 benchmarks +flagged as latency bound through memory-carried dependencies by \gus{} from the +dataset, and present in Figure~\ref{fig:nomemdeps_boxplot} a comparative +box plot for the tools under scrutiny. We also present in +Table~\ref{table:nomemdeps_stats} the same statistics on this pruned dataset. +While the results for \llvmmca, \uica{} and \iaca{} globally improved +significantly, the most noticeable improvements are the reduced spread of the +results and the Kendall's $\tau$ correlation coefficient's increase. + +From this, +we argue that detecting memory-carried dependencies is a weak point in current +state-of-the-art static analyzers, and that their results could be +significantly more accurate if improvements are made in this direction. diff --git a/manuscrit/50_CesASMe/30_future_works.tex b/manuscrit/50_CesASMe/30_future_works.tex new file mode 100644 index 0000000..7f25683 --- /dev/null +++ b/manuscrit/50_CesASMe/30_future_works.tex @@ -0,0 +1,106 @@ +\section{Conclusion and future works} + +In this article, we have presented a fully-tooled approach that enables: + +\begin{itemize} +\item the generation of a wide variety of microbenchmarks, reflecting both the + expertise contained in an initial benchmark suite, and the diversity of + code transformations allowing to stress different aspects of a performance model + ---~or even a measurement environment, \eg{} \bhive; and +\item the comparability of various measurements and + analyses applied to each of these microbenchmarks. +\end{itemize} + +Thanks to this tooling, we were able to show the limits and strengths of +various performance models in relation to the expertise contained in the +Polybench suite. We discuss throughput results in +Section~\ref{ssec:overall_results} and bottleneck prediction in +Section~\ref{ssec:bottleneck_pred_analysis}. + +We were also able to demonstrate the difficulties of reasoning at the level of +a basic block isolated from its context. We specifically study those +difficulties in the case of \bhive{} in Section~\ref{ssec:bhive_errors}. +Indeed, the actual values ---~both from registers and memory~--- involved in a +basic block's computation are constitutive not only of its functional +properties (\ie{} the result of the calculation), but also of some of its +non-functional properties (\eg{} latency, throughput). + +We were also able to show in Section~\ref{ssec:memlatbound} +that state-of-the-art static analyzers struggle to +account for memory-carried dependencies; a weakness significantly impacting +their overall results on our benchmarks. We believe that detecting +and accounting for these dependencies is an important future works direction. + +Moreover, we present this work in the form of a modular software package, each +component of which exposes numerous adjustable parameters. These components can +also be replaced by others fulfilling the same abstract function: another +initial benchmark suite in place of Polybench, other loop nest +optimizers in place of PLUTO and PoCC, other code +analyzers, and so on. This software modularity reflects the fact that our +contribution is about interfacing and communication between distinct issues. + +\medskip + +Furthermore, we believe that the contributions we made in the course of this work +may eventually be used to face different, yet neighbouring issues. +These perspectives can also be seen as future works: + +\smallskip + +\paragraph{Program optimization} the whole program processing we have designed +can be used not only to evaluate the performance model underlying a static +analyzer, but also to guide program optimization itself. In such a perspective, +we would generate different versions of the same program using the +transformations discussed in Section~\ref{sec:bench_gen} and colored blue in +Figure~\ref{fig:contrib}. These different versions would then feed the +execution and measurement environment outlined in +Section~\ref{sec:bench_harness} and colored orange in Figure~\ref{fig:contrib}. +Indeed, thanks to our previous work, we know that the results of these +comparable analyses and measurements would make it possible to identify which +version is the most efficient, and even to reconstruct information indicating +why (which bottlenecks, etc.). + +However, this approach would require that these different versions of the same +program are functionally equivalent, \ie{} that they compute the same +result from the same inputs; yet we saw in Section~\ref{sec:bench_harness} +that, as it stands, the transformations we apply are not concerned with +preserving the semantics of the input codes. To recover this semantic +preservation property, abandoning the kernelification pass we have presented +suffices; this however would require to control L1-residence otherwise. + +\smallskip + +\paragraph{Dataset building} our microbenchmarks generation phase outputs a +large, diverse and representative dataset of microkernels. In addition to our +harness, we believe that such a dataset could be used to improve existing +data-dependant solutions. + +%the measurement and execution environment we +%propose is not the only type of tool whose function is to process a large +%dataset (\ie{} the microbenchmarks generated earlier) to automatically +%abstract its characteristics. We can also think of: + +Inductive methods, for instance in \anica, strive to preserve the properties of a basic +block through successive abstractions of the instructions it contains, so as to +draw the most general conclusions possible from a particular experiment. +Currently, \anica{} starts off from randomly generated basic blocks. This +approach guarantees a certain variety, and avoids +over-specialization, which would prevent it from finding interesting cases too +far from an initial dataset. However, it may well lead to the sample under +consideration being systematically outside the relevant area of the search +space ---~\ie{} having no relation to real-life programs or those in the user's +field. + +On the other hand, machine learning methods based on neural networks, for +instance in \ithemal, seek to correlate the result of a function with the +characteristics of its input ---~in this case to correlate a throughput +prediction with the instructions making up a basic block~--- by backpropagating +the gradient of a cost function. In the case of \ithemal{}, it is trained on +benchmarks originating from a data suite. As opposed to random generation, +this approach offers representative samples, but comes with a risk of lack of +variety and over-specialization. + +Comparatively, our microbenchmark generation method is natively meant to +produce a representative, varied and large dataset. We believe that +enriching the dataset of the above-mentioned methods with our benchmarks might +extend their results and reach. diff --git a/manuscrit/50_CesASMe/99_conclusion.tex b/manuscrit/50_CesASMe/99_conclusion.tex new file mode 100644 index 0000000..3815954 --- /dev/null +++ b/manuscrit/50_CesASMe/99_conclusion.tex @@ -0,0 +1,2 @@ +%% \section*{Conclusion} +%% \todo{} diff --git a/manuscrit/50_CesASMe/main.tex b/manuscrit/50_CesASMe/main.tex index 0ebb0ef..da40a41 100644 --- a/manuscrit/50_CesASMe/main.tex +++ b/manuscrit/50_CesASMe/main.tex @@ -1 +1,9 @@ \chapter{A more systematic approach to throughput prediction performance analysis} + +\input{00_intro.tex} +\input{05_related_works.tex} +\input{10_bench_gen.tex} +\input{20_evaluation.tex} +\input{25_results_analysis.tex} +\input{30_future_works.tex} +\input{99_conclusion.tex} diff --git a/manuscrit/50_CesASMe/overview.tex b/manuscrit/50_CesASMe/overview.tex new file mode 100644 index 0000000..55285f6 --- /dev/null +++ b/manuscrit/50_CesASMe/overview.tex @@ -0,0 +1,82 @@ +\begin{figure*}[ht!] + \definecolor{col_bench_gen}{HTML}{5a7eff} + \definecolor{col_bench_gen_bg}{HTML}{dbeeff} + \definecolor{col_bench_harness}{HTML}{ffa673} + \definecolor{col_results}{HTML}{000000} +\centerline{ + \begin{tikzpicture}[ + hiddennode/.style={rectangle,draw=white, very thick, minimum size=5mm, align=center, font=\footnotesize}, + normnode/.style={rectangle,draw=black, very thick, minimum size=5mm, align=center, font=\footnotesize}, + resultnode/.style={rectangle,draw=col_results, fill=black!2, very thick, minimum size=5mm, align=center, font=\footnotesize}, + bluenode/.style={rectangle, draw=col_bench_gen, fill=col_bench_gen_bg, very thick, minimum height=5mm, minimum width=4cm, align=center, font=\footnotesize}, + rednode/.style={rectangle, draw=col_bench_harness, fill=orange!5, very thick, minimum size=5mm, align=center, font=\footnotesize}, + bencher/.style={rednode, minimum width=2.5cm, minimum height=5mm}, + genarrow/.style={draw=col_bench_gen}, + harnarrow/.style={draw=col_bench_harness}, + ] + \centering + %Nodes + \node[bluenode] (bench) {Benchmark suite \figref{ssec:bench_suite}}; + \node[bluenode] (pocc) [below=of bench] {Loop nest optimizers \figref{ssec:loop_nest_optimizer}}; + \node[bluenode] (kernel) [below=of pocc] {Constraining utility \figref{ssec:kernelify}}; + \node[bluenode] (gcc) [below=of kernel] {Compilations \figref{ssec:compile}}; + \node[rednode] (gdb) [right=0.1\textwidth of gcc] {Basic block \\extraction \figref{ssec:bb_extr}}; + \node[bencher] (ithemal) [right=4cm of gdb] {Ithemal}; + \node[bencher] (iaca) [above=0.5em of ithemal] {IACA}; + \node[bencher] (uica) [above=0.5em of iaca] {uiCA}; + \node[bencher] (llvm) [above=0.5em of uica] {llvm-mca}; + \node[bencher] (bhive) [above=0.5em of llvm] {BHive (measure)}; + \node[rednode] (ppapi) [left=1cm of bhive] {perf (measure)}; + \node[rednode] (gus) [below=0.5em of ppapi] {Gus}; + %% \node[rednode] (uica) [below=of gdb] {uiCA}; + \node[rednode] (lifting) [right=of bhive] { + Prediction lifting\\\figref{ssec:harness_lifting}}; + \node[ + draw=black, + very thick, + dotted, + fit=(ppapi) (gus) (bhive) (llvm) (uica) (iaca) (ithemal) + ] (comps) {}; + \node (throughput_label) [above=0.2em of comps,align=center] { + \footnotesize Throughput predictions \\\footnotesize \& measures + \figref{ssec:throughput_pred_meas}}; + \node[draw=black, + very thick, + dotted, + %% label={below:\footnotesize Variations}, + label={[above,xshift=1cm]\footnotesize Variations}, + fit=(pocc) (kernel) (gcc) + ] (vars) {}; +\node[resultnode] (bench2) [below=of lifting] {Evaluation metrics \\ for + code analyzers}; + + % Key + \node[] (keyblue1) [below left=0.7cm and 0cm of vars] {}; + \node[hiddennode] (keyblue2) [right=0.5cm of keyblue1] {Section~\ref{sec:bench_gen}~: generating microbenchmarks}; + \node[] (keyred1) [right=0.6cm of keyblue2] {}; + \node[hiddennode] (keyred2) [right=0.5cm of keyred1] {Section~\ref{sec:bench_harness}~: benchmarking harness}; + \node[] (keyresult1) [right=0.6cm of keyred2] {}; + \node[hiddennode] (keyresult2) [right=0.5cm of keyresult1] + {Section~\ref{sec:results_analysis}~: results analysis}; + + %Lines + \draw[-, very thick, harnarrow] (keyred1.east) -- (keyred2.west); + \draw[-, very thick, genarrow] (keyblue1.east) -- (keyblue2.west); + \draw[-, very thick] (keyresult1.east) -- (keyresult2.west); + \draw[->, very thick, genarrow] (bench.south) -- (pocc.north); + \draw[->, very thick, genarrow] (pocc.south) -- (kernel.north); + \draw[->, very thick, genarrow] (kernel.south) -- (gcc.north); + \draw[->, very thick, genarrow] (gcc.east) -- (gdb.west); + \draw[->, very thick, genarrow] (gcc.east) -- (ppapi.west); + \draw[->, very thick, genarrow] (gcc.east) -- (gus.west); + \draw[->, very thick, harnarrow] (gdb.east) -- (uica.west); + \draw[->, very thick, harnarrow] (gdb.east) -- (iaca.west); + \draw[->, very thick, harnarrow] (gdb.east) -- (ithemal.west); + \draw[->, very thick, harnarrow] (gdb.east) -- (bhive.west); + \draw[->, very thick, harnarrow] (gdb.east) -- (llvm.west); + \draw[->, very thick, harnarrow] (comps.east|-lifting) -- (lifting.west); + \draw[->, very thick] (lifting.south) -- (bench2.north); + \end{tikzpicture} +} +\caption{Our analysis and measurement environment.\label{fig:contrib}} +\end{figure*} diff --git a/manuscrit/assets/imgs/50_CesASMe/.gitignore b/manuscrit/assets/imgs/50_CesASMe/.gitignore new file mode 100644 index 0000000..13f0a16 --- /dev/null +++ b/manuscrit/assets/imgs/50_CesASMe/.gitignore @@ -0,0 +1 @@ +!*.pdf diff --git a/manuscrit/assets/imgs/50_CesASMe/nomemdeps_boxplot.pdf b/manuscrit/assets/imgs/50_CesASMe/nomemdeps_boxplot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..019a570c487d8021362f907bb37d03022282bae0 GIT binary patch literal 19306 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 literal 0 HcmV?d00001 diff --git a/manuscrit/assets/imgs/50_CesASMe/overall_analysis_boxplot.pdf b/manuscrit/assets/imgs/50_CesASMe/overall_analysis_boxplot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e898951255edab7490fa3d504ee69961891052a5 GIT binary patch literal 24211 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 literal 0 HcmV?d00001 diff --git a/manuscrit/assets/imgs/50_CesASMe/results_comparability_hist.pdf b/manuscrit/assets/imgs/50_CesASMe/results_comparability_hist.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0fda8f53fb93c4ec73ef63d04bdb3b829eddbc60 GIT binary patch literal 15567 zcmb_@2RxN;^mszJ8JQ*FTFJh{z1Ov}v&S_f*(;k%XpoT=Wsi_hc9I#gw~#0mDkEf* zGLnApd#i7K%kTf~^ZWeyJokCt=RMDP&N<I{&pFTH(N<6rf(wg6c#1}$g*6a36b5y* zJO`1Kgo^0-y4gTQ<Sp<PPOf%P5p4@Q8&4<#0Kh?|r6D#h)*ztRFAbDjUGPvO0YJn^ z%krF!6&{ND_NeHCKc$Pez}rAYzg1{k;PEydE>INs2oce>x3G3_v4f(&zx8mn(zU@u zO@M9{lmJp}eDF{a6=#41x$n9B_gn>P`U^SmKN$e&2C4V30jT>%z6j37)78tv3g8E% zALw7h#@fL`&eaF#5eEE=B9Ovj2!t36hJnKoPy|d&SPbY#L>}M}K;sF;exp;?#l;nP zf`e^-l?wR!hZxmuT<q}nPyh`GBckNs1TYTdPJlENY^+?ZZ9w`x@g6o7&Jdrp-gJ$) z(MsB@%R7v9Io;w%nm0NIx<hgGIf8}!p<-kg9vLhwys+tV-@ROeMap2Fq?uyx@#rN= zeh{Xpqal+aHmBKmcWcT2%kI){@4VqWd&8NtmFLg1GnMV`^M6z9_l0P8_dIIear3jw zz{*m&&%9nG+L73meb%I~tQ-1%d(Nz9ZES4~DYiv?Y{&0(pswcOq~^I;2%A|NlVkSN z_L}&c+cNrvQ4!FJvXX9trv5_hA{7Ik#d7cG9d)U*3Dg5uKYsbLz45W*<2yTV&pS@t zubGpajLIS6Q%<J$NyFYW1o`hqw!gHPcY8r?sN8Kb8F<@BL$obFUY9@RHk^6$tdLm| z@=!D^m**&BnrGrAH;u<i2~+8lqk8F~S~5iSnOQWN7upTmwDlg`+icCt7pHv_w*R6z zDraf3_8i;xt;iHwj5Fs0qKR_uBk`7{9JmL^{c`br`3&u9t-iA)bd72S)<;-aTMDy6 z<;vjsZuVK4t(dH|L?_1N2Q}K#oJ=QjXBU`J`ZMG7+5sZ`g+X0cR`mzu-H^MZbR%1O z>0z!4SRq9LyV!8SweTz-p6T24lEmqu+6LFGTJ8#}cG3?-Wn2+%QM00q^nGxGLrVeU zCoQC8!pIR{8l%XdI0ewquY6cPLbjuG)mlFwBivP#DnLmfnZ>c6^G!6rrIx~pY9%=u zqGJ0Nli6r~_kIPj8l~o}YtQeH^l=u_ZKwxlJ3@m|rKn2o9wTf0sf;jfx2Rwh*^g~@ znkRm|$1C}gA??t+%Xyp|3c-Pn+>|TgA7~YaxTjNQ{rWo%^sXH}a7vT2kn4jwKHKhX zP}{jcMaQ?aS_X=9;pC*l%E@|t(k8E&&g+F=INm&fI&>ggHT?94Fy6xN=&l_a@#vPE zSj_uQ&o#G<XM8I^->-LLZyvkuBT>*QML#}!l%j0yHE)ZKf=(8|E{w{@oFn)MlE&`= zzm8TbZ?aM&`KnMyXZGu8ehZM>1I<PH-QwbGAOGR*>^sZsaTA7r&_zCd71|8K6QpyX zc_JPTSNH{<V#0(v<J2Qo{fUflH~b~zmHL_w;zmqF*80;Kq1ql%!LnQXB0siL*n9(s z3RVmu*BNzZI-O8$$9c?@__jO$@f)W}Ug#(;eG><?-Nu!RH(o!ymhSB0z1jC<_ivQ+ zJwGquE1*HLuI8(K6e@I5B>B^a{HqTF4ceO{s4hDd^IJTU5Gr{sEb`UTAbYs#jq{eA zYlG=->E$f5qOWJ(T&~~QT3l;;fPL|BW2n^x<vOm@BHqINnA$JLBRgd7okn({ccDj+ zuS$(|65=s8d+}IAMbDsQ)@%HsLGO1J)YOTb*RgJ~#QA)yw�@rq4HJj%Ss%zc|I} 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 literal 0 HcmV?d00001