ROOT  6.07/01
Reference Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
main.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 Jochen Gerhard <gerhard@compeng.uni-frankfurt.de>
3  Copyright (C) 2010-2012 Matthias Kretz <kretz@kde.org>
4 
5  Permission to use, copy, modify, and distribute this software
6  and its documentation for any purpose and without fee is hereby
7  granted, provided that the above copyright notice appear in all
8  copies and that both that the copyright notice and this
9  permission notice and warranty disclaimer appear in supporting
10  documentation, and that the name of the author not be used in
11  advertising or publicity pertaining to distribution of the
12  software without specific, written prior permission.
13 
14  The author disclaim all warranties with regard to this
15  software, including all implied warranties of merchantability
16  and fitness. In no event shall the author be liable for any
17  special, indirect or consequential damages or any damages
18  whatsoever resulting from loss of use, data or profits, whether
19  in an action of contract, negligence or other tortious action,
20  arising out of or in connection with the use or performance of
21  this software.
22 
23 */
24 
25 /*!
26  Finite difference method example
27 
28  We calculate central differences for a given function and
29  compare it to the analytical solution.
30 
31 */
32 
33 #include <Vc/Vc>
34 #include <iostream>
35 #include <iomanip>
36 #include <cmath>
37 #include "../tsc.h"
38 #include <Vc/common/macros.h>
39 
40 #define USE_SCALAR_SINCOS
41 
42 enum {
43  N = 10240000,
44  PrintStep = 1000000
45 };
46 
47 static const float epsilon = 1e-7f;
48 static const float lower = 0.f;
49 static const float upper = 40000.f;
50 static const float h = (upper - lower) / N;
51 
52 // dfu is the derivative of fu. This is really easy for sine and cosine:
53 static inline float fu(float x) { return ( std::sin(x) ); }
54 static inline float dfu(float x) { return ( std::cos(x) ); }
55 
56 static inline Vc::float_v fu(Vc::float_v::AsArg x) {
57 #ifdef USE_SCALAR_SINCOS
58  Vc::float_v r;
59  for (int i = 0; i < Vc::float_v::Size; ++i) {
60  r[i] = std::sin(x[i]);
61  }
62  return r;
63 #else
64  return Vc::sin(x);
65 #endif
66 }
67 
68 static inline Vc::float_v dfu(Vc::float_v::AsArg x) {
69 #ifdef USE_SCALAR_SINCOS
70  Vc::float_v r;
71  for (int i = 0; i < Vc::float_v::Size; ++i) {
72  r[i] = std::cos(x[i]);
73  }
74  return r;
75 #else
76  return Vc::cos(x);
77 #endif
78 }
79 
80 using Vc::float_v;
81 
82 // It is important for this example that the following variables (especially dy_points) are global
83 // variables. Else the compiler can optimze all calculations of dy away except for the few places
84 // where the value is used in printResults.
88 
90 {
91  std::cout
92  << "------------------------------------------------------------\n"
93  << std::setw(15) << "fu(x_i)"
94  << std::setw(15) << "FD fu'(x_i)"
95  << std::setw(15) << "SYM fu'(x)"
96  << std::setw(15) << "error %\n";
97  for (int i = 0; i < N; i += PrintStep) {
98  std::cout
99  << std::setw(15) << y_points[i]
100  << std::setw(15) << dy_points[i]
101  << std::setw(15) << dfu(x_points[i])
102  << std::setw(15) << std::abs((dy_points[i] - dfu(x_points[i])) / (dfu(x_points[i] + epsilon)) * 100)
103  << "\n";
104  }
105  std::cout
106  << std::setw(15) << y_points[N - 1]
107  << std::setw(15) << dy_points[N - 1]
108  << std::setw(15) << dfu(x_points[N - 1])
109  << std::setw(15) << std::abs((dy_points[N - 1] - dfu(x_points[N - 1])) / (dfu(x_points[N - 1] + epsilon)) * 100)
110  << std::endl;
111 }
112 
113 int main()
114 {
115  {
117  for ( unsigned int i = 0; i < x_points.vectorsCount(); ++i, x_i += float_v::Size ) {
118  const float_v x = x_i * h;
119  x_points.vector(i) = x;
120  y_points.vector(i) = fu(x);
121  }
122  }
123 
124  dy_points = Vc::malloc<float, Vc::AlignOnVector>(N + float_v::Size - 1) + (float_v::Size - 1);
125 
126  double speedup;
128 
129  { ///////// ignore this part - it only wakes up the CPU ////////////////////////////
130  const float oneOver2h = 0.5f / h;
131 
132  // set borders explicit as up- or downdifferential
133  dy_points[0] = (y_points[1] - y_points[0]) / h;
134  // GCC auto-vectorizes the following loop. It is interesting to see that both Vc::Scalar and
135  // Vc::SSE are faster, though.
136  for ( int i = 1; i < N - 1; ++i) {
137  dy_points[i] = (y_points[i + 1] - y_points[i - 1]) * oneOver2h;
138  }
139  dy_points[N - 1] = (y_points[N - 1] - y_points[N - 2]) / h;
140  } //////////////////////////////////////////////////////////////////////////////////
141 
142  {
143  std::cout << "\n" << std::setw(60) << "Classical finite difference method" << std::endl;
144  timer.Start();
145 
146  const float oneOver2h = 0.5f / h;
147 
148  // set borders explicit as up- or downdifferential
149  dy_points[0] = (y_points[1] - y_points[0]) / h;
150  // GCC auto-vectorizes the following loop. It is interesting to see that both Vc::Scalar and
151  // Vc::SSE are faster, though.
152  for ( int i = 1; i < N - 1; ++i) {
153  dy_points[i] = (y_points[i + 1] - y_points[i - 1]) * oneOver2h;
154  }
155  dy_points[N - 1] = (y_points[N - 1] - y_points[N - 2]) / h;
156 
157  timer.Stop();
158  printResults();
159  std::cout << "cycle count: " << timer.Cycles()
160  << " | " << static_cast<double>(N * 2) / timer.Cycles() << " FLOP/cycle"
161  << " | " << static_cast<double>(N * 2 * sizeof(float)) / timer.Cycles() << " Byte/cycle"
162  << "\n";
163  }
164 
165  speedup = timer.Cycles();
166  {
167  std::cout << std::setw(60) << "Vectorized finite difference method" << std::endl;
168  timer.Start();
169 
170  // All the differentials require to calculate (r - l) / 2h, where we calculate 1/2h as a
171  // constant before the loop to avoid unnecessary calculations. Note that a good compiler can
172  // already do this for you.
173  const float_v oneOver2h = 0.5f / h;
174 
175  // Calculate the left border
176  dy_points[0] = (y_points[1] - y_points[0]) / h;
177 
178  // Calculate the differentials streaming through the y and dy memory. The picture below
179  // should give an idea of what values in y get read and what values are written to dy in
180  // each iteration:
181  //
182  // y [...................................]
183  // 00001111222233334444555566667777
184  // 00001111222233334444555566667777
185  // dy [...................................]
186  // 00001111222233334444555566667777
187  //
188  // The loop is manually unrolled four times to improve instruction level parallelism and
189  // prefetching on architectures where four vectors fill one cache line. (Note that this
190  // unrolling breaks auto-vectorization of the Vc::Scalar implementation when compiling with
191  // GCC.)
192  for (unsigned int i = 0; i < (y_points.entriesCount() - 2) / float_v::Size; i += 4) {
193  // Prefetches make sure the data which is going to be used in 24/4 iterations is already
194  // in the L1 cache. The prefetchForOneRead additionally instructs the CPU to not evict
195  // these cache lines to L2/L3.
196  Vc::prefetchForOneRead(&y_points[(i + 24) * float_v::Size]);
197 
198  // calculate float_v::Size differentials per (left - right) / 2h
199  const float_v dy0 = (y_points.vector(i + 0, 2) - y_points.vector(i + 0)) * oneOver2h;
200  const float_v dy1 = (y_points.vector(i + 1, 2) - y_points.vector(i + 1)) * oneOver2h;
201  const float_v dy2 = (y_points.vector(i + 2, 2) - y_points.vector(i + 2)) * oneOver2h;
202  const float_v dy3 = (y_points.vector(i + 3, 2) - y_points.vector(i + 3)) * oneOver2h;
203 
204  // Use streaming stores to reduce the required memory bandwidth. Without streaming
205  // stores the CPU would first have to load the cache line, where the store occurs, from
206  // memory into L1, then overwrite the data, and finally write it back to memory. But
207  // since we never actually need the data that the CPU fetched from memory we'd like to
208  // keep that bandwidth free for real work. Streaming stores allow us to issue stores
209  // which the CPU gathers in store buffers to form full cache lines, which then get
210  // written back to memory directly without the costly read. Thus we make better use of
211  // the available memory bandwidth.
212  dy0.store(&dy_points[(i + 0) * float_v::Size + 1], Vc::Streaming);
213  dy1.store(&dy_points[(i + 1) * float_v::Size + 1], Vc::Streaming);
214  dy2.store(&dy_points[(i + 2) * float_v::Size + 1], Vc::Streaming);
215  dy3.store(&dy_points[(i + 3) * float_v::Size + 1], Vc::Streaming);
216  }
217 
218  // Process the last vector. Note that this works for any N because Vc::Memory adds padding
219  // to y_points and dy_points such that the last scalar value is somewhere inside lastVector.
220  // The correct right border value for dy_points is overwritten in the last step unless N is
221  // a multiple of float_v::Size + 2.
222  // y [...................................]
223  // 8888
224  // 8888
225  // dy [...................................]
226  // 8888
227  {
228  const size_t i = y_points.vectorsCount() - 1;
229  const float_v left = y_points.vector(i, -2);
230  const float_v right = y_points.lastVector();
231  ((right - left) * oneOver2h).store(&dy_points[i * float_v::Size - 1], Vc::Unaligned);
232  }
233 
234  // ... and finally the right border
235  dy_points[N - 1] = (y_points[N - 1] - y_points[N - 2]) / h;
236 
237  timer.Stop();
238  printResults();
239  std::cout << "cycle count: " << timer.Cycles()
240  << " | " << static_cast<double>(N * 2) / timer.Cycles() << " FLOP/cycle"
241  << " | " << static_cast<double>(N * 2 * sizeof(float)) / timer.Cycles() << " Byte/cycle"
242  << "\n";
243  }
244  speedup /= timer.Cycles();
245  std::cout << "Speedup: " << speedup << "\n";
246 
248  return 0;
249 }
static float fu(float x)
Definition: main.cpp:53
const char * Size
Definition: TXMLSetup.cxx:56
_VC_CONSTEXPR size_t vectorsCount() const
Definition: memory.h:169
Vc::Memory< float_v, N > y_points
Definition: main.cpp:86
static const float epsilon
Definition: main.cpp:47
void Stop()
Definition: tsc.h:53
static const float upper
Definition: main.cpp:49
double cos(double)
TFile * f
_VC_CONSTEXPR size_t entriesCount() const
Definition: memory.h:163
TStopwatch timer
Definition: pirndm.C:37
Double_t x[n]
Definition: legend1.C:17
Vc::Memory< float_v, N > x_points
Definition: main.cpp:85
unsigned long long Cycles() const
Definition: tsc.h:63
static Vc_ALWAYS_INLINE Vector< T > abs(const Vector< T > &x)
Definition: vector.h:450
Vc_ALWAYS_INLINE void prefetchForOneRead(const void *addr)
Prefetch the cacheline containing addr for a single read access.
Definition: memory.h:565
Vc_ALWAYS_INLINE Vc_PURE VectorPointerHelper< V, AlignedFlag > vector(size_t i)
Definition: memorybase.h:310
double sin(double)
float *VC_RESTRICT dy_points
Definition: main.cpp:87
ROOT::R::TRInterface & r
Definition: Object.C:4
void printResults()
Definition: main.cpp:89
tuple free
Definition: fildir.py:30
A helper class for fixed-size two-dimensional arrays.
Definition: memory.h:120
Definition: main.cpp:43
int main(int argc, char **argv)
Main program.
Definition: main.cpp:22
#define VC_RESTRICT
Definition: macros.h:145
VECTOR_NAMESPACE::float_v float_v
Definition: vector.h:84
static float dfu(float x)
Definition: main.cpp:54
void Start()
Definition: tsc.h:43
Vc_ALWAYS_INLINE Vc_PURE VectorPointerHelper< V, AlignedFlag > lastVector()
Definition: memorybase.h:427
static const float lower
Definition: main.cpp:48
static const float h
Definition: main.cpp:50
Vector< float > float_v
Definition: vector.h:417