From: bangerth Date: Fri, 31 Aug 2012 14:20:58 +0000 (+0000) Subject: Remove a link. Add a note. X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=751f8edffaf644afd29276fe07fe59f34464f767;p=dealii-svn.git Remove a link. Add a note. git-svn-id: https://svn.dealii.org/trunk@26181 0785d39b-7218-0410-832d-ea1e28bc413d --- diff --git a/deal.II/doc/reports/new-threads/index.html b/deal.II/doc/reports/new-threads/index.html index bff69d0e9e..b146117901 100644 --- a/deal.II/doc/reports/new-threads/index.html +++ b/deal.II/doc/reports/new-threads/index.html @@ -10,8 +10,10 @@ A short description of the new threading scheme

-Since the report on -multithreading was written in 2000, we have put in place a new +Since the first deal.II report on +multithreading was written in 2000 (see the list of deal.II +publications), +we have put in place a new implementation of the threading scheme (the first release to contain it is 4.0). The new scheme can do all that you could do before, so the report is in a sense still valid, but it describes a syntax that is no more used. We will here @@ -21,6 +23,14 @@ that use it must look like, and for pitfalls to watch out for, please still refer to the report mentioned above.

+

+We note that since this report was written, there has been another +incarnation of support for multicore machines, namely by using the +Threading Building Blocks and tasks. The documentation module on +parallel computing, available through the modules page of the deal.II +manual, explains this new direction in more detail. +

+

1. Rationale and Introduction

@@ -75,7 +85,7 @@ Regarding the last point, note that any other function is called by obj.f(arg1, arg2); Ideally, the following syntax for starting any function on a new -thread would be nice: +thread would be nice:
     spawn f(arg1, arg2);
     spawn obj.f(arg1,arg2);
@@ -97,7 +107,7 @@ this syntax, as well as the following points:
 
  • Overloading spawn() so as to take unbound functions and member functions, whether virtual or static. Of course, every call needs to be type -safe, i.e. the exact +safe, i.e. the exact same conversions of arguments need to be performed as in a usual call (except for two additional copies that are necessary).
  • spawn() needs to return a value that allows us to identify, @@ -123,7 +133,7 @@ we want to be able to retrieve it once the thread has finished: collectively, rather than one-by-one.

- +

Basically, the syntax above is all you need to know. It is as simple as that. The rest of this text, in comparison is very much of technical nature. I @@ -154,7 +164,7 @@ in the library; all entities that are not to be used by the user are placed into a namespace internal, those to be used are in a namespace Threads. The implementation uses Boost's shared_ptr. Some parts of -the implementation parallel the +the implementation parallel the boost::function library, but they are small and taylored to the particular purpose at hand; in particular, they make heavy use of the boost::tuple library. We note that the code has in some places already evolved @@ -185,11 +195,11 @@ threads. For POSIX threads, this class looks as follows: mutable volatile bool was_joined; mutable boost::mutex join_mutex; mutable boost::condition join_condition; - + public: thread_description_base () : was_joined (false) {}; virtual ~thread_description_base () { /* ... */ }; - + void create (void * (*p) (void *), void *d) { pthread_create (&pt, 0, p, d); }; @@ -201,7 +211,7 @@ threads. For POSIX threads, this class looks as follows: if (!was_joined) pthread_join (pt, 0); was_joined = true; - }; + }; };

@@ -247,7 +257,7 @@ called. As mentioned, there is exactly one thread_description<RT> object per created thread. It is accessed using boost::shared_ptr objects, and references are held from each Thread<RT> object -for this thread as +for this thread as well as from a wrapper function on the new thread. The object is thus deleted, when all Thread<RT> objects for this thread have gone out of scope (or point to different threads) and the thread itself has @@ -272,19 +282,19 @@ implementation: public: Thread () {}; Thread (const boost::shared_ptr<thread_description<RT> > &td) - : thread_description (td) {}; - + : thread_description (td) {}; + void join () const { thread_description->join (); }; - + RT return_value () { join (); return thread_description->ret_val.get(); }; - + bool operator == (const thread &t) { return thread_description == t.thread_description; }; - + private: boost::shared_ptr<thread_description<RT> > thread_description; }; @@ -313,20 +323,20 @@ template argument:
     template <typename RT = void>
