Re: GThreads and GThreadPool help for a dummy

2010-07-27 Thread Øystein Schønning-Johansen
 However back to my code:
  pool = g_thread_pool_new( my_calc, (gpointer) common, 8, FALSE, err );

  for (i = 0; i  n_tot; i++ )
          g_thread_pool_push( pool, store[i], err );

  g_thread_pool_free( pool, FALSE, TRUE );

 Why doesn't the g_thread_pool_free function stop and wait for all
 threads. I really specify this to wait (the third parameter is wait_
 and set TRUE). In my understanding this must then be a bug in glib.
 Should I fill in a bug report?

Oh my! What a blunder. I got the parameters in my_calc() swapped. It
then just returned without calculating anything. I fixed this, and it
now seems to work. The documentation on this is a bit unclear, and may
need some polishing, but there is no bug.

Scaling is a bit strange though! I'm working on a box with 2 intel
xeon quad core processors and would therefore expect to get a close
linear speed up from 1 -8 threads. Here's the results:

Threads  execution time [sec]
   1 14.38
   2 11.73
   3  7.59
   4  6.42
   5  5.89
   6 14.91
   7 33.94
   8 35.00

Is it the mutex lock and unlock that gives all this overhead when
there's many threads. I'll try splitting the array and see if that
gives better scalability.

-Øystein
___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


Re: GThreads and GThreadPool help for a dummy

2010-07-24 Thread richard boaz
I realized after i sent that email that it was a little rushed, and too
unqualified.  So in the interest of completeness (and to avoid possible
misuse), the following addendum to the last post:

The code provided assumed a background program, i.e., not a GUI or a program
running with a main loop, since the call to g_async_queue_pop() in the main
routine is blocking.  As written, this code should absolutely NOT be used in
a program where a main loop is expected to remain responsive.

However, with a little modification, the algorithm can easily be made
non-blocking and usable in a GUI:

   - Remove all references to the async queue commQ.
   - In finishThreads(), instead of returning the answer via commQ, use
   g_idle_add() to pass the result back to the main loop.  (write a new routine
   that takes the result and presents it back to the user, whatever that
   means.)

In general, too, the following warnings must accompany this code:

   - Case is not handled when code is called a second time before the first
   call has completed, i.e., it is not re-entrant as written.  Calling a 2nd
   time before 1st execution has completed will cause indecipherable havoc;
   either handle this case, or disallow it.
   - There are memory leaks.

And, while I'm here, a couple of relevant guidelines to writing
multi-threaded code:

   - Use as few locks as possible in the threads.  In the example provided,
   the stored result could have been referred to directly (added to) in each
   executing thread, using a lock around the summation.  Instead, the solution
   provides for each thread to access to a single (and unique) element of an
   array of float values holding the summing result, while the finishThreads
   sums each thread's result once all threads have completed, returning this
   single value to the main routine/loop.  In this manner, no lock is necessary
   here in computing the result, guaranteeing a faster completion.


   - Whatever code must exist inside a lock, make it absolutely as small as
   possible.  The more code that exists inside a lock, the greater the chance
   that other threads will get stuck waiting on the lock to be free.  The more
   threads that must sit and wait for a lock to be free, the less the point of
   having made the code multi-threaded in the first place (since, by
   definition, locked code means only one can execute at a time, not much
   different than writing in-line...).  In the provided solution, a single lock
   is required around g_queue_pop_head() (which executes very fast), making the
   likelihood of a thread having to wait on the lock to be free very low
   indeed.

cheers,

richard

