Windows Desktop Apps That Are CPU Bound…
So What’s The Problem?
This article is a bit esoteric but if you have an interest in Windows programming you may find it interesting. That said…
Sometimes we’ll develop CPU bound programs (“apps”) that perform Bizillions of calculations. For example, a program that finds the prime factors of very large numbers (e.g. 100 digits or more) can run for a very long time. In these types of programs I’ll typically code the “Calculation function” to periodically check whether the user (me) wants to have intermediate results or other useful information “displayed.” Also, the code would additionally wait for the user to tell the Calculation function to pause, continue, or terminate after viewing the information that was displayed. All that said, here’s the problem…
The typical Windows Desktop app (WinForms or WPF) is a message-driven program. When you click on the button that says “Factor The Number” an event message is generated and is sent to the program to be read and processed. Eventually the event message works its way to its “FactorButton_Click” routine for processing. Visual Studio generates the “button click” routines in our programs but it’s up to us to add the code inside those generated functions/methods. That code that we supply will perform the calculations and may periodically display useful information in a “log” for the user to read. Examples of the kind of information displayed might be the number that’s currently being factored, how many factors have been found so far, how long it’s been working on the current number, etc.
The “log” where the app’s useful information is displayed as text would typically be a Multi-line TextBox. That is, a TextBox serves as the “log” and is the modern GUI version of “printf” (that we would have used “back in the day”).
The act of writing to the “log” (the TextBox) will generate “paint” messages which eventually make their way to the code for the TextBox that is functioning as the “log.” When the “paint” messages are processed by the TextBox code it will actually draw the text on the screen. But the problem is that the “paint” messages for the log often won’t get to the TextBox in a timely fashion as long as the calculation routine has control! Also, the user won’t be able to scroll the TextBox because mouse clicks and keypresses won’t get processed as long as the calculation routine continues to run. The messages (text) now exist in the log’s TextBox but we can’t effectively use it, or anything else on the app’s window until the “Calculation” routine finishes. The app is effectively “frozen” until the Calculation routine ends.
I have lately been writing numerous CPU Bound “calculation” programs and the above issue had been an ongoing pain in the butt. After thinking about the problem on and off for several months I finally decided to solve the problem once and for all.
Note: It’s occasionally been the case that when the problem is explained to another programmer they propose “just use the Yield() or Sleep() function.” However, we need to remember, Yield() and Sleep() allow other threads to get control… but the problem is that the code to process events for your own controls, like Buttons and TextBoxes, is NOT in other threads… It’s in your own thread! And, the next thing often proposed is to use multiple threads as part of a solution; No thanks… that’s way too complicated and error-prone. Anywho… persistence and patience eventually led me to the following:
Here’s the basic idea as illustrated above…
The “Log” is contained in the Memory Mapped File. A utility Class named MMFlogger in a classlib is how an app would access the log. From the perspective of an app (or programmer), the file consists of just 2 very simple things:
.1. A long integer “command” that can be read or written. How the “command” (a long integer) is interpreted and used is completely up to app. For example, in the programs I write:
0 (zero) means “continue processing”
1 means “pause” and wait for a new command.
2 means “terminate” the calculations (but not the program).
.2. The second “field” in the file can be thought of as a giant string much like the Text field of a WinForms Textbox (e.g. myTextbox.Text += “\r\nAnother message for the Textbox”;).
A user app would typically use just the following 2 MMFlogger functions/methods like so:
long lCommand = myMMFlogger.ReadCommand();
string something = “\r\nAppend something to the giant string in the file”;
From the User’s (programmer’s) perspective it’s about as simple as it gets. It’s essentially like writing to a WinForms TextBox!
How is the User actually going to view the log?
In the flowchart you will see “Process B” which is a utility app/program that monitors the log in the MemoryMappedFile. It can be set to automatically (or manually) read the data (strings) in the log and write them to a TextBox for the User to view on the screen. Of course, the User can scroll forwards and backwards, select – copy – and paste to a text file or spreadsheet, etc. And there are buttons the User can click to have “commands” sent to the “Calculation app” (Process A) to tell it to pause, continue, terminate, etc. The User can also have the data (string) cleared from the log/file (perhaps in preparation for a new round of computations by the “Calculation app”). In a nutshell:
– We eliminate the log’s TextBox from the User app and replace it with a Memory Mapped File that the app uses just like it would use a TextBox but with the added benefit that it can now easily receive “commands” from the User (person).
– The User (person) now views and interacts with the log via a TextBox in the “Utility App” (Process B). In addition, the User (person) can now send “commands” to the “Calculation app” instructing it, for example, to pause, continue, terminate the calculations, etc.
So that’s it except for a few notes:
As shown in the flowchart above, Process A is the “Calculation app.” It uses the MMFlogger Class to write text to the “Log.” The “Log” is implemented via a Memory Mapped File.
Process B is a general purpose utility app for viewing the “Log” that the “Calculation app” is writing to. It can be used with any Windows desktop application. When we code our “Calculation app” we can, if we want, have it automatically start the utility “Log” viewing app (Process B). Or it can be started manually by the User.
Above I stated that the second “field” in the Memory Mapped File can be thought of as a giant string. The truth is that you can NOT write or read strings to/from a Memory Mapped File. The strings must first be converted from/to byte arrays as part of the process and there are some hidden “gotchas” along the way. The utility Class MMFLogger that was developed as part of this project hides all that from you and me.
As it turns out, reading/writing a Memory Mapped File is VERY FAST! It’s much faster than writing to TextBoxes so you should not fret over using the MMFlogger Class in your CPU Bound app.
If you want more details or the code/projects just ask.