-    class ThreadGroup 
+    class ThreadGroup
     {
       public:
         ThreadGroup & operator += (const Thread<RT> &t) {
           threads.push_back (t);
 	  return *this;
         };
-    
+
         void join_all () const {
           for (typename std::vector<Thread<RT> >::const_iterator
                  t=threads.begin(); t!=threads.end(); ++t)
             t->join ();
         };
-        
+
       private:
         std::vector<Thread<RT> > threads;
     };
@@ -334,7 +344,7 @@ template argument:
 
 

Since objects of type Thread<RT> are freely copyable, there -is no need +is no need to provide an index operator for ThreadGroup; if you need to index its elements (for example to get at the return value), use std::vector<Thread<RT> >. @@ -350,7 +360,7 @@ functions. We only show them for unary member functions: spawn (C &c, RT (C::*fun_ptr)(Arg1)) { return mem_fun_encapsulator<RT, C, boost::tuple<Arg1> > (c,fun_ptr); } - + template <typename RT, typename C, typename Arg1> mem_fun_encapsulator<RT,const C,boost::tuple<Arg1> > spawn (const C &c, RT (C::*fun_ptr)(Arg1) const) { @@ -361,9 +371,9 @@ functions. We only show them for unary member functions:

Note that we need two overloaded versions, for const and -non-const +non-const member functions. Both create an intermediate object (in the -internal +internal namespace) that will accept arguments in place of the function being called on the new thread, make sure a new thread is created, copy the arguments to the new thread's stack, and only then return. The exact @@ -377,7 +387,7 @@ functions. One would really like to have something also for objects other than pointers to (member-)functions that provide an operator(). However, this doesn't seem to be possible if operator() returns something other than void or takes -arguments. This +arguments. This would need some kind of typeof-operator which is not standard C++. See the discussion in the Open Problems section.

@@ -407,18 +417,18 @@ looks like this: template <typename RT, typename C, typename ArgList> class mem_fun_encapsulator<RT,C,ArgList,1> { - typedef typename mem_fun_ptr<RT,C,ArgList>::type MemFunPtr; - + typedef typename mem_fun_ptr<RT,C,ArgList>::type MemFunPtr; + public: mem_fun_encapsulator (C &c, MemFunPtr mem_fun_ptr) : c (c), mem_fun_ptr(mem_fun_ptr) {}; - - Thread<RT> + + Thread<RT> operator() (typename boost::tuples::element<0,ArgList>::type arg1) { return mem_fun_wrapper<RT,C,ArgList> (mem_fun_ptr, c, boost::tie(arg1)).fire_up (); }; - + private: C &c; MemFunPtr mem_fun_ptr; @@ -433,7 +443,7 @@ template is never used.)

-The constructor stores the two addresses. If one calls +The constructor stores the two addresses. If one calls

     spawn(obj, &C::f) (42);
 
@@ -446,7 +456,7 @@ arguments, but bind them all together with boost::tie, so that we n not have different versions of the mem_fun_wrapper class for different numbers of arguments. (However, we need a separate partial specialization of the mem_fun_encapsulator class for each number of -function arguments.) The tie_args template is used to make a version +function arguments.) The tie_args template is used to make a version of the ArgList type with all reference types; it is described below.

@@ -460,22 +470,22 @@ us first consider the base class that it has in common with Thread<RT> fire_up () { thread_descriptor = DescriptionPointer(new typename thread_description<RT>()); - - boost::mutex::scoped_lock lock (mutex); + + boost::mutex::scoped_lock lock (mutex); thread_descriptor->create (&EntryPointClass::entry_point, (void *)this); condition.wait (lock); - + return thread_descriptor; } - + protected: typedef boost::shared_ptr<thread_description<RT> > DescriptionPointer; - + DescriptionPointer thread_descriptor; - - mutable boost::mutex mutex; + + mutable boost::mutex mutex; mutable boost::condition condition; };
@@ -485,11 +495,11 @@ object, and calls it with a pointer to the present object, and the address of the starting point is EntryPointClass::entry_point, where EntryPoint is the name of a class that implements this thread starting function and is passed as a template argument to -wrapper_base. +wrapper_base. Before it starts the new thread, it acquires a mutex and afterwards wait until a condition is signalled before it finishes by using the thread descriptor object to generate a Thread<RT> -object. +object.