2010/7/23 richard boaz ivor.b...@gmail.com

 this is how i do it (not using pools), i have modified my code for your
 purposes, though it's probably wrong somewhere in the implementation details
 (i didn't even compile it), but i leave that to you.

 this is very easily generalizable to do any kind of background work on a
 list of items.

 richard

 === BEGIN code snippet ===

  { // in the main routine
  gfloat *retPtr, tot_err;
  GQueue *threadsQ = g_queue_new(); // Q holding list of data items to
 process
  data_t data[N_DATAPOINTS];  // data items to process
  fill_the_data_array(data);
 for(i = 0;
  i  N_DATAPOINTS;
  i++)
  {
 g_queue_push_head(threadsQ, data[i]); // put data item onto Q for
 processing
  }
 g_thread_init(NULL);
 commQ = g_async_queue_new();  // create comms Q
  threadsProc(threadsQ, maxThreads);  // initiate threads
  retPtr = (g_float*) g_async_queue_pop(commQ); // sit and wait for all
 threads in threadsProc() to complete...
  tot_err = *retPtr;   // the answer
  }

 // all the rest in a different source file...

 static GQueue *threadsQ; // Q holding the data to process
 static int numThreads; // number of threads (and a counter too)
 static g_float *tot_err; // array of g_float, one element per thread
 static g_float total_errors; // total errors, added up, returned to main
 static GStaticMutex QMutex = G_STATIC_MUTEX_INIT; // mutex to read Q

 typedef struct _THREADARGS
 { // structure of arguments to thread proc
 GThreadFunc returnFunc;
  int threadNum;
 } THREADARGS;

 static void finishThreads()
 { // called once a thread has finished its work
 numThreads--;
 if (!numThreads)
  { // return to main routine
 int i;
  g_float retValue = 0;
 for (i = 0;inumThreads; i++)
  retValue += tot_err[i];
  total_errors = retValue;
 g_async_queue_push(commQ, total_errors);
  }
 }

 static data_t *get_data()
 { // get the next data item off the Q to process, called by _addData()
  data_t *data;
 g_static_mutex_lock(QMutex);
  data = g_queue_pop_head(threadsQ);
 g_static_mutex_unlock(QMutex);
  return (data);
 }

 static void _addData(THREADARGS *threadArgs)
 { // processing thread
  int threadNum = threadArgs-threadNum;
 GThreadFunc retFunc = threadArgs-returnFunc;
  data_t *data = get_data(); // get a data item to process
  while (data)
 {
  tot_err[threadNum] += 

Re: GThreads and GThreadPool help for a dummy

2010-07-24 Thread Robert Pearce
On Sat, 24 Jul 2010 10:16:09 +0200 richard wrote:
 
 And, while I'm here, a couple of relevant guidelines to writing
 multi-threaded code:

There's another that you've missed:

 - Use as few threads as possible.

Basically, threads add overhead. You get benefit only up to the point where 
there are as many threads as you have independent CPU cores. Beyond that your 
performance drops off as compared to a well structured idle loop, and the 
readability isn't noticeably better either.

If you want to sum the results of foo(x[i]) over an array of N elements, where 
N is large and foo() is simple, I wouldn't use threads. If this is a background 
task and you want GTK to remain responsive, I would launch exactly ONE thread 
to do the summing. If foo() is complex, I would split N into M ranges, where M 
is roughly the number of CPU cores you expect to have, and launch M threads. 
Only if N is approximately equal to M would I take the approach being discussed 
here.

Rob
___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


Re: GThreads and GThreadPool help for a dummy

2010-07-24 Thread richard boaz
indeed.  the way i solve this is by programmatically determining the number
of CPU's available on the machine at program startup, knowing (through
testing) how much of the CPU a particular task requires, taken in
conjunction with how much the program should be allowed to take over the
machine.  and then starting the maximum number of threads determined by all
of these (there's probably even more one could consider here).  all
programmable, thus always making best use of all available CPU's vs.
requirements vs. known trade-offs.

further, by knowing how many data elements are required to be processed, if
this number is actually less than the computed allowed maximum number of
threads, then this becomes the maximum number of threads to be started.  (no
sense starting more worker threads than there are elements to be processed.)

another thing i left out is adding a progress meter.  when the app is a GUI,
indications back to the user how far along the task is, (if the task
requires say more than 2 seconds), is good practice, and will make your
users happy.  this is easily achieved by adding a call to g_idle_add() in
the worker thread to invoke a routine responsible for updating a progress
meter.  since the main loop is not blocked, screen refresh is immediate
(assuming you've left space on the CPU's to allow the computer to do more
work than just the worker threads).

richard

On Sat, Jul 24, 2010 at 4:03 AM, Robert Pearce r...@bdt-home.demon.co.ukwrote:

 On Sat, 24 Jul 2010 10:16:09 +0200 richard wrote:
 
  And, while I'm here, a couple of relevant guidelines to writing
  multi-threaded code:

 There's another that you've missed:

  - Use as few threads as possible.

 Basically, threads add overhead. You get benefit only up to the point where
 there are as many threads as you have independent CPU cores. Beyond that
 your performance drops off as compared to a well structured idle loop, and
 the readability isn't noticeably better either.

 If you want to sum the results of foo(x[i]) over an array of N elements,
 where N is large and foo() is simple, I wouldn't use threads. If this is a
 background task and you want GTK to remain responsive, I would launch
 exactly ONE thread to do the summing. If foo() is complex, I would split N
 into M ranges, where M is roughly the number of CPU cores you expect to
 have, and launch M threads. Only if N is approximately equal to M would I
 take the approach being discussed here.

 Rob
 ___
 gtk-list mailing list
 gtk-list@gnome.org
 http://mail.gnome.org/mailman/listinfo/gtk-list

___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


Re: GThreads and GThreadPool help for a dummy

2010-07-24 Thread Øystein Schønning-Johansen
2010/7/23 richard boaz ivor.b...@gmail.com:
 this is how i do it (not using pools), i have modified my code for your
 purposes, though it's probably wrong somewhere in the implementation details
 (i didn't even compile it), but i leave that to you.
 this is very easily generalizable to do any kind of background work on a
 list of items.

Thanks for the code. I'll try it out. I think the pool code in glib
works similar, except that it uses an asynchronous queue.

However back to my code:
  pool = g_thread_pool_new( my_calc, (gpointer) common, 8, FALSE, err );

  for (i = 0; i  n_tot; i++ )
  g_thread_pool_push( pool, store[i], err );

  g_thread_pool_free( pool, FALSE, TRUE );

Why doesn't the g_thread_pool_free function stop and wait for all
threads. I really specify this to wait (the third parameter is wait_
and set TRUE). In my understanding this must then be a bug in glib.
Should I fill in a bug report?

-Øystein
___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


Re: GThreads and GThreadPool help for a dummy

2010-07-23 Thread Øystein Schønning-Johansen
I tried, and the new problem is now:

  pool = g_thread_pool_new( my_calc, (gpointer) common, 8, FALSE, err );

  for (i = 0; i  n_tot; i++ )
  g_thread_pool_push( pool, store[i], err );

  g_thread_pool_free( pool, FALSE, TRUE );

When I run this code, it continues without waiting for the threads to
complete. I expected that the g_thread_pool_free function would make it wait
for all threads to complete before the program code continues, however
it does not! How can I make it wait?

(Yes, I added a static mutex in my_func. I'm convinced that I need it. )

-Øystein
___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


Re: GThreads and GThreadPool help for a dummy

2010-07-23 Thread richard boaz
this is how i do it (not using pools), i have modified my code for your
purposes, though it's probably wrong somewhere in the implementation details
(i didn't even compile it), but i leave that to you.

this is very easily generalizable to do any kind of background work on a
list of items.

richard

=== BEGIN code snippet ===

{ // in the main routine
 gfloat *retPtr, tot_err;
 GQueue *threadsQ = g_queue_new(); // Q holding list of data items to
process
 data_t data[N_DATAPOINTS];  // data items to process
 fill_the_data_array(data);
for(i = 0;
 i  N_DATAPOINTS;
 i++)
 {
g_queue_push_head(threadsQ, data[i]); // put data item onto Q for
processing
 }
g_thread_init(NULL);
commQ = g_async_queue_new();  // create comms Q
 threadsProc(threadsQ, maxThreads);  // initiate threads
 retPtr = (g_float*) g_async_queue_pop(commQ); // sit and wait for all
threads in threadsProc() to complete...
 tot_err = *retPtr;   // the answer
 }

// all the rest in a different source file...

static GQueue *threadsQ; // Q holding the data to process
static int numThreads; // number of threads (and a counter too)
static g_float *tot_err; // array of g_float, one element per thread
static g_float total_errors; // total errors, added up, returned to main
static GStaticMutex QMutex = G_STATIC_MUTEX_INIT; // mutex to read Q

typedef struct _THREADARGS
{ // structure of arguments to thread proc
GThreadFunc returnFunc;
 int threadNum;
} THREADARGS;

static void finishThreads()
{ // called once a thread has finished its work
numThreads--;
if (!numThreads)
 { // return to main routine
int i;
 g_float retValue = 0;
for (i = 0;inumThreads; i++)
 retValue += tot_err[i];
 total_errors = retValue;
g_async_queue_push(commQ, total_errors);
 }
}

static data_t *get_data()
{ // get the next data item off the Q to process, called by _addData()
 data_t *data;
g_static_mutex_lock(QMutex);
 data = g_queue_pop_head(threadsQ);
g_static_mutex_unlock(QMutex);
 return (data);
}

static void _addData(THREADARGS *threadArgs)
{ // processing thread
 int threadNum = threadArgs-threadNum;
GThreadFunc retFunc = threadArgs-returnFunc;
 data_t *data = get_data(); // get a data item to process
 while (data)
{
 tot_err[threadNum] += calc_error(data);
data = get_data(); // until no more to do
 }
(*(retFunc))(NULL);
}

void threadsProc(GQueue *q, int nThreads)
{
int i;
numThreads = nThreads;
 tot_err = calloc(numThreads, sizeof(g_float));
 threadsQ = q;
 for(i=0;inumThreads;i++)
{
THREADARGS *threadArgs;
 threadArgs = calloc(1, sizeof(THREADARGS));
threadArgs-returnFunc = (GThreadFunc) finishThreads;
 threadArgs-threadNum = i;
g_thread_create((GThreadFunc) _addData, (void *) threadArgs, FALSE, NULL);
 }
}

=== END code snippet ===


2010/7/23 Øystein Schønning-Johansen oyst...@gnubg.org

 I tried, and the new problem is now:

  pool = g_thread_pool_new( my_calc, (gpointer) common, 8, FALSE, err );

  for (i = 0; i  n_tot; i++ )
  g_thread_pool_push( pool, store[i], err );

  g_thread_pool_free( pool, FALSE, TRUE );

 When I run this code, it continues without waiting for the threads to
 complete. I expected that the g_thread_pool_free function would make it
 wait
 for all threads to complete before the program code continues, however
 it does not! How can I make it wait?

 (Yes, I added a static mutex in my_func. I'm convinced that I need it. )

 -Øystein
 ___
 gtk-list mailing list
 gtk-list@gnome.org
 http://mail.gnome.org/mailman/listinfo/gtk-list

___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


GThreads and GThreadPool help for a dummy

2010-07-22 Thread Øystein Schønning-Johansen
Hi,

I'm not really experienced when it comes to threading, and GThreads is
the thing I want to use, since (I assume) it's quite portable.

I have a function that returns a gfloat and I call the function
sequentially and accumulate the total.
Like this:

/* My sequential pseudo code */
data_t data[N_DATAPOINTS];
gfloat tot_err = 0.0f;
guint i;

fill_the_data_array( data );

for ( i = 0; i  N_DATAPOINTS; i++ )
tot_err += calc_error( data[i] );

/* End of my sequential pseudo code */

I have the impression that this should be rather simple to thread
for-loop. Since addition is commutative, I may not even need a mutex
for the tot_err variable.

Can anyone point me in the right direction?

-Øystein
___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list


Re: GThreads and GThreadPool help for a dummy

2010-07-22 Thread Chris Vine
On Thu, 22 Jul 2010 13:27:29 +0200
Øystein Schønning-Johansen oyst...@gnubg.org wrote:
 I'm not really experienced when it comes to threading, and GThreads is
 the thing I want to use, since (I assume) it's quite portable.
 
 I have a function that returns a gfloat and I call the function
 sequentially and accumulate the total.
 Like this:
 
 /* My sequential pseudo code */
 data_t data[N_DATAPOINTS];
 gfloat tot_err = 0.0f;
 guint i;
 
 fill_the_data_array( data );
 
 for ( i = 0; i  N_DATAPOINTS; i++ )
 tot_err += calc_error( data[i] );
 
 /* End of my sequential pseudo code */
 
 I have the impression that this should be rather simple to thread
 for-loop. Since addition is commutative, I may not even need a mutex
 for the tot_err variable.

Commutativity (indifference to the order of evaluation) is irrelevant
to whether you would need a mutex for access to a shared variable (you
would if access is shared to a variable which is written to as well as
read) but there are ways of arriving at a sum which would not require
shared access: in particular g_thread_join() is a synchronisation point
which will cause memory/caches between processors to become visible and
also return a value for summing.

The glib documentation on threading is not all that good and does not go
into questions of memory synchronisation.  If you are beginning with
threads you might do better to read up on pthreads, which GThread
mimics and of which it implements a subset.  On unix-like systems
GThreads is mostly just a wrapper for pthreads.

Chris


___
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list