Cas 1 – calculs concurrents, couplage plus faible

Le cas 1, « calculs concurrents, couplage plus faible », tel que proposé dans vos notes de cours, se présente comme suit.

struct Probleme
{
   virtual bool est_resolu() const volatile = 0;
   virtual void resoudre_un() volatile = 0;
   virtual ~Probleme() throw()
      { }
};
unsigned long __stdcall calcul(void *p)
{
   for (Probleme *prob = static_cast<Probleme *>(p);
        !prob->est_resolu(); )
      prob->resoudre_un();
   return 0UL;
}


#include <algorithm>
class Navire
{
public:
   typedef short poids_t;
   enum { NB_SOUTES = 20 };
private:
   poids_t soutes_[NB_SOUTES];
   int no_;
   bool complete_, en_cours_;
public:
   int numero() const throw()
      { return no_; }
   bool operator<(const Navire &n) const throw()
      { return numero() < n.numero(); }
   Navire(const int no)
      : no_(no), complete_(false), en_cours_(false)
   {
      using std::fill;
      fill(begin(), end(), poids_t());
   }
   void placer(const poids_t p)
   {
      using std::min_element;
      debut_travail();
      *(min_element(begin(), end())) += p;
      complete_ = true;
   }
   void debut_travail() throw()
      { en_cours_ = true; }
   bool est_place() const throw()
      { return complete_; }
   bool est_en_cours() const throw()
      { return en_cours_; }
   bool pas_en_cours() const throw()
      { return !en_cours_; }
   typedef poids_t* iterator;
   typedef const poids_t* const_iterator;
   iterator begin() throw()
      { return soutes_; }
   const_iterator begin() const throw()
      { return soutes_; }
   iterator end() throw()
      { return std::end(soutes_); }
   const_iterator end() const throw()
      { return std::end(soutes_); }
};
template <class T>
   class Sequence
   {
      T cur_;
   public:
      Sequence(T base)
         : cur_(base)
      {
      }
      T operator()()
         { return cur_++; }
   };
template <class T>
   Sequence<T> creer_sequence(T init)
      { return Sequence<T>(init); }
//#include "Mutex.h"
//#include "Incopiable.h"
#include <windows.h>
//#include "Mutex.h"
class Incopiable
{
   Incopiable(const Incopiable &);
   void operator=(const Incopiable &);
protected:
   Incopiable() throw() {}
   ~Incopiable() throw() {}
};
class Mutex
{
   HANDLE m_;
public:
   Mutex() : m_(CreateMutex(0, FALSE, 0)) {}
   ~Mutex() throw() { CloseHandle(m_); }
   void obtenir() const volatile throw() { WaitForSingleObject(m_, INFINITE); }
   void relacher() const volatile throw() { ReleaseMutex(m_); }
};
class Autoverrou
{
   const volatile Mutex &m_;
public:
   Autoverrou(const volatile Mutex &m) throw() : m_(m) { m_.obtenir(); }
   ~Autoverrou() throw() { m_.relacher(); }
};

