GeorgeCochran
April 16th, 2004, 01:48 AM
I do some consulting work in which I write custom stand-alone dialog-based apps in VC6 of the following style: The program opens with a modal dialog box in which the user enters some parameters. Then the program starts a lengthy series of mathematical calculations that takes hours and sometimes days to finish. During these calculations I put a modeless dialog box on the screen to show the user the progress in the calculation, an estimated time to completion, and two buttons that allow the user to stop the program (one to save the data to resume later, the other to just cancel the run). The progress dialog class uses a timer to update itself every 3 seconds with data from the calculation.
For example, the last such project that I finished required 8 days to run on a 3GHz P4 desktop. The point is that I’m very interested in getting the calculations running as fast as possible.
When I first started writing these programs, I naively wrote one containing the two dialog boxes and a procedure for the math, and then I let it rip. The result was a frozen computer since the progress dialog (and any other app running on the machine) didn’t get to process any messages since the math procedure was hogging all the processor time. So after studying the MS no-help files, I’ve tried three different modifications of this basic model to keep from freezing the computer.
1. A console app using mfc in which I don’t use the console. Apparently console apps generated by the AppWizard come with a built-in message loop that allows others time on the processor. The drawback is that there’s a black window on the screen that I don’t use.
2. Making the math calculation into a worker thread, and letting the main thread do the dialog boxes. By trial and error I’ve discovered that using THREAD_PRIORITY_ABOVE_NORMAL results in a frozen computer, and using a NORMAL priority results in a very sluggish feeling computer. So I’ve been setting the thread priority at BELOW_NORMAL. Also, there are a small number of variables in the app class that the two threads use to exchange data about their states (the worker thread gets access to them through a pointer that was passed to it); I’ve not worried about possible clashes with this and so far it hasn’t been a problem.
3. Instead of multi-threading, I’ve put a message loop in the math procedure that is called after each series of calculations (once in each iteration of a long for() loop), that basically implements something like idle-loop processing. I’m using the code below, which I copied out of a MS help file. The original version in the help file did not run, and I discovered by trial and error that commenting out the lines marked resulted in something that seems to work correctly.
void CMyApp::IdleLoop()
{
// BOOL bDoingBackgroundProcessing = TRUE; (commented out)
// while ( bDoingBackgroundProcessing ) (commented out)
{ MSG msg;
while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if ( !PumpMessage( ) )
{
// bDoingBackgroundProcessing = FALSE; (commented out)
// AfxPostQuitMessage(exitcode); (commented out)
break;
}
}
// let MFC do its idle processing
LONG lIdle = 0;
while ( AfxGetApp()->OnIdle(lIdle++ ) );
}
So here’s my question: Which of these three models would result in the fastest code? Presumably the multi-threading model would require the processor to continually change threads, and that overhead must take some time. The Idle-Loop model has the overhead of calling the idle-loop procedure and waiting for all other messages to be processed before getting back to the math calculation. The console model presumably has some overhead too, but I don’t understand the guts of that very well. In fact, I don’t really understand the guts of worker threads and message loops either, and I don’t want to invest time in learning about what I view as administrative trivia. I want to focus my time on programming the procedure that does the math and not worry about all these multi-tasking details. Suggestions?
For example, the last such project that I finished required 8 days to run on a 3GHz P4 desktop. The point is that I’m very interested in getting the calculations running as fast as possible.
When I first started writing these programs, I naively wrote one containing the two dialog boxes and a procedure for the math, and then I let it rip. The result was a frozen computer since the progress dialog (and any other app running on the machine) didn’t get to process any messages since the math procedure was hogging all the processor time. So after studying the MS no-help files, I’ve tried three different modifications of this basic model to keep from freezing the computer.
1. A console app using mfc in which I don’t use the console. Apparently console apps generated by the AppWizard come with a built-in message loop that allows others time on the processor. The drawback is that there’s a black window on the screen that I don’t use.
2. Making the math calculation into a worker thread, and letting the main thread do the dialog boxes. By trial and error I’ve discovered that using THREAD_PRIORITY_ABOVE_NORMAL results in a frozen computer, and using a NORMAL priority results in a very sluggish feeling computer. So I’ve been setting the thread priority at BELOW_NORMAL. Also, there are a small number of variables in the app class that the two threads use to exchange data about their states (the worker thread gets access to them through a pointer that was passed to it); I’ve not worried about possible clashes with this and so far it hasn’t been a problem.
3. Instead of multi-threading, I’ve put a message loop in the math procedure that is called after each series of calculations (once in each iteration of a long for() loop), that basically implements something like idle-loop processing. I’m using the code below, which I copied out of a MS help file. The original version in the help file did not run, and I discovered by trial and error that commenting out the lines marked resulted in something that seems to work correctly.
void CMyApp::IdleLoop()
{
// BOOL bDoingBackgroundProcessing = TRUE; (commented out)
// while ( bDoingBackgroundProcessing ) (commented out)
{ MSG msg;
while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if ( !PumpMessage( ) )
{
// bDoingBackgroundProcessing = FALSE; (commented out)
// AfxPostQuitMessage(exitcode); (commented out)
break;
}
}
// let MFC do its idle processing
LONG lIdle = 0;
while ( AfxGetApp()->OnIdle(lIdle++ ) );
}
So here’s my question: Which of these three models would result in the fastest code? Presumably the multi-threading model would require the processor to continually change threads, and that overhead must take some time. The Idle-Loop model has the overhead of calling the idle-loop procedure and waiting for all other messages to be processed before getting back to the math calculation. The console model presumably has some overhead too, but I don’t understand the guts of that very well. In fact, I don’t really understand the guts of worker threads and message loops either, and I don’t want to invest time in learning about what I view as administrative trivia. I want to focus my time on programming the procedure that does the math and not worry about all these multi-tasking details. Suggestions?