Mercurial > cortex
view thesis/aux/mitthesis/chap1.tex @ 505:c15e24d24396
possibe fix using anchoredListing
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 30 Mar 2014 00:17:54 -0400 |
parents | 6b0f77df0e53 |
children |
line wrap: on
line source
1 %% This is an example first chapter. You should put chapter/appendix that you2 %% write into a separate file, and add a line \include{yourfilename} to3 %% main.tex, where `yourfilename.tex' is the name of the chapter/appendix file.4 %% You can process specific files by typing their names in at the5 %% \files=6 %% prompt when you run the file main.tex through LaTeX.7 \chapter{Introduction}9 Micro-optimization is a technique to reduce the overall operation count of10 floating point operations. In a standard floating point unit, floating11 point operations are fairly high level, such as ``multiply'' and ``add'';12 in a micro floating point unit ($\mu$FPU), these have been broken down into13 their constituent low-level floating point operations on the mantissas and14 exponents of the floating point numbers.16 Chapter two describes the architecture of the $\mu$FPU unit, and the17 motivations for the design decisions made.19 Chapter three describes the design of the compiler, as well as how the20 optimizations discussed in section~\ref{ch1:opts} were implemented.22 Chapter four describes the purpose of test code that was compiled, and which23 statistics were gathered by running it through the simulator. The purpose24 is to measure what effect the micro-optimizations had, compared to25 unoptimized code. Possible future expansions to the project are also26 discussed.28 \section{Motivations for micro-optimization}30 The idea of micro-optimization is motivated by the recent trends in computer31 architecture towards low-level parallelism and small, pipelineable32 instruction sets \cite{patterson:risc,rad83}. By getting rid of more33 complex instructions and concentrating on optimizing frequently used34 instructions, substantial increases in performance were realized.36 Another important motivation was the trend towards placing more of the37 burden of performance on the compiler. Many of the new architectures depend38 on an intelligent, optimizing compiler in order to realize anywhere near39 their peak performance40 \cite{ellis:bulldog,pet87,coutant:precision-compilers}. In these cases, the41 compiler not only is responsible for faithfully generating native code to42 match the source language, but also must be aware of instruction latencies,43 delayed branches, pipeline stages, and a multitude of other factors in order44 to generate fast code \cite{gib86}.46 Taking these ideas one step further, it seems that the floating point47 operations that are normally single, large instructions can be further broken48 down into smaller, simpler, faster instructions, with more control in the49 compiler and less in the hardware. This is the idea behind a50 micro-optimizing FPU; break the floating point instructions down into their51 basic components and use a small, fast implementation, with a large part of52 the burden of hardware allocation and optimization shifted towards53 compile-time.55 Along with the hardware speedups possible by using a $\mu$FPU, there are56 also optimizations that the compiler can perform on the code that is57 generated. In a normal sequence of floating point operations, there are58 many hidden redundancies that can be eliminated by allowing the compiler to59 control the floating point operations down to their lowest level. These60 optimizations are described in detail in section~\ref{ch1:opts}.62 \section{Description of micro-optimization}\label{ch1:opts}64 In order to perform a sequence of floating point operations, a normal FPU65 performs many redundant internal shifts and normalizations in the process of66 performing a sequence of operations. However, if a compiler can67 decompose the floating point operations it needs down to the lowest level,68 it then can optimize away many of these redundant operations.70 If there is some additional hardware support specifically for71 micro-optimization, there are additional optimizations that can be72 performed. This hardware support entails extra ``guard bits'' on the73 standard floating point formats, to allow several unnormalized operations to74 be performed in a row without the loss information\footnote{A description of75 the floating point format used is shown in figures~\ref{exponent-format}76 and~\ref{mantissa-format}.}. A discussion of the mathematics behind77 unnormalized arithmetic is in appendix~\ref{unnorm-math}.79 The optimizations that the compiler can perform fall into several categories:81 \subsection{Post Multiply Normalization}83 When more than two multiplications are performed in a row, the intermediate84 normalization of the results between multiplications can be eliminated.85 This is because with each multiplication, the mantissa can become86 denormalized by at most one bit. If there are guard bits on the mantissas87 to prevent bits from ``falling off'' the end during multiplications, the88 normalization can be postponed until after a sequence of several89 multiplies\footnote{Using unnormalized numbers for math is not a new idea; a90 good example of it is the Control Data CDC 6600, designed by Seymour Cray.91 \cite{thornton:cdc6600} The CDC 6600 had all of its instructions performing92 unnormalized arithmetic, with a separate {\tt NORMALIZE} instruction.}.94 % This is an example of how you would use tgrind to include an example95 % of source code; it is commented out in this template since the code96 % example file does not exist. To use it, you need to remove the '%' on the97 % beginning of the line, and insert your own information in the call.98 %99 %\tagrind[htbp]{code/pmn.s.tex}{Post Multiply Normalization}{opt:pmn}101 As you can see, the intermediate results can be multiplied together, with no102 need for intermediate normalizations due to the guard bit. It is only at103 the end of the operation that the normalization must be performed, in order104 to get it into a format suitable for storing in memory\footnote{Note that105 for purposed of clarity, the pipeline delays were considered to be 0, and106 the branches were not delayed.}.108 \subsection{Block Exponent}110 In a unoptimized sequence of additions, the sequence of operations is as111 follows for each pair of numbers ($m_1$,$e_1$) and ($m_2$,$e_2$).112 \begin{enumerate}113 \item Compare $e_1$ and $e_2$.114 \item Shift the mantissa associated with the smaller exponent $|e_1-e_2|$115 places to the right.116 \item Add $m_1$ and $m_2$.117 \item Find the first one in the resulting mantissa.118 \item Shift the resulting mantissa so that normalized119 \item Adjust the exponent accordingly.120 \end{enumerate}122 Out of 6 steps, only one is the actual addition, and the rest are involved123 in aligning the mantissas prior to the add, and then normalizing the result124 afterward. In the block exponent optimization, the largest mantissa is125 found to start with, and all the mantissa's shifted before any additions126 take place. Once the mantissas have been shifted, the additions can take127 place one after another\footnote{This requires that for n consecutive128 additions, there are $\log_{2}n$ high guard bits to prevent overflow. In129 the $\mu$FPU, there are 3 guard bits, making up to 8 consecutive additions130 possible.}. An example of the Block Exponent optimization on the expression131 X = A + B + C is given in figure~\ref{opt:be}.133 % This is an example of how you would use tgrind to include an example134 % of source code; it is commented out in this template since the code135 % example file does not exist. To use it, you need to remove the '%' on the136 % beginning of the line, and insert your own information in the call.137 %138 %\tgrind[htbp]{code/be.s.tex}{Block Exponent}{opt:be}140 \section{Integer optimizations}142 As well as the floating point optimizations described above, there are143 also integer optimizations that can be used in the $\mu$FPU. In concert144 with the floating point optimizations, these can provide a significant145 speedup.147 \subsection{Conversion to fixed point}149 Integer operations are much faster than floating point operations; if it is150 possible to replace floating point operations with fixed point operations,151 this would provide a significant increase in speed.153 This conversion can either take place automatically or or based on a154 specific request from the programmer. To do this automatically, the155 compiler must either be very smart, or play fast and loose with the accuracy156 and precision of the programmer's variables. To be ``smart'', the computer157 must track the ranges of all the floating point variables through the158 program, and then see if there are any potential candidates for conversion159 to floating point. This technique is discussed further in160 section~\ref{range-tracking}, where it was implemented.162 The other way to do this is to rely on specific hints from the programmer163 that a certain value will only assume a specific range, and that only a164 specific precision is desired. This is somewhat more taxing on the165 programmer, in that he has to know the ranges that his values will take at166 declaration time (something normally abstracted away), but it does provide167 the opportunity for fine-tuning already working code.169 Potential applications of this would be simulation programs, where the170 variable represents some physical quantity; the constraints of the physical171 system may provide bounds on the range the variable can take.172 \subsection{Small Constant Multiplications}174 One other class of optimizations that can be done is to replace175 multiplications by small integer constants into some combination of176 additions and shifts. Addition and shifting can be significantly faster177 than multiplication. This is done by using some combination of178 \begin{eqnarray*}179 a_i & = & a_j + a_k \\180 a_i & = & 2a_j + a_k \\181 a_i & = & 4a_j + a_k \\182 a_i & = & 8a_j + a_k \\183 a_i & = & a_j - a_k \\184 a_i & = & a_j \ll m \mbox{shift}185 \end{eqnarray*}186 instead of the multiplication. For example, to multiply $s$ by 10 and store187 the result in $r$, you could use:188 \begin{eqnarray*}189 r & = & 4s + s\\190 r & = & r + r191 \end{eqnarray*}192 Or by 59:193 \begin{eqnarray*}194 t & = & 2s + s \\195 r & = & 2t + s \\196 r & = & 8r + t197 \end{eqnarray*}198 Similar combinations can be found for almost all of the smaller199 integers\footnote{This optimization is only an ``optimization'', of course,200 when the amount of time spent on the shifts and adds is less than the time201 that would be spent doing the multiplication. Since the time costs of these202 operations are known to the compiler in order for it to do scheduling, it is203 easy for the compiler to determine when this optimization is worth using.}.204 \cite{magenheimer:precision}206 \section{Other optimizations}208 \subsection{Low-level parallelism}210 The current trend is towards duplicating hardware at the lowest level to211 provide parallelism\footnote{This can been seen in the i860; floating point212 additions and multiplications can proceed at the same time, and the RISC213 core be moving data in and out of the floating point registers and providing214 flow control at the same time the floating point units are active. \cite{byte:i860}}216 Conceptually, it is easy to take advantage to low-level parallelism in the217 instruction stream by simply adding more functional units to the $\mu$FPU,218 widening the instruction word to control them, and then scheduling as many219 operations to take place at one time as possible.221 However, simply adding more functional units can only be done so many times;222 there is only a limited amount of parallelism directly available in the223 instruction stream, and without it, much of the extra resources will go to224 waste. One process used to make more instructions potentially schedulable225 at any given time is ``trace scheduling''. This technique originated in the226 Bulldog compiler for the original VLIW machine, the ELI-512.227 \cite{ellis:bulldog,colwell:vliw} In trace scheduling, code can be228 scheduled through many basic blocks at one time, following a single229 potential ``trace'' of program execution. In this way, instructions that230 {\em might\/} be executed depending on a conditional branch further down in231 the instruction stream are scheduled, allowing an increase in the potential232 parallelism. To account for the cases where the expected branch wasn't233 taken, correction code is inserted after the branches to undo the effects of234 any prematurely executed instructions.236 \subsection{Pipeline optimizations}238 In addition to having operations going on in parallel across functional239 units, it is also typical to have several operations in various stages of240 completion in each unit. This pipelining allows the throughput of the241 functional units to be increased, with no increase in latency.243 There are several ways pipelined operations can be optimized. On the244 hardware side, support can be added to allow data to be recirculated back245 into the beginning of the pipeline from the end, saving a trip through the246 registers. On the software side, the compiler can utilize several tricks to247 try to fill up as many of the pipeline delay slots as possible, as248 seendescribed by Gibbons. \cite{gib86}