>
> What is the running time of 4 threads compared to one thread? I am interested
> in knowing if all threads actually running concurrently on 4 cores, because
> running it on a dualcore machine, the running time does not tell me that is
> the case.
Well, I think it does - I suspect that you may be getting confused by the
measurement you are making.
Remember that the clock() function tells you how hard the core CPU is working,
in effect, as it is a measure of resource utilisation, rather than a measure of
actual elapsed time - so in most implementations what this means is that the
harder you work the core, the faster the value returned by the clock() function
appears to tick.
This is particularly true with modern CPU's where each core will scale its
internal clock very aggressively to reduce power utilisation and so forth.
So, rather the using clock() to measure thread utilisation, I suggest you try
making a "wall clock" measurement of how long each thread takes to do its job,
and see how that goes.
Probably use gettimeofday() or something instead.
OK, here's a hack that does that - on this quad-core Mac, the time for 10000000
iterations is around 0.26 seconds, and that is more or less flat if I have one
thread running or 4 - once I go beyond 4 threads, the time per thread starts to
increase slightly as threads 5, 6, and 7 compete for CPU with the other 4
threads.
Note that the increase is not all that significant as this machine also has
hyperthreading, so the 4 cores are "almost" 8 cores in practice... (the
iteration time rises to around 0.31 with all 7 threads maxed out.)
--------
//////////////////////////////////////////////////////
// Threading demo
// fltk-config --compile simple-thread-demo.cxx
//
#include <stdlib.h>
//#include <unistd.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Value_Output.H>
#include <FL/fl_message.H>
#ifdef WIN32
# include <windows.h>
# include <process.h>
# define THREAD HANDLE
# define wait_here(x) Sleep(x)
# define SCHED_YIELD Sleep(0)
#else /* for *nix and OSX */
# include <pthread.h>
# include <sched.h> // maybe necessary for sched_yield ?
# define THREAD pthread_t
# define PTHREAD_WRAPPER 1
# define wait_here(x) usleep((x)*1000)
# define SCHED_YIELD sched_yield()
#endif
#define MAX_THRDS 7
#define GUI_REFRESH_THD 9876543
#define ITER_COUNT 10000000
static volatile double startTime[MAX_THRDS];
static volatile double endTime[MAX_THRDS];
static Fl_Double_Window *main_win=(Fl_Double_Window *)0;
static Fl_Button *work[MAX_THRDS];
static Fl_Value_Output *out[MAX_THRDS];
static Fl_Value_Output *time_out[MAX_THRDS];
static Fl_Button *yeild_bt=(Fl_Button *)0;
static Fl_Button *quit_bt=(Fl_Button *)0;
/* Should the worker threads yield periodically or not? */
static int use_yield = 0;
// Use to signal child threads to terminate... It's a cheap IPC hack...
static int keep_running = -1;
static int thread_active[MAX_THRDS]; // one per worker...
/* Wrapper to start the worker thread */
THREAD make_thread (void *(*task)(void *), void *arg)
{
int res;
#ifdef WIN32
res = _beginthread(task, 0, arg);
return (THREAD)res;
#else // linux, OSX and other unix systems
pthread_t thread;
res = pthread_create(&thread, NULL, task, arg);
if (res == 0)
return thread;
else
return (THREAD)0;
#endif
}
// Send the updated value to the GUI - this may be sloooowwwww....
static void display_value(int id, double val) {
Fl::lock(); // avoid conflicting calls
out[id]->value(val); // update the GUI value
Fl::unlock(); // allow other threads to access FLTK again
Fl::awake(); // tell the GUI thread that something maybe changed
}
static void display_time(int id, double val) {
Fl::lock(); // avoid conflicting calls
time_out[id]->value(val); // update the GUI value
Fl::unlock(); // allow other threads to access FLTK again
Fl::awake(); // tell the GUI thread that something maybe changed
}
/* This is the worker thread - it chews up ALL the CPU that it is given
*/
void *test_thread(void *arg)
{
long id = (long)arg;
double dr = 0.0;
int iter = GUI_REFRESH_THD;
int ii = ITER_COUNT;
struct timeval tp;
gettimeofday(&tp, NULL);
startTime[id] = (double)tp.tv_sec + (double)tp.tv_usec/1e6;
while ((keep_running) && (thread_active[id]))
{
// do something slow here
for (int j = 0; j < 6789; j++)
{
double d = (double)(j+1);
dr = log(d);
if(iter < 0) {
// update periodically - less slow!
display_value(id, dr);
iter = GUI_REFRESH_THD;
}
iter--;
ii--;
if (ii < 1) {
gettimeofday(&tp, NULL);
endTime[id] = (double)tp.tv_sec + (double)tp.tv_usec/1e6;
double dt = (double)(endTime[id] - startTime[id]);
display_time(id, dt);
ii = ITER_COUNT;
gettimeofday(&tp, NULL);
startTime[id] = (double)tp.tv_sec + (double)tp.tv_usec/1e6;
}
}
/* On some systems, particularly single-cpu ones, throwing in the
* occasional voluntary worker yield will make the GUI thread seem
* more responsive. */
// if(use_yield) SCHED_YIELD;
}
return NULL;
} // END of test_thread(void *arg)
static void do_start(int state, int id) {
if(state) { // start the worker thread
thread_active[id] = 1;
make_thread(test_thread, (void *)id);
}
else { // terminate the worker thread
thread_active[id] = 0; // the worker will see this and exit
}
}
static void cb_work(Fl_Button* o, void*v) {
int state = o->value();
unsigned long vv = (unsigned long)v & 0xFF;
do_start(state, (int)vv);
}
static void cb_yeild_bt(Fl_Button* o, void*) {
use_yield = (int)o->value();
}
static void cb_quit_bt(Fl_Button*, void*) {
main_win->hide(); // close the GUI
keep_running = 0; // ask the workers to expire
}
int main(int argc, char **argv) {
// ensure thread support is enabled...
Fl::lock();
// now build the GUI
main_win = new Fl_Double_Window(800, 209, "Thread control");
main_win->begin();
for (int ib = 0; ib < MAX_THRDS; ib++) {
work[ib] = new Fl_Button(25 + (ib * 110), 18, 70, 27, "Worker");
work[ib]->tooltip("Start worker thread");
work[ib]->type(FL_TOGGLE_BUTTON);
work[ib]->callback((Fl_Callback*)cb_work, (void*)ib);
out[ib] = new Fl_Value_Output(25 + (ib * 110), 58, 70, 24);
time_out[ib] = new Fl_Value_Output(25 + (ib * 110), 90, 70, 24);
}
yeild_bt = new Fl_Button(25, 163, 70, 27, "With Yeild");
yeild_bt->tooltip("Cause worker threads to yeild periodically");
yeild_bt->type(FL_TOGGLE_BUTTON);
yeild_bt->callback((Fl_Callback*)cb_yeild_bt);
quit_bt = new Fl_Button(240, 163, 70, 27, "Quit");
quit_bt->tooltip("Doh!");
quit_bt->callback((Fl_Callback*)cb_quit_bt);
main_win->end();
// show the GUI
main_win->show(argc, argv);
// start the event loop
return Fl::run();
}
/* end of file */
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk