From: Sam Moore Date: Thu, 24 Jul 2014 13:06:58 +0000 (+0800) Subject: Less terrible FloatingPointCPUvsGPU writeup X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=e3cd7f4dd5d154dbd1e7aca0055164005185b67c;p=ipdf%2Fdocuments.git Less terrible FloatingPointCPUvsGPU writeup Less blatantly wrong more kind of actually formal. David got the nVidia and intel screenshots. --- diff --git a/FloatingPointCPUvsGPU.pdf b/FloatingPointCPUvsGPU.pdf index a2b5b31..b22d74e 100644 Binary files a/FloatingPointCPUvsGPU.pdf and b/FloatingPointCPUvsGPU.pdf differ diff --git a/FloatingPointCPUvsGPU.tex b/FloatingPointCPUvsGPU.tex index 0ad0df8..c28adfa 100644 --- a/FloatingPointCPUvsGPU.tex +++ b/FloatingPointCPUvsGPU.tex @@ -1,4 +1,4 @@ -\documentclass[11pt]{article} +\documentclass[9pt]{article} \input{template} \begin{document} @@ -8,85 +8,91 @@ \section*{Abstract} -Modern GPUs appear to not be compliant with IEEE-754 when using floating point operations. There is also difference between the behaviour of different GPU models. -We compare the rendering of filled circles on a x86-64 CPU and a GPU (AMD/ATI Whilstler LE [Radeon HD 6610M/7610M]). +We qualitatively illustrate differences between floating point operations on the x86-64 CPU and several GPUs by rendering filled ellipses. \section{Introduction} -Although it is well known that the behaviour of GPU drivers is inconsistent, there is little research into the behaviour of floating point operations using such drivers. +The IEEE Standard for Floating-Point Arithmetic \cite{ieee2008-754} has been widely adopted by hardware manufacturers of CPUs and programming language standards. + +Although it is well known that the behaviour of GPU drivers is inconsistent, there is little formal academic research into the behaviour of floating point operations using such drivers. In 2004 Hillesland and Lastra adapted Kahan's well known program for testing floating point arithmetic on CPUs during the 1980s ``Paranoia'' for GPUs and found that many GPUs did not appear to be compliant with IEEE-754\cite{hillesland2004paranoia}. -Given the recent interest in use of the GPU for vector graphics\cite{kilgard2012gpu} this seems worthy of further investigation. -Using the same algorithm implemented in C/C++ and GLSL we have shown that a particular GPU using a particular driver exhibit less precision than IEEE-754 binary32 floats. +Given the recent interest in use of the GPU for vector graphics\cite{kilgard2012gpu} the behaviour of GPUs when performing floating point operations is worthy of closer investigation. -\section{Algorithm} +Using a straight forward filled ellipse rendering algorithm implemented in C/C++ and GLSL we show inconsistent floating point behaviour when comparing the x86-64 CPU, an nVidia GPU\footnote{??? using the nVidia driver}, an intel GPU\footnote{??? using the intel driver} and an AMD/ATI GPU\footnote{Whistler LE (Radeon HD 6610M/7610M) using the fglrx driver}. -For each integer valued $(x,y)$ in the bounding rectangle, if $x^2 + y^2 \leq r^2$ where $r$ is the radius of the circle, then $(x,y)$ should be filled. +\section{Algorithm} -There are two sources of error; the coordinate transforms of vertices \emph{before} rendering, and the operations required to subsequently render the circle at that position. The difference between using the CPU and GPU for the former is apparent but is only easily demonstrated in a live demo. The images below demonstrate the precision issues with the latter\footnote{Me write um good english}. +For each pixel position $(x,y)$ normalised relative to the bounding rectangle, if $x^2 + y^2 \leq 1$ then $(x,y)$ should be filled. + +Although $x$ and $y$ may be treated as integers on the CPU, since the OpenGL API requires floating point vertex coordinates, our CPU implementation also normalises the coordinates relative to the bounding rectangle; this way we can compare the performance of floating point operations on the CPU and GPU(s). + +\pagebreak +\subsection{GLSL Fragment Shader} +\begin{minted}{glsl} +#version 140 +// Fragment shader (others omitted) + +in vec2 objcoords; // Coordinates x, y, relative to bounding rectangle (from other shaders) +out vec4 output_colour; + +uniform vec4 colour; + +void main() +{ + if ((objcoords.x)*(objcoords.x) + (objcoords.y)*(objcoords.y) > 1.0) + { + discard; + } + output_colour = colour; +} +\end{minted} + +\subsection{CPU Rendering Algorithm (simplified)} +\begin{minted}{c++} +// where bounds = {x,y,w,h} gives the bounding rectangle in integer pixel positions +// and centre = {x,y} is the centre of the circle +// and pixels[][] is the display buffer +for (int x = bounds.x; x < bounds.x+bounds.w; ++x) +{ + for (int y = bounds.y; y < bounds.y+bounds.h; ++y) + { + float dx = 2.0*(float)(x - centre.x)/(float)(bounds.w); + float dy = 2.0*(float)(y - centre.y)/(float)(bounds.h); + if (dx*dx + dy*dy <= 1.0) + { + pixels[x][y] = true; + } + } +} +\end{minted} + +Note: The \verb/pixels/ buffer is uploaded directly to the GPU after CPU rendering is completed. \section{Results} -Each pair of figures compares rendering using an x86-64 CPU and OpenGL shaders running on the AMD/ATI Whilstler LE [Radeon HD 6610M/7610M] using the \emph{fglrx} proprietry graphics drivers. +Figure \ref{comparison.pdf} shows the edge of a unit radius circle viewed under a magnification of approximately $5\times10^6$ as rendered using the CPU. -\begin{figure}[H] -\centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/cpu0.png} -\caption{A circle. Sort of.} -\end{figure} -The images produced by GPU and CPU rendering are indistinguishable at the original scale. Note that the "CPU rendering" is essentially producing a bitmap and then sending that to the GPU; so all floating point operations are still done on the CPU, wherase "GPU rendering" involves passing floats representing vertex positions to a GLSL shader program. -\begin{figure}[H] -\centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/cpu1.png} -\caption{CPU, zoomed} -\end{figure} +\section{Conclusion} -\begin{figure}[H] -\centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/gpu1.png} -\caption{GPU, zoomed} -\end{figure} +nVidia looks qualitatively similar to the CPU rendering. Frankly I was just happy fglrx didn't segfault. Wierd shit happens with intel. If anyone isn't obeying IEEE-754 here, it is probably intel. -The rounding errors begin to become apparent. +\bibliographystyle{unsrt} +\bibliography{papers} -\begin{figure}[H] -\centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/cpu2.png} -\caption{CPU, zoomed more} -\end{figure} - -\begin{figure}[H] -\centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/gpu2.png} -\caption{GPU, zoomed more} -\end{figure} - -Even worse... - -\begin{figure}[H] -\centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/cpu3.png} -\caption{CPU, zoomed more} -\end{figure} +\pagebreak \begin{figure}[H] \centering -\includegraphics[width=\textwidth]{figures/circles_gpu_vs_cpu/gpu3.png} -\caption{GPU, zoomed more} +\includegraphics[width=0.8\textwidth]{figures/circles_gpu_vs_cpu/comparison.pdf} +\caption{The edges of a unit circle viewed through bounds (x,y,w,h) = (0.0869386,0.634194,2.63295e-07,2.63295e-07)}\label{comparison.pdf} \end{figure} -Image is not recognisable as once being a circle. -Note that at this scale there are also issues with translation using the CPU (ie: It was not possible to position the circle so that it covered the same fraction of the screen as in the earlier images). - -It is hard to tell how much of this is due to bugs in the fglrx driver and how much is actually due to physical limitations on the GPU hardware. -David found code for a graphics driver with a \verb/USE_IEEE_FLOATS/ define that was \verb/false/ by default...\footnote{David say more here?} -\section{Conclusions} -fglrx is pretty terrible. GPUs are probably not as good at floating point as CPUs. \end{document} diff --git a/figures/circles_gpu_vs_cpu/comparison.pdf b/figures/circles_gpu_vs_cpu/comparison.pdf new file mode 100644 index 0000000..70b0eff Binary files /dev/null and b/figures/circles_gpu_vs_cpu/comparison.pdf differ diff --git a/figures/circles_gpu_vs_cpu/comparison.svg b/figures/circles_gpu_vs_cpu/comparison.svg new file mode 100644 index 0000000..76eb9ae --- /dev/null +++ b/figures/circles_gpu_vs_cpu/comparison.svg @@ -0,0 +1,685 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + x86-64 CPU + nVidia shader + fglrx shader + intel shader + + + diff --git a/figures/circles_gpu_vs_cpu/cpu0.png b/figures/circles_gpu_vs_cpu/cpu0.png deleted file mode 100644 index 27e1f29..0000000 Binary files a/figures/circles_gpu_vs_cpu/cpu0.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/cpu1.png b/figures/circles_gpu_vs_cpu/cpu1.png deleted file mode 100644 index b10ac75..0000000 Binary files a/figures/circles_gpu_vs_cpu/cpu1.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/cpu2.png b/figures/circles_gpu_vs_cpu/cpu2.png deleted file mode 100644 index 982e22b..0000000 Binary files a/figures/circles_gpu_vs_cpu/cpu2.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/cpu3.png b/figures/circles_gpu_vs_cpu/cpu3.png deleted file mode 100644 index c438aaa..0000000 Binary files a/figures/circles_gpu_vs_cpu/cpu3.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/fglrx.png b/figures/circles_gpu_vs_cpu/fglrx.png new file mode 100644 index 0000000..1298471 Binary files /dev/null and b/figures/circles_gpu_vs_cpu/fglrx.png differ diff --git a/figures/circles_gpu_vs_cpu/gpu0.png b/figures/circles_gpu_vs_cpu/gpu0.png deleted file mode 100644 index d18919c..0000000 Binary files a/figures/circles_gpu_vs_cpu/gpu0.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/gpu1.png b/figures/circles_gpu_vs_cpu/gpu1.png deleted file mode 100644 index e18e014..0000000 Binary files a/figures/circles_gpu_vs_cpu/gpu1.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/gpu2.png b/figures/circles_gpu_vs_cpu/gpu2.png deleted file mode 100644 index 954a229..0000000 Binary files a/figures/circles_gpu_vs_cpu/gpu2.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/gpu3.png b/figures/circles_gpu_vs_cpu/gpu3.png deleted file mode 100644 index 55d8d00..0000000 Binary files a/figures/circles_gpu_vs_cpu/gpu3.png and /dev/null differ diff --git a/figures/circles_gpu_vs_cpu/intel.png b/figures/circles_gpu_vs_cpu/intel.png new file mode 100644 index 0000000..aa70955 Binary files /dev/null and b/figures/circles_gpu_vs_cpu/intel.png differ diff --git a/figures/circles_gpu_vs_cpu/nvidia.png b/figures/circles_gpu_vs_cpu/nvidia.png new file mode 100644 index 0000000..85a2d0a Binary files /dev/null and b/figures/circles_gpu_vs_cpu/nvidia.png differ diff --git a/figures/circles_gpu_vs_cpu/x86-64.png b/figures/circles_gpu_vs_cpu/x86-64.png new file mode 100644 index 0000000..03d9a94 Binary files /dev/null and b/figures/circles_gpu_vs_cpu/x86-64.png differ