#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <iterator>
class Chargement
   : Incopiable, public Probleme
{
   typedef std::vector<Navire> vnav;
public:
   typedef Navire::poids_t poids_t;
   enum { NB_NAVIRES = 1000 };
private:
   Mutex mutex_;
   vnav navires_;
   class ACharger
   {
      std::vector<poids_t> cargo_;
      ACharger()
      {
         using std::srand;
         using std::time;
         using std::generate_n;
         srand(static_cast<unsigned int>(time(0)));
         const int NELEMS = 100000;
         generate_n(back_inserter(cargo_), NELEMS, []() {
            return static_cast<poids_t>(rand() % 40 + 10);
         });
      }
   public:
      typedef std::vector<poids_t>::iterator iterator;
      typedef std::vector<poids_t>::const_iterator const_iterator;
      static ACharger &get()
      {
         static ACharger singleton;
         return singleton;
      }
      iterator begin() throw()
         { return std::begin(cargo_); }
      const_iterator begin() const throw()
         { return std::begin(cargo_); }
      iterator end() throw()
         { return std::end(cargo_); }
      const_iterator end() const throw()
         { return std::end(cargo_); }
   };
   bool est_resolu() const
   {
      using std::count_if;
      return count_if(
         std::begin(navires_), std::end(navires_), [](const Navire &nav) {
            return nav.est_place();
         }) == navires_.size();
   }
   void resoudre_un()
   {
      using std::find_if;
      using std::for_each;
      auto itt = find_if(navires_.begin(),navires_.end(), [](const Navire &nav) {
         return nav.pas_en_cours();
      });
      if (itt == std::end(navires_)) return;
      itt->debut_travail();
      for_each(std::begin(ACharger::get()), std::end(ACharger::get()), [&itt](const poids_t poids) {
         itt->placer(poids);
      });
   }
public:
   Chargement()
   {
      using std::generate_n;
      using std::back_inserter;
      int n = NB_NAVIRES;
      generate_n(back_inserter<vnav>(navires_), n, creer_sequence(0));
   }
   bool est_resolu() const volatile
   {
      Autoverrou av(mutex_);
      return const_cast<const Chargement*>(this)->est_resolu();
   }
   void resoudre_un() volatile
   {
      Autoverrou av(mutex_);
      const_cast<Chargement*>(this)->resoudre_un();
   }
   std::vector<Navire> resultats() const
      { return navires_; }
};
#include <iostream>
#include <ctime>
#include <windows.h>
int main()
{
   using std::begin;
   using std::end;
   using std::cout;
   using std::endl;
   using std::clock;
   using std::clock_t;
   using std::vector;
   using std::for_each;
   const int NTHREADS = 10;
   for (int n = 1; n <= NTHREADS; ++n)
   {
      Chargement cn;
      vector<HANDLE> v(n);
      for (int i = 0; i < n; ++i)
         v[i] = CreateThread(0, 0, calcul, &cn, CREATE_SUSPENDED, 0);
      clock_t avant = clock();
      for_each(begin(v), end(v), ResumeThread);
      WaitForMultipleObjects(n, &v[0], TRUE, INFINITE);
      clock_t apres = clock();
      for_each(begin(v), end(v), CloseHandle);
      cout << "Temps total, "
           << Chargement::NB_NAVIRES << " navires, "
           << Navire::NB_SOUTES << " soutes, "
           << n << " thread(s): "
           << static_cast<double>(apres-avant) /
              CLOCKS_PER_SEC * 1000
           << " ms." << endl;
   }
}

Sur mon petit ordinateur portatif, compilé avec Visual Studio 2010, configuration Release, et en prenant bien entendu soin d'ajouter _SECURE_SCL=0 aux options du préprocesseur pour désactiver les (franchement inutiles et nuisibles) Checked Iterators, j'obtiens :

Temps total, 1000 navires, 20 soutes, 1 thread(s): 7978 ms.
Temps total, 1000 navires, 20 soutes, 2 thread(s): 7581 ms.
Temps total, 1000 navires, 20 soutes, 3 thread(s): 7645 ms.
Temps total, 1000 navires, 20 soutes, 4 thread(s): 7653 ms.
Temps total, 1000 navires, 20 soutes, 5 thread(s): 7519 ms.
Temps total, 1000 navires, 20 soutes, 6 thread(s): 7685 ms.
Temps total, 1000 navires, 20 soutes, 7 thread(s): 7440 ms.
Temps total, 1000 navires, 20 soutes, 8 thread(s): 7299 ms.
Temps total, 1000 navires, 20 soutes, 9 thread(s): 7521 ms.
Temps total, 1000 navires, 20 soutes, 10 thread(s): 7581 ms.
Appuyez sur une touche pour continuer...

Valid XHTML 1.0 Transitional

CSS Valide !