|
Performance Issue |
Posted by David Wilson on Jun-21-2011 05:17 |
|
Our application more or less continuously redraws ChartDirector charts to animate our charts in real time (resize, real time data update, etc.)
Chart update is not as fluid as we could hope for, so I put VTune on our application to find the performance issues.
I found that 60% of our application time was spent in ChartDirector function
CImageMapHandler_destroy()
which seems to be called from many methods.
The particular chart I was testing did not ImageMapHandler explicitly (it does not support tool tip hot spots), so I was wondering why so much time is spent in this function? I'm going to suppose that ImageMapHandler has some other purpose, if not, is there a way to avoid using ImageMapHandler?
This would result in significant speedup of our chart animations. |
Re: Performance Issue |
Posted by Peter Kwan on Jun-22-2011 00:12 |
|
Hi David,
Are you using image maps for the charts (for tooltips or clickable hot spots)? The ImageMapHandler is used for handling image maps.
ChartDirector can render charts very quickly. In charts that have a lot of data points, the image maps usually will take more CPU time than the chart itself. Each "hot spot" on the chart is like a tiny push button that can response to mouse actions. It is resource intensive to handler thousands of tiny push buttons on the chart.
To confirm if the image map is the cause of the problem, please do not create the image map at all (comment out all lines that calls setImageMap or getHTMLImageMap). If this solves the problem, we can confirm the majority of the CPU resources is used in processing the hot spots.
Actually, I am a little bit surprise to see CImageMapHandler_destroy can consume so much CPU resource. Is the testing performing in "debug" mode or "release" mode? Is it performmed inside the debugging environment? (Even in "release" mode, if the code is executed inside the IDE debugger, the performance will be quite different from actually executing the code.)
Because the hot spots are never needed unless the mouse is actually moving on the chart, one common method is to not generate the image map until the mouse is really moving on the chart.
For example, during resizing, there is no need to generate the image map, because the mouse is likely not on the chart during resizing (it may be dragging the window corner to resize). In fact, most of the time when the chart is rapidly changing, there is no need to generate the image map. The image map is only needed when the chart stops changing. By that time, a small delay caused by the image map would not cause any noticible lag.
In the ChartDirector "Zooming and Scrolling" sample code, you can drag the chart to scroll it. The scrolling is an animation achieved by redrawing the chart rapidly. If you try the code, you can see the scrolling is quite smooth. We achieve this by not updating the image map when the chart is still scrolling. We update the image map only when the chart has stopped moving.
Regards
Peter Kwan |
Re: Performance Issue |
Posted by David Wilson on Jun-22-2011 03:12 |
|
As usual, many thanks for your prompt and courteous reply. ChartDirector has a splendid support team.
We are aware that there is significant ChartDirector overhead associated with image maps, and we were not using them in the chart I tested. We are also aware of the overhead associated with Debug mode and were using Release mode in our testing. Specifically, our tests were performed in VS 2010 against a Release Win32 executable.
Even though I do not believe the chart in question was using image maps, I removed all references to image maps and tool tips (e.g., setImageMap, getHTMLImageMap, ImageMapHandler, etc.) from our application, rebuilt and retested. The qualitative results were not significantly different. CImageMapHandler_destroy() was using 54% of CPU time (about 18 seconds in my test).
It is conceivable that the debugger or VTune may have inflated the CImageMapHandler_destroy() runtime in the report. To test this, I ran the same program (Release Win32) standalone outside of the IDE. I didn't see a significant performance improvement, so I suspect that the VTune performance reports are close to reality.
The VTune output leads me to believe the CImageMapHandler_destroy() is being called many times. Is there normally more than one image map handler for a chart? Does this function call itself recursively to destroy pieces of itself? Is it called on image maps that don't actually exist?
Because the hot spots are never needed unless the mouse is actually moving on the chart, one common method is to not generate the image map until the mouse is really moving on the chart.
I appreciate the suggestions for avoiding image map overhead in resize, zoom and scroll, however, to the best of my knowledge, the chart I am testing does not use an image map at all. Perhaps I have done something inadvertently to cause image maps to be created on my chart.
At any rate, it would be really nice to solve this puzzle. |
Re: Performance Issue |
Posted by Peter Kwan on Jun-22-2011 15:08 |
|
Hi David,
The report that CImageMapHandler_destroy is using a lot of CPU is quite strange. (It should use negligible CPU, as basically it just releases memory.) Also, it should never be called if no image map is used. (It is only called when you call setImageMap.)
I assume you are using ChartDirector for C++, as only this version of ChartDirector has CImageMapHandler_destroy.
In ChartDirector for C++, the GUI code ChartViewer is open source. So we can trace this problem by tracing into the source code.
The CImageMapHandler_destroy is the destructor for the ImageMapHandler class. The only place where the ImageMapHandler is constructed is in setImageMap. If setImageMap is never called, the ImageMapHandler is never constructed and therefore cannot be freed.
To diagnose further, I suggest you may modify the "ChartViewer.cpp" (for MFC) or "qchartviewer.cpp" (for QT) in your project. Please find the setImageMap method, and comment out all the code there. In this way, the CImageMapHandler_destroy should never be called.
I have not used VTune before. Is it possible for the tool to find out how does the CImageMapHandler_destroy get called (a stack trace)? For finding the stack trace, you may also try to run the code in debug mode, and set a break point in the ImageMapHandler destructor. This is by opening the "chartdir.h" file, search for "~ImageMapHandler()". You should find that it calls "CImageMapHandler_destroy(ptr);". Please set a break point therefore. If the break point is hit, please obtain the stack trace to see why it is getting called.
Please kindly let me know of the results.
Regards
Peter Kwan |
Re: Performance Issue |
Posted by David Wilson on Jun-22-2011 23:49 |
|
We agree that CImageMapHandler_destroy() should not be hogging CPU, and probably should not be called at all if we are not using image maps. However, if VTune is correct, CImageMapHandler is being called many, many times within chartdir50.dll.
You are correct that I am using ChartDirector with Visual C++ in Visual Studio 2010.
Before posting my previous message, I had already found that ImageMapHandler instances were constructed in setImageMap(), and I'll take your word that this is the only place. At any rate, I commented out not just the contents of CChartViewer::setImageMap(), but entire method (in ChartViewer.h and ChartViewer.cpp), along with getImageMapHandler(), setDefaultToolTip() and getToolTip() methods, and removed all calls to these methods from our sources, so we are definitely not creating image maps via those methods. Even with this change, CImageMapHandler_destroy() still dominates the runtime.
I attempted to set a breakpoint for ~ImageMapHandler() as you asked. This did not work because ~ImageMapHandler() is an inline method, so the breakpoints were optimized away. Anyway, the ~ImageMapHandler() calls I am interested in apparently occur in chartdir50.dl (see below), so they are presumably replaced with CImageMapHandler_destroy() calls at compile time. As I do not have the CImageMapHandler_destroy() source, I cannot set a breakpoint in it.
The best I can do now is to show you a typical VTune stack trace. This trace comes from an application build in which I had setImageMap() commented out as described above, so our application code does not explicitly create image maps.
The trace, shows CImageMapHandler_destroy() being called ultimately from CBaseChart_makeChart2(). Note that CImageMapHandler_destroy() is called from within chartdir50.dll (perhaps as an expansion of inline method ~ImageMapHandler()). Note also that CImageMapHandler_destroy() seems to call itself recursively, in this case to a depth of 8 calls, which is curious if there is no ImageMapHandler object actually being destroyed.
chartdir50.dll!CImageMapHandler_destroy - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1d2b86 - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1d2ecf - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1d1035 - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1c49c5 - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1c4a71 - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1c8c00 - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1c0d89 - [Unknown]
chartdir50.dll!runMethod+0x15bdb8 - [Unknown]
chartdir50.dll!runMethod+0x14f16d - [Unknown]
chartdir50.dll!runMethod+0x142417 - [Unknown]
chartdir50.dll!runMethod+0x15df10 - [Unknown]
chartdir50.dll!runMethod+0x160f2c - [Unknown]
chartdir50.dll!runMethod+0xfe4f8 - [Unknown]
chartdir50.dll!runMethod+0xfe572 - [Unknown]
chartdir50.dll![chartdir50.dll]+0x6777 - [Unknown]
chartdir50.dll!CBaseChart_makeChart2+0x4c12a - [Unknown]
TrainOps.exe!CChartViewer::setChart(class BaseChart *) - chartviewer.cpp:396
[above this are more functions from our application]
Sometimes CViewPortManager_zoomTo() also shows up in the trace.
chartdir50.dll!CImageMapHandler_destroy - [Unknown]
chartdir50.dll!CImageMapHandler_destroy+0x1c0b1a - [Unknown]
chartdir50.dll!runMethod+0x13fbb0 - [Unknown]
chartdir50.dll!runMethod+0x1266ff - [Unknown]
chartdir50.dll!runMethod+0x124d01 - [Unknown]
chartdir50.dll!CViewPortManager_zoomTo+0x62eff - [Unknown]
chartdir50.dll!CViewPortManager_zoomTo+0x69ffd - [Unknown]
chartdir50.dll!runMethod+0x11ee1f - [Unknown]
chartdir50.dll!runMethod+0x11dad1 - [Unknown]
chartdir50.dll![chartdir50.dll]+0x6713 - [Unknown]
chartdir50.dll!CBaseChart_makeChart2+0x4c12a - [Unknown]
TrainOps.exe!CChartViewer::setChart(class BaseChart *) - chartviewer.cpp:396
[above this are more functions from our application]
Thank you for your help so far. I hope this helps you pinpoint the issue. |
Re: Performance Issue |
Posted by Peter Kwan on Jun-23-2011 03:29 |
|
Hi David,
From the output your included in your message, It is quite possible CImageMapHandler_destroy is never called. Also, there is no recrusive call, as the address of each call is different.
The line "chartdir50.dll!CImageMapHandler_destroy+0x1d2b86" means a subroutine at that address is being called, but it is not CImageMapHandler_destroy. In fact, it is very far away from CImageMapHandler_destroy, up to 0x1d2b86 bytes away.
What I guess is that VTune know the address being called. Now VTune needs to translate the address into a symbol for human reading. But there are no symbols for internal methods in "chartdir50.dll". (ChartDirector is a released build and all debug symbols are stripped away.) The only symbols available are export symbols in the DLL (the publicly accessing API). So VTune just guesses based on the nearest available symbol, which happens to be CImageMapHandler_destroy.
I can also show you the source code of CImageMapHandler_destroy. It just has one line, and does not call itself recrusively.
void CImageMapHandler_destroy(ImageMapHandlerInternal *ptr)
{ delete (ImageMapHandler *)ptr; }
The CImageMapHandler_destroy is never used inside ChartDirector. It exists purely as an external interface. In fact, all the C???_??? methods are never called internally by ChartDirector.
I see that the call starts from setChart. It is not surprising that the setChart call consumes the majority of the CPU. Most of the methods in ChartDirector are just setting up the parameters (it just stores the parameters you supplied), and consumes negligible CPU. When you call setChart, it will actually draw the chart. Computer graphics code can consume quite some CPU, and certain methods (such as the method to access a pixel) can get call a huge number of times (because there are many pixels).
For most of the charts, the main factor affecting performance is the number of pixels (not the number of data points, unless you have millions of them). If you are plotting a chart like that in the "realtime chart", which is around 600 x 270 in size, it should take very little CPU (may be a few percent per chart). However, if you are plotting a full HD screen 1920 x 1080 chart (which contains almost 13 times more pixels), it may take more CPU time.
Generating a realtime chart in ChartDirector is like generating a movie dynamically on the fly. As a rule of thumb, ChartDirector should easily update at DVD quality (720 x 480 pixels at 24 charts per second). However, if would be much more demanding to generate an HD (1920 x 1080) quality movie at realtime on the fly. The frame rate may be much lower.
If you could inform me the chart size you are using, the number of data points, the chart update rate, and CPU consumed, and the CPU type you are using, I may see if it looks normal or abnormal to me.
Regards
Peter Kwan |
Re: Performance Issue |
Posted by David Wilson on Jun-23-2011 05:11 |
|
Apparently your analysis is correct. VTune is misattributing the CPU time of unknown functions in chartdir50.dll to CImageMapHandler_destroy(), runMethod() and CViewPortManager_zoomTo().
This is bad news for us, since I was hoping to get more speed out of ChartDirector by eliminating image maps, and it seems we are not actually using them. So ~75% of our application CPU time is spent in legitimate ChartDirector rendering, and there's not much we can do about it.
FYI, our test charts are rendered at about 1920 x 2000 pixels, which I suppose would tax ChartDirector rendering capabilities.
Anyway, thank you for helping me sort this out. I'll contact you if I need further assistance.
- Dave Wilson |
|