GThreads and GThreadPool help for a dummy
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
On Thu, 22 Jul 2010 13:27:29 +0200 Øystein Schønning-Johansen 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
Re: GThreads and GThreadPool help for a dummy
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
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;ithreadNum; 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;ireturnFunc = (GThreadFunc) finishThreads; threadArgs->threadNum = i; g_thread_create((GThreadFunc) _addData, (void *) threadArgs, FALSE, NULL); } } === END code snippet === 2010/7/23 Ø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 > ___ gtk-list mailing list gtk-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-list
Re: GThreads and GThreadPool help for a dummy
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 > 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;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 > w
Re: GThreads and GThreadPool help for a dummy
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
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 wrote: > 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/7/23 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. 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
> 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