The Qt Framework and (soft) real-time projects
2012-07 Update
Please see the new Tip #19 for an update on how Qt is working out.
2007-04-01 Update
The Qt_RealtimeIO_App has been updated to remove some unnecessary code related to event processing. Minor changes to the text were made to correct erroneous statements about .NET image processing (you can get the raw bits if you can find the documentation, see this about documentation quality). My new Tip #15, Implicit Sharing and Multi-Threading Problems provides some advice on using shared objects in both Qt and other frameworks.
2007-02-03
I started out with Borland way back when they first came out with Turbo Pascal 1.0 for the CP/M operating system running on 8080 chips. It was much better than the Pascal compiler from Digital Research that would crash and burn on any procedure over about 200 lines of code. Borland has almost always produced high quality tools for application development at reasonable prices. When they came out with their Turbo C++ compiler I used it for a few projects where Pascal wasn't efficient enough when running on the old 286 and 386 processors of that period. When I got involved with machine vision, their Borland C++ Builder provided just the right mix of execution speed and fast RAD development needed to create the Ventek GS2000 product. Yes, it was a pain dealing with third party libraries that would only link to the Microsoft compiler, but I did the necessary work and made it all fit together.
As of late, Borland seems more interested in providing large corporate customers with tools needed to control their programmers and projects rather than producing the tools for writing the programs. They tried to sell their compiler tools division in 2006 but the right buyer could not be found. It has now been spun out into CodeGear, a separate division. From what I can gather from their road map, they are strongly targeting the .NET platform and only making minor improvements in the Win32 tools area. Also, Kylix is dead although everyone is still begging for it (see the bottom of their road map).
Looks great! Microsoft has .NET 3.0 now and CodeGear will soon support .NET 2.0. C++ updates will come out later. .... Everyone else that has played this game with Microsoft has lost. I wonder how long CodeGear will last?
Considering that the improvements to the VCL (Visual Component Library) have been mostly minor because Borland has concentrated on .NET interop with Delphi and the VCL, and that no .NET interop is planned for C++, I got the feeling that it was about time I started looking for another tool vendor. Microsoft has set the direction for C++ with the C++/CLI interop interfaces (more commonly "C++ divided by CLI" if you're part of the hard core C++ crowd). Anyone who tries to follow them will always be behind. This is Microsoft's second attempt at C++ interop, the first one was a mess. Even if Borland followed them you could never be sure that Microsoft wouldn't change direction again. Also, the situation with the change from Visual Basic 6 to VB .NET makes me very concerned about getting into bed with Gates and company. If you like to rewrite your software every few years, please be my guest. Borland tends to upgrade lower versions of code quite well because they were grounded to the VCL rather than drifting like Microsoft through multiple DB APIs just to make it hard for the competition.
This is what started me off on a search for a good native code framework that could build GUI interfaces rapidly and provide the speed needed for image processing. I previously looked at the .NET platform and rejected it due to managed code performance concerns. I was also interested in cross-platform support in case Microsoft goes off on some tangent and tries to make us all program in managed .NET code.
The thing that made me pick Borland C++ Builder and the VCL framework in the first place was performance. Image processing involves lots of lists of objects and the ability to create, move, sort and access them quickly allows you to get more work done. Our grader must provide image solutions in under 0.5 seconds and do it at a rate of about once per second on a 24/7 basis. For each image, I create from 200 to 1000 defect objects off of the heap and place them into lists for processing. Any container class I use needs to be fast and memory efficient. When I first started the GS2000 project I figured I would use the STL (Standard Template Library) containers, so I wrote a test program to see how they compared to the VCL TList container. Well, it turned out the TList container was about 10 times faster. The STL has the advantage of better type checking, but I went for the speed and used TList. I decided to make container tests the basis for picking a new framework.
I had tried the Qt framework out a few years ago, but gave up on it due to the poor integration of version 3 with the Win32 platform and Microsoft tools. Qt version 4.2.2 has grown up quite a bit and now offers fantastic graphics view classes and access to OpenGL and most SQL databases. I signed up on Trolltech's web site for a 30-day trial of Qt and compared it to Microsoft's Visual Studio 2005 and .NET 2.0. I also took a short look at Boost but it didn't address the GUI areas that I needed. Juce was another framework I looked at, and it looked very good, but it wasn't as far along as Qt at RAD GUI design.
What follows is a report on the timing figures I obtained. I can only tell you the Qt and Borland values, because the Microsoft's MSDN license doesn't allow me to tell you their timing. You can download the programs and run them yourself to see those numbers.
An independent view of Qt is available here.
An independent view of JUCE is available here.
The Test Program Downloads
You can download the VS2005 source and .NET 2.0 versions of these programs here:
dNet_CPP_List_Test.zip 49 KB ZIP file, the .NET managed C++ version.
Win32_CPP_List_Test.zip 18 KB ZIP file, the native C++ version.
Qt_CPP_List_Test.zip 9 KB ZIP file, the native Qt C++ version.
Qt_RealtimeIO_App.zip 33 KB ZIP file, the native Qt C++ version.
Qt_Test_BIN.zip 3 MB ZIP file, Qt Win32 runtime DLLs and EXEs for above.
If you have .NET 2.0 installed and only want to run the EXE files, download the first two and the last file. The Qt versions can be compiled and run without changes on most modern Linux distributions, such as OpenSUSE 10.2, if you install the Qt 4 support. All the source code is covered by the GNU General Public License (GPL).
The following test results were run on a laptop with an Intel Core Duo 2.0 GHz processor with 2 GB RAM on Windows XP.
Screen Shots of C++ List Test
First, here is the program I wrote back around 1999 to test the STL and TList. Three runs of each library are tested. I never finished the MFC CObject version as I didn't have time to fiddle with getting MFC to integrate with the rest of the code.
These are the results for the Qt command line version using a QList. The combination of fast C++ and the MS VC2005 compiler runs much faster than the modern Borland TList!
VCL, Qt and .NET Evaluation
List containers were compared for efficiency on three platforms of interest:
- VCL is the Borland Developer Studio 2006 framework for Win32.
- Qt is the Trolltech framework for Win32, Linux and Mac OS X.
- .NET is the Microsoft framework for Win32 and CLR.
The ability to copy, sort and delete lists of 5000 objects were tested on an Intel Core Duo 2.0 GHz processor with the following results:
This TList holds an integer container class and used the standard TList sort method.
VCL TList(TIntContainer) |
Time (microseconds) |
---|---|
Class Add/Clear |
192 |
Class Add/Sort Forward/Clear |
542 |
Class Add/Sort Reverse/Clear |
552 |
The STL run shows add/clear for both a class and integers. Sorting a class is not directly implemented so I didn't bother to add the code for the class.
STL list<int> |
Time (microseconds) |
---|---|
Class Add/Clear |
1710 |
Int Add/Clear |
1710 |
Int Add/Sort Forward/Clear |
6450 |
Int Add/Sort Reverse/Clear |
4350 |
The Qt results with MS VC2005:
Qt QList<int> |
Time (microseconds) |
---|---|
Int Add/Clear |
76 |
Int Add/Sort Forward/Clear |
224 |
Int Add/Sort Reverse/Clear |
217 |
All I will say about the missing values is the Microsoft .NET add/clear times were very good, but sorting was not.
Comparing Classes, VCL to Qt
The following table summarizes the class mappings between the VCL and Qt frameworks:
VCL Class |
Corresponding Qt Class |
---|---|
TObject |
QObject |
TThread |
QThread |
TCriticalSection |
QMutex |
TDateTime |
QDateTime |
TTimer |
QTimer |
TList |
QList |
AnsiString |
QString |
TPaintBox, TBitMap |
QGraphicsView, QImage, QPixMap |
TMemoryStream, TFileStream |
QTextStream, QDataStream |
TIniFile |
QSettings |
TStringList |
QStringList |
In almost all cases, the Qt classes provide greater functionality than the VCL. The Qt classes also work directly as stack variables where VCL objects must all come from the heap. Memory leaks from using temporary stack objects are less of a worry. If you have a big investment in VCL code, once you get rid of all the __fastcall statements, the conversion is fairly straightforward. Where the VCL automatically clears class memory on creation, you will be adding a lot of variable initialization to your constructors with Qt classes.
Speed and Framework Summary
The Qt framework provides the fastest object list operations and sorting. It also uses the Microsoft Visual C++ compiler which allows easy access to many libraries designed to only work on the Win32 platform. There is good support for many of the class types used in the VCL and the MySQL database. Existing Borland VCL programs should translate easily into their equivalent Qt framework classes. Programs not using Win32 APIs can be easily cross-compiled to run on Linux or the Mac OS X with much the same look and feel on each platform.
The only drawback with Qt compared to the VCL is that the visual design of the program in Qt Designer is limited. It allows you to design forms and components, but doesn't allow easy creation of the program event methods to respond to the controls. These events must be manually assigned to slots in program code to make the interface work. While this is not overly complex, it requires more manually written code to make the program function. One advantage Qt does have: the amount of code needed for a complex visual interface can be quite simple with their advanced framework interfaces.
The VS2005 integration works to a point, but VS likes to crash the built-in Qt Designer. The special Designer built into VS will link a control's default event into a method when you double click the control. Making further edits to the controls may cause VS to disappear from the screen. You can use the stand-alone Qt Designer to design the forms. It has a better interface than VC and is rock solid. The control signal-slot methods are also easy to enter as code because the default events are automatically linked by the Qt pre-processor through a naming convention with the control's name.
The Windows .NET framework limits program function to Windows only and also has performance issues. Qt can provide some access to Windows technology because it makes use of Microsoft Visual C++ for building applications on the Windows platform.
GUI Look and Feel Summary
While Windows .NET provides layout controls, they don't always provide the desired results:
- Objects change position in the frame unless the table layout is used.
- Objects are clipped or scroll bars must be used when the window is sized small.
- Layout frames for each layout mode are placed on a form and objects are then placed into them.
The Qt Designer provides better layout controls and also different window styles:
- Objects stay in the same positions and shrink or expand to fit the form.
- Scroll bars normally are not used on small forms and a minimum size is enforced.
- Objects are grouped and a layout mode is assigned to the group. This can be layered into groups of objects to any nesting level.
- Some of the special window styles, such as plastique, provide much of the look and feel of the new Windows Vista interface without the overhead.
Windows .NET Image Processing Issues
Both the Borland VCL and Qt provide image containers that allow access to the raw image buffer used to store the pixels. The QImage class provides easy image processing and goes way beyond Borland's TImage, allowing read/write to a large number of standard formats. This allows quick access to the pixels where low level processing must be done directly in programs. Windows .NET hides the raw image buffer and doesn't allow easy access to it. You must use setPixel and getPixel methods to operate directly on pixels and this will make these operations very slow. While raw access is available, one must use "unsafe" programming mode to work with the raw image. The actual functions used for raw access are hidden about 6 levels deep in the MSDN documentation.
The Qt Graphics View Class
Yes, you can purchase an image processing library, but why spend the money if you can get one with a framework that does most of what is needed. Qt includes a QGraphicsView class that can build three dimensional tree lists of two dimensional image objects and allow you to view and interact with them using the mouse. Their 40,000 Chips demo blew me away when I first ran it and it does all this with minimal lines of code.
A Look at Using Qt for Soft Real-Time Applications
We now take a look at the time waiting functions in Qt and compare them to Win32 timers and the Linux Real Time Clock (RTC) for their ability to keep threads running at a steady rate. The ability of the thread to run on accurate equal periods depends greatly on what type of CPU you run it on. Single processors have some problems under heavy loads, but hyper-threading is good and Core 2 Duo is even better. The Win32 tests were run on a Core Duo at 2 GHz. The Linux tests were run on a Core 2 Duo at 1.67 GHz.
This first test ran a QThread at real-time priority and used a QTimer event to signal the thread's event loop. This run shows some rather large delays when I moved the window around the screen with the mouse. The Win32 QTimer appears to use the main Windows event loop for timer events, even though they are being sent to a "real-time" thread.
This run shows some rather large delays when I minimized and maximized the window. Again, the main Windows event loop appears to block the QTimer events.
A Win32 periodic timer provides the best timing and is independent of mouse actions. This is the only way to perform accurate periodic real-time processing on Win32.
This screen shot shows the program running on OpenSUSE 10.2. Moving the window or min/max operations don't bother the timing much. Timing is not as solid as the Win32 periodic timer, but is still quite good. We notice that Qt appears to fine adjust the periods so the average rate equals the selected time. Periods are split between 0.008 and 0.012 seconds to average out to the selected 0.01 period. It should be noted that I didn't implement a high resolution timer to obtain the statistics, the standard QTime object was used which only has 1 millisecond resolution.
The Qt msleep function also is not affected by the mouse much, but the time period is not accurate for small values.
The Real Time Clock gives fairly solid results that are not affected by the mouse. Note that it can only run on 1, 2, 4 and 8 millisecond periods so the system will select a proper value rather than the 10 milliseconds I set. The laptop I was running this on didn't provide solid RTC interrupts, however. The RTC would stop after a few seconds of operation. Your experience with the RTC may also vary. Desktop computers usually run better using the RTC. The QTimer events are more stable and usually good enough.
Here, I turned on the UDP packets and used Wireshark to monitor the network port. Wireshark can be configured to show the delay between packets and sort the results. There were about 1300 total packets in this sample. Only a few were much lower than the selected period.
This is the top end of the packet sample periods. Only a few packets were over the selected period. The single 21 second delay was due to starting the program, there was a 21 second gap between the last system packet and the first RTC packet.