Paul Heidmann Fractal Example  1.0
threadedFractalEngine.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 // Copyright (C) 2014 Paul S. Heidmann
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // For a copy of the GNU General Public License see
16 // <http://www.gnu.org/licenses/>.
17 //
18 // Author contact info:
19 // Paul Heidmann
20 // paul@heidmann.com
21 
22 #include <deque>
23 #include <mutex>
24 #include <vector>
25 #include <memory>
26 #include <thread>
27 #include <bitset>
28 #include <cstdint>
29 #include <iostream>
30 #include <algorithm>
31 #include <functional>
32 #include <boost/utility.hpp>
33 
34 #include <math.h> // for fabs
35 
36 #include <pngFactory.hpp>
37 #include <fractalParams.hpp>
38 #include <newtonsMethod.hpp>
39 
40 namespace fractal
41 {
42 
43 /// \brief This class splits the work of computing a given fractal amoung
44 /// several threads.
45 ///
46 /// This class splits the work of computing a given fractal among several
47 /// threads. This is done to take advantage of machines with multiple cores.
48 /// The number of threads spawned is settable (see m_numThreads). The
49 /// fractal is split by rows (that is, lines with a constant imaginary
50 /// component in the complex plane), with each row being the unit of
51 /// work for a thread. Computation continues by all threads until all
52 /// rows are computed.
53 /// \tparam floatType The underlying floating point type to be used in
54 /// all computations. This will also be the type used
55 /// to instantiate all complex numbers.
56 /// \author Paul S. Heidmann
57 template <typename floatType>
58 class threadedFractalEngine : private boost::noncopyable
59 {
60 public:
61 
62  /// \brief This type is used to define variables that hold colors.
63  /// The standard RGB color definition is used here.
64  typedef std::tuple<std::uint8_t, std::uint8_t, std::uint8_t> colorType;
65 
66  threadedFractalEngine() = delete;
67 
68  /// \brief The class destructor.
70 
71  /// \brief This is the constructor that must be used to create instances
72  /// of this class.
73  /// \param fParams The parameters used to generate the given fractal.
74  /// \param pngFact The PNG factory class used to create the PNG image.
76  const std::shared_ptr<fractal::fractalParams<floatType>>& fParams,
77  const std::shared_ptr<png::pngFactory>& pngFact ) :
78  m_numThreads{ 8 },
79  m_fParams{ fParams },
80  m_pngFact{ pngFact },
81  m_tenthOfRange{ 0 }
82  {
83  }
84 
85  /// \brief This method is called to compute the given fractal.
86  void doFractal( void )
87  {
88  // First, load up the row queue, before spawning any worker threads.
89  // That way, the queue being empty will always indicate that all
90  // work has been completed.
91  {
92  std::lock_guard<std::mutex> lk{ this->m_rowQueueGuard };
93  for( floatType im{ this->m_fParams->lowerLeftExtent.imag() };
94  im < this->m_fParams->upperRightExtent.imag();
95  im += this->m_fParams->delta.imag() )
96  this->m_rowQueue.push_back( im );
97  }
98 
99  // Compute tenths of the range. This will be for the status count.
100  this->m_tenthOfRange = (fabs(
101  this->m_fParams->upperRightExtent.imag() -
102  this->m_fParams->lowerLeftExtent.imag() )) / 10.0;
103  std::cout << "Count to ten:" << std::endl;
104 
105  // Spawn the worker threads.
106  for( std::uint32_t thr{0}; thr < (this->m_numThreads); ++thr )
107  {
108  std::unique_ptr<std::thread> curThr{
109  new std::thread{
110  std::bind(
112  this ) } };
113  this->m_threads.push_back( std::move(curThr) );
114  }
115 
116  // Wait for each thread to finish. When all threads have
117  // exitted, the work is complete.
118  std::for_each(
119  this->m_threads.begin(),
120  this->m_threads.end(),
121  []( decltype(*(this->m_threads.begin()))& thrd ) -> void
122  {
123  thrd->join();
124  } );
125  std::cout << std::endl;
126  }
127 
128 private:
129 
130  /// \brief This attribute holds the number of threads that this instance
131  /// is to spawn to compute the given fractal.
132  const std::uint32_t m_numThreads;
133 
134  /// \brief This attribute holds a pointer to the class instance that
135  /// contains the parameters used to generate the fractal.
136  const std::shared_ptr<fractal::fractalParams<floatType>> m_fParams;
137 
138  /// \brief This attribute holds a pointer to the factory used to generate
139  /// the PNG image.
140  const std::shared_ptr<png::pngFactory> m_pngFact;
141 
142  /// \brief This attribute holds a mutex used to serialize access to the
143  /// PNG factory.
144  std::mutex m_pngFactGuard;
145 
146  /// \brief This attribute is the queue that holds the rows of the fractal
147  /// yet to be computed.
148  std::deque<floatType> m_rowQueue;
149 
150  /// \brief This attribute holds the mutex that guards access to the
151  /// row queue.
152  /// \sa m_rowQueue
153  std::mutex m_rowQueueGuard;
154 
155  /// \brief This attribute holds the thread objects used to compute the
156  /// given fractal.
157  std::vector<std::unique_ptr<std::thread>> m_threads;
158 
159  /// \brief This attribute holds a tenth of the imaginary range (that is,
160  /// a tenth of the rows). This is used in the status count.
161  floatType m_tenthOfRange;
162 
163  /// \brief This attribute holds ten booleans. These booleans are set when
164  /// the corresponding status number has been printed. Note that the
165  /// default constructor sets all bits to false, and this class takes
166  /// advantage of this fact.
167  std::bitset<10> m_tenthStatusPrinted;
168 
169  /// \brief This method is the main (top level) method of each of the fractal
170  /// computation threads.
171  void fractalWorkerThread( void )
172  {
173  floatType curRow{ 0.0 };
174  std::int32_t statNum{ -1 };
175  while( true )
176  {
177  {
178  std::lock_guard<std::mutex> lk{ this->m_rowQueueGuard };
179  if( this->m_rowQueue.empty() )
180  return;
181  curRow = this->m_rowQueue.front();
182  this->m_rowQueue.pop_front();
183 
184  // See if it is time to print a status number. Note that this is
185  // done while the mutex is still locked, for obvious reasons.
186  const std::uint32_t tenth(
187  static_cast<std::uint32_t>(
188  (curRow - this->m_fParams->lowerLeftExtent.imag()) / (this->m_tenthOfRange) ) );
189  if( !(this->m_tenthStatusPrinted[tenth]) )
190  {
191  this->m_tenthStatusPrinted.set( tenth, true );
192  statNum = tenth;
193  }
194  else
195  {
196  statNum = -1;
197  }
198  }
199 
200  if( statNum >= 0 )
201  std::cout << statNum << std::flush;
202 
203  for( floatType rl{ this->m_fParams->lowerLeftExtent.real() };
204  rl < this->m_fParams->upperRightExtent.real();
205  rl += this->m_fParams->delta.real() )
206  {
207  try
208  {
210  std::complex<floatType>( rl, curRow ),
211  this->m_fParams->f,
212  this->m_fParams->f_prime,
213  this->m_fParams->maxIterations,
214  this->m_fParams->zeroEpsilon };
215  std::uint32_t numIterations{ 0 };
216  const std::complex<floatType> zero{
217  solver.findZero( numIterations ) };
218  const colorType color{
219  this->m_fParams->colorMapFunc(
220  this->m_fParams->maxIterations,
221  zero,
222  numIterations ) };
223  {
224  std::lock_guard<std::mutex> lk{ this->m_pngFactGuard };
225  this->m_pngFact->addPixel(
226  rl,
227  curRow,
228  std::get<0>(color),
229  std::get<1>(color),
230  std::get<2>(color) );
231  }
232  }
233  catch( ... )
234  {
235  std::lock_guard<std::mutex> lk{ this->m_pngFactGuard };
236  this->m_pngFact->addPixel(
237  rl,
238  curRow,
239  0,
240  0,
241  0 );
242  }
243  }
244  }
245  }
246 };
247 
248 }
std::bitset< 10 > m_tenthStatusPrinted
This attribute holds ten booleans. These booleans are set when the corresponding status number has be...
~threadedFractalEngine()
The class destructor.
std::tuple< std::uint8_t, std::uint8_t, std::uint8_t > colorType
This type is used to define variables that hold colors. The standard RGB color definition is used her...
threadedFractalEngine(const std::shared_ptr< fractal::fractalParams< floatType >> &fParams, const std::shared_ptr< png::pngFactory > &pngFact)
This is the constructor that must be used to create instances of this class.
const std::shared_ptr< fractal::fractalParams< floatType > > m_fParams
This attribute holds a pointer to the class instance that contains the parameters used to generate th...
void doFractal(void)
This method is called to compute the given fractal.
std::mutex m_rowQueueGuard
This attribute holds the mutex that guards access to the row queue.
This class splits the work of computing a given fractal amoung several threads.
const std::uint32_t m_numThreads
This attribute holds the number of threads that this instance is to spawn to compute the given fracta...
floatType m_tenthOfRange
This attribute holds a tenth of the imaginary range (that is, a tenth of the rows). This is used in the status count.
std::deque< floatType > m_rowQueue
This attribute is the queue that holds the rows of the fractal yet to be computed.
std::mutex m_pngFactGuard
This attribute holds a mutex used to serialize access to the PNG factory.
This class is a container class for the parameters that are used to generate a fractal.
This class implements Newton&#39;s method (the same one from your typical first year calculus class)...
void fractalWorkerThread(void)
This method is the main (top level) method of each of the fractal computation threads.
const std::shared_ptr< png::pngFactory > m_pngFact
This attribute holds a pointer to the factory used to generate the PNG image.
std::vector< std::unique_ptr< std::thread > > m_threads
This attribute holds the thread objects used to compute the given fractal.