@@ -497,9 +507,9 @@ The magic happens in the derived class:

     template <typename RT, class C, typename ArgList>
     struct mem_fun_wrapper
-       : public wrapper_base<RT, mem_fun_wrapper<RT,C,ArgList> > 
+       : public wrapper_base<RT, mem_fun_wrapper<RT,C,ArgList> >
     {
-        typedef typename mem_fun_ptr<RT,C,ArgList>::type MemFunPtr;      
+        typedef typename mem_fun_ptr<RT,C,ArgList>::type MemFunPtr;
         typedef typename tie_args<ArgList>::type ArgReferences;
         mem_fun_wrapper (MemFunPtr            mem_fun_ptr,
                          C                   &c,
@@ -510,11 +520,11 @@ The magic happens in the derived class:
       private:
         mem_fun_wrapper ();
         mem_fun_wrapper (const mem_fun_wrapper &);
-        
+
         C            &c;
         MemFunPtr     mem_fun_ptr;
         ArgReferences args;
-        
+
         static void * entry_point (void *arg)
           {
             const wrapper_base<RT> *w
@@ -524,17 +534,17 @@ The magic happens in the derived class:
             MemFunPtr mem_fun_ptr = wrapper->mem_fun_ptr;
             C        &c           = wrapper->c;
             ArgList   args        = wrapper->args;
-  
+
             boost::shared_ptr<thread_description<RT> >
               thread_descriptor  = wrapper->thread_descriptor;
-            
+
             {
               boost::mutex::scoped_lock lock (wrapper->mutex);
               wrapper->condition.notify_one ();
             }
-            
+
             call (mem_fun_ptr, c, args, thread_descriptor->ret_val);
-            
+
             return 0;
           };
     };
@@ -591,7 +601,7 @@ a get() function that returns void.
 

-    template <typename RT> struct return_value 
+    template <typename RT> struct return_value
     {
       private:
         RT value;
@@ -600,7 +610,7 @@ a get() function that returns void.
         void set (RT v) { value = v; }
     };
 
-    template <typename RT> struct return_value<RT &> 
+    template <typename RT> struct return_value<RT &>
     {
       private:
         RT * value;
@@ -642,7 +652,7 @@ number of arguments, in the usual way:
 The Caller class has the following member functions:
 
 
-    template <typename RT> struct Caller 
+    template <typename RT> struct Caller
     {
         template <typename PFun, typename ArgList>
         static void do_call (PFun     fun_ptr,
@@ -727,13 +737,13 @@ this, but I couldn't locate this.
     template <typename Tuple>
     struct tie_args_helper<Tuple,1>
     {
-        typedef 
+        typedef
         boost::tuple<typename add_reference_to_Nth<0,Tuple>::type>
         type;
     };
 
     template <typename Tuple>
-    struct tie_args 
+    struct tie_args
     {
         typedef typename tie_args_helper<Tuple>::type type;
     };
@@ -795,7 +805,7 @@ This only works if operator() satisfies the signature
 

We could add another overload if operator() is -const. However, what one +const. However, what one would like is an overload for more general signatures. Unfortunately, this requires that we can infer type and number of arguments and return type of operator() at the time we declare the return type of @@ -834,7 +844,7 @@ The calling thread would then not have to copy the arguments onto its local stack and signal to the calling thread. It would only have to delete the memory after the call to the user-supplied function returns. Apart from replacing ArgReferences by -ArgList in some places, +ArgList in some places, the scheme would basically just replace *_encapsulator::operator(), fire_up, and thread_entry_point:

@@ -904,7 +914,7 @@ legal:
The question, then, would be: do we want to allow conversions between Thread<double> and Thread<int> objects? -And do we want to allow a +And do we want to allow a conversion from Thread<T> to Thread<void> (i.e.: casting away the return value)?

@@ -919,7 +929,7 @@ different return value types into a ThreadGroup:
     double f1 ();
     int    f2 ();
- 
+
     ThreadTroup<double> tg;
     tg += spawn(f1)();
     tg += spawn(f2)();    // convert Thread<int> to Thread<double>
@@ -950,7 +960,7 @@ types in the chain into a boost::tuple of growing length, and writi
 a function that converts a value of the first type of this tuple to
 the second, to the third, ..., to the last type in the tuple. However,
 a plethora of internal compiler errors has scared me off doing more
-experiments in this direction. 
+experiments in this direction.