PMH4514
asked on
Design critique question - UI performance
HI!
Maybe somebody can suggest some improvements for me.
I have a CStatic derived class called CDisplay and a CCamera base class that pushes frames, around 10fps to the display. CCamera also has a CViewFinder base class that can alpahblend into the camera display, one of many "menus" implemented in CViewfinderMenu each containing "widgets" that I've implemented as CWidget.
So we have what amounts to menus and buttons, but they are graphical and blended directly into the video feed, as opposed to MFC dialogs and CButton's as they aren't static, they are painted (and the menu background alphablended) into a video stream.
So in my CDisplay::PreTranslateMess age() (the only CStatic that can grab the messages) I check for mouse movement, and I forward the MSG* into CViewFinder. The CViewFinder will then forward the MSG* into each of its CViewfinderMenu instances, and the CViewFinderMenu will forward the MSG* into each of its CWidget instances. CWidget implements it's UI type stuff just like any button would, with normal, glow and pressed states.
Everything uses a CPtrList as the collection. the viewfinder has a CPtrList of menus, each menu has a CPtrList of widgets.
Each time I "forward the MSG pointer" it's just a matter of looping through, for example:
POSITION pos = NULL;
for( pos = m_pWidgets->GetHeadPositio n(); pos != NULL; )
((CWidget*)m_pWidgets->Get Next(pos)) ->CheckMes sage(pMSG) ;
Anyway, everything does work as I'd expect, but it's a bit "sluggish" - not the video frame rate, rather, the speed at which my elements respond to the mouse messages. I roll my mouse into the camera's video area, and the alphablended menus appear and my widgets glow and unglow as the mouse enters/leaves them, but rather than a widget glowing immediately upon mouse entry, there is a delay, perhaps 1/2 a second (at most, but it's not immediate, and UI/visual responses need to be immediate)
At first I thought "well maybe it's because repaints are happening at the cameras frame rate" - but this model is implemented against two hardware devices, one at 10fps the other running at 30fps.. I don't perceive any difference in UI response regardless which feed is running, so I'm not sure if it's that.
Perhaps CPtrList is not the right container? would switching to a vector or deque improve performance? Maybe some kind of global lookup table of widgets so rather than constantly looping through everything I can look directly to a mouse postion? Any suggestions on designs to improve UI performance here?
thanks!
-Paul
Maybe somebody can suggest some improvements for me.
I have a CStatic derived class called CDisplay and a CCamera base class that pushes frames, around 10fps to the display. CCamera also has a CViewFinder base class that can alpahblend into the camera display, one of many "menus" implemented in CViewfinderMenu each containing "widgets" that I've implemented as CWidget.
So we have what amounts to menus and buttons, but they are graphical and blended directly into the video feed, as opposed to MFC dialogs and CButton's as they aren't static, they are painted (and the menu background alphablended) into a video stream.
So in my CDisplay::PreTranslateMess
Everything uses a CPtrList as the collection. the viewfinder has a CPtrList of menus, each menu has a CPtrList of widgets.
Each time I "forward the MSG pointer" it's just a matter of looping through, for example:
POSITION pos = NULL;
for( pos = m_pWidgets->GetHeadPositio
((CWidget*)m_pWidgets->Get
Anyway, everything does work as I'd expect, but it's a bit "sluggish" - not the video frame rate, rather, the speed at which my elements respond to the mouse messages. I roll my mouse into the camera's video area, and the alphablended menus appear and my widgets glow and unglow as the mouse enters/leaves them, but rather than a widget glowing immediately upon mouse entry, there is a delay, perhaps 1/2 a second (at most, but it's not immediate, and UI/visual responses need to be immediate)
At first I thought "well maybe it's because repaints are happening at the cameras frame rate" - but this model is implemented against two hardware devices, one at 10fps the other running at 30fps.. I don't perceive any difference in UI response regardless which feed is running, so I'm not sure if it's that.
Perhaps CPtrList is not the right container? would switching to a vector or deque improve performance? Maybe some kind of global lookup table of widgets so rather than constantly looping through everything I can look directly to a mouse postion? Any suggestions on designs to improve UI performance here?
thanks!
-Paul
As wayside says run a release build. Debug has loads of error checks that slows things down a lot.
Build menu - Profile. (Follow instructions, look it up in help). That will provide a lot of info as to what functions take the time up. It is worth running. Rather than assuming a bit of your code is slow check - the bottleneck may not be where you think it is.
Build menu - Profile. (Follow instructions, look it up in help). That will provide a lot of info as to what functions take the time up. It is worth running. Rather than assuming a bit of your code is slow check - the bottleneck may not be where you think it is.
ASKER
Hi guys -
yes, actually I did try on a release build. There is improvement as I'd expect, but there is still a delay, which if it wasn't pertaining to visual response to mouse, I wouldn't care, but I have to optimize it somehow.
We're not even talking about alot of menus.. A single 1000x1000 24bit image from the video stream will get two 400x400 rects alphablended onto it, and at most 8 "widgets" - each being a 30x30 bitmap resource.. I do all the widget painting into a memory DC once, the menu backgrounds of course have to get alphablended per frame, but everything else is pre-drawn an only blitted in place as the frames update.
CPU utilization is always kinda high, as I am streaming 2 video streams at once, one at 10fps, the other at 30fps, each 1000x1000..
But even when I turned one of them off, it's still a bit sluggish.
AndyAinscow - I'll checkout the Profile option.. It's disabled on my Build Menu. I'll look it up in help.
yes, actually I did try on a release build. There is improvement as I'd expect, but there is still a delay, which if it wasn't pertaining to visual response to mouse, I wouldn't care, but I have to optimize it somehow.
We're not even talking about alot of menus.. A single 1000x1000 24bit image from the video stream will get two 400x400 rects alphablended onto it, and at most 8 "widgets" - each being a 30x30 bitmap resource.. I do all the widget painting into a memory DC once, the menu backgrounds of course have to get alphablended per frame, but everything else is pre-drawn an only blitted in place as the frames update.
CPU utilization is always kinda high, as I am streaming 2 video streams at once, one at 10fps, the other at 30fps, each 1000x1000..
But even when I turned one of them off, it's still a bit sluggish.
AndyAinscow - I'll checkout the Profile option.. It's disabled on my Build Menu. I'll look it up in help.
ASKER
hmm.. I've enabled profiling in my link settings and rebuilt the probjec.. but Build\Profile is still grayed out ??
I think you need the professional or higher version of VC++ (my memory could be wrong here). Have you got a standard/student version?
If it's disabled, I think that means you are running VC++ 6.0 Standard version. IIRC, you need the Professional version to get the profiler.
ASKER
I'm running Enterprise Edition.. is "professional" higher?
No, enterprise is higher than pro. It ought to be enabled on your build menu then.
Is the profiler an installation option?
Is the profiler an installation option?
You could always do "poor man's" profiling.
class Profiler {
Profiler(const char *funcname);
~Profiler();
private:
const char *m_funcname;
int m_start;
}
Profiler::Profiler(const char *funcname) {
m_funcname = funcname;
m_start = GetTickCount();
}
Profiler::~Profiler() {
FILE *fp = fopen("c:/temp/time.log", "a+");
if (fp) {
fprintf(fp, "%s: start: %ld elasped: %ld\n", m_funcname,
m_start, GetTickCount()-m_start);
fclose(fp);
}
}
#define PROFILE_FUNC(x)
#if PROFILE_MY_CODE == 1
Profiler prof(x);
#endif
Then at the top of each function you want to time, add:
PROFILE_FUNC(__FUNCTION__)
Add a define of PROFILE_MY_CODE=1 into the preprocessor options to turn on profiling. Set to zero to turn it off.
Run your program for a while, and then analyzet the log file to see where the time is going.
class Profiler {
Profiler(const char *funcname);
~Profiler();
private:
const char *m_funcname;
int m_start;
}
Profiler::Profiler(const char *funcname) {
m_funcname = funcname;
m_start = GetTickCount();
}
Profiler::~Profiler() {
FILE *fp = fopen("c:/temp/time.log", "a+");
if (fp) {
fprintf(fp, "%s: start: %ld elasped: %ld\n", m_funcname,
m_start, GetTickCount()-m_start);
fclose(fp);
}
}
#define PROFILE_FUNC(x)
#if PROFILE_MY_CODE == 1
Profiler prof(x);
#endif
Then at the top of each function you want to time, add:
PROFILE_FUNC(__FUNCTION__)
Add a define of PROFILE_MY_CODE=1 into the preprocessor options to turn on profiling. Set to zero to turn it off.
Run your program for a while, and then analyzet the log file to see where the time is going.
ASKER
is that basically what profiler does wayside?
>>Is the profiler an installation option?
I'm not sure.. ??
>>Is the profiler an installation option?
I'm not sure.. ??
> is that basically what profiler does wayside?
There are different ways of profiling.
One method typically modifies the assembly code to intercept function calls. It can then record information about what function it is in, who called it, timing information, etc. This can then be presented in a very nice UI that lets you see where all the time is going, and the calling paths into these functions. BoundsChecker works this way.
Another method is to interrupt the program every so often, say every 5 milliseconds, and recording what function you are in. At the end, by figuring out how many times you were in each function vs. the total number of samples taken, you can figure out how much time each function used relative to the others. This method doesn't give you insight into where the functions are being called from, though. I think the built-in profiler works like this, but my memory is hazy, I haven't used the built-in one in years (I don't even see it in VC++ .Net).
My crude method will give you actual elasped times, unlike the statistical data of method 2, but it also may not give you much insight into which code paths may be using all the time.
There are different ways of profiling.
One method typically modifies the assembly code to intercept function calls. It can then record information about what function it is in, who called it, timing information, etc. This can then be presented in a very nice UI that lets you see where all the time is going, and the calling paths into these functions. BoundsChecker works this way.
Another method is to interrupt the program every so often, say every 5 milliseconds, and recording what function you are in. At the end, by figuring out how many times you were in each function vs. the total number of samples taken, you can figure out how much time each function used relative to the others. This method doesn't give you insight into where the functions are being called from, though. I think the built-in profiler works like this, but my memory is hazy, I haven't used the built-in one in years (I don't even see it in VC++ .Net).
My crude method will give you actual elasped times, unlike the statistical data of method 2, but it also may not give you much insight into which code paths may be using all the time.
ASKER
ok I'll give your profiler a try, see what I see.
Any other comments on the approach itself? using CPtrList vs. some other type of container? etc..
Any other comments on the approach itself? using CPtrList vs. some other type of container? etc..
You have two menus with 8 widgets each?
I really don't think the container is the problem.
I wrote some code to compare the performance of CArray, vector, a static array and a dynamic array (https://www.experts-exchange.com/questions/20981128/CArray-slows-execution.html).
I used loops going through 50 million iterations, the worst performer took < 3 seconds. Scaling down to 16 interations, the time would be on the order of 1 microsecond.
Using an array or vector would probably be quicker than a linked list, but you are still talking about microseconds for CPtrList. I'm betting the time is being spent in the repaint code.
I really don't think the container is the problem.
I wrote some code to compare the performance of CArray, vector, a static array and a dynamic array (https://www.experts-exchange.com/questions/20981128/CArray-slows-execution.html).
I used loops going through 50 million iterations, the worst performer took < 3 seconds. Scaling down to 16 interations, the time would be on the order of 1 microsecond.
Using an array or vector would probably be quicker than a linked list, but you are still talking about microseconds for CPtrList. I'm betting the time is being spent in the repaint code.
ASKER
>>I'm betting the time is being spent in the repaint code.
probably right.. I'll update when I've figured out something tangible.
probably right.. I'll update when I've figured out something tangible.
ASKER
nice loop tests by the way!
ASKER
wayside:
I placed this in my stdafx.h file:
#include "Profiler.h"
#define PROFILE_FUNC(x)
#if PROFILE_MY_CODE == 1
Profiler prof(x);
#endif
and I get a compiler error:
error C2065: 'x' : undeclared identifier
I placed this in my stdafx.h file:
#include "Profiler.h"
#define PROFILE_FUNC(x)
#if PROFILE_MY_CODE == 1
Profiler prof(x);
#endif
and I get a compiler error:
error C2065: 'x' : undeclared identifier
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
oh ok thanks.
in this:
PROFILE_FUNC(__FUNCTION__) ;
at the top of any function I want to profile, is __FUNCTION__ a macro?
I'm getting:
error C2065: '__FUNCTION__' : undeclared identifier
in this:
PROFILE_FUNC(__FUNCTION__)
at the top of any function I want to profile, is __FUNCTION__ a macro?
I'm getting:
error C2065: '__FUNCTION__' : undeclared identifier
ASKER
oh ps. way above, wayside, you wrote:
>>What does the CheckMessage() function do?
that just does this:
POSITION pos = NULL;
for( pos = m_pWidgets->GetHeadPositio n(); pos != NULL; )
((CWidget*)m_pWidgets->Get Next(pos)) ->CheckMes sage(pMSG) ;
>>What does the CheckMessage() function do?
that just does this:
POSITION pos = NULL;
for( pos = m_pWidgets->GetHeadPositio
((CWidget*)m_pWidgets->Get
Hmm, __FUNCTION__ must be a VC++ .Net -only macro, and not be defined in VC++ 6.0.
This makes it slightly more painful to use these, you will have to put the name in yourself in each function:
int MyClass::func1() {
PROFILE_FUNC("MyClass::fun c1");
...
}
This makes it slightly more painful to use these, you will have to put the name in yourself in each function:
int MyClass::func1() {
PROFILE_FUNC("MyClass::fun
...
}
ASKER
oh I see.
>>This makes it slightly more painful to use these,
oh well, there will only be a few I'll be checking
thanks!
>>This makes it slightly more painful to use these,
oh well, there will only be a few I'll be checking
thanks!
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Good point, Andy.
Also, note that because the timing is based on object construction/destruction, you can use this anywhere you have a separate scope:
int MyClass::func1(){
PROFILE_FUNC("func1 start")
...
if (some_condition == true) {
PROFILE_FUNC("func1 some_condition == true block")
}
}
This would give you a timing of both the entire function, and just that one specific block.
Also, note that because the timing is based on object construction/destruction, you can use this anywhere you have a separate scope:
int MyClass::func1(){
PROFILE_FUNC("func1 start")
...
if (some_condition == true) {
PROFILE_FUNC("func1 some_condition == true block")
}
}
This would give you a timing of both the entire function, and just that one specific block.
ASKER
cool thanks.
interestingly... I changed the priority of the thread that is capturing video frames and pushing them through these filters to THREAD_PRIORITY_TIME_CRITI CAL and that made a substantial difference in how fast the UI responds..
I know threads aren't supposed to update the UI, but I think this is a slightly different case, what with no CStatic type window controls being involved.. since the "menus & buttons" are actually painted into each video frame, I could think of no other way
interestingly... I changed the priority of the thread that is capturing video frames and pushing them through these filters to THREAD_PRIORITY_TIME_CRITI
I know threads aren't supposed to update the UI, but I think this is a slightly different case, what with no CStatic type window controls being involved.. since the "menus & buttons" are actually painted into each video frame, I could think of no other way
If I'm reading the docs right, that thread priority will boost your thread to higher than any other thread except those belonging to a realtime process. This could noticeably slow down everything else on your machine.
If you decide to boost your priority as part of your solution, you might want to experiment with different priorities to see what the lowest priority is that gives you the results you want.
If you decide to boost your priority as part of your solution, you might want to experiment with different priorities to see what the lowest priority is that gives you the results you want.
ASKER
>>This could noticeably slow down everything else on your machine.
That is probably OK though.. Our app/computer is integrated with our hardware, users won't be running other apps or anything.
it's not *the* solution, there is still some lag.. less, but still noticeable..
That is probably OK though.. Our app/computer is integrated with our hardware, users won't be running other apps or anything.
it's not *the* solution, there is still some lag.. less, but still noticeable..
Thread priority is probably a hack. Find out what bit of code is slow then look at how to optimise it.
ASKER
>> Thread priority is probably a hack. Find out what bit of code is slow then look at how to optimise it.
agreed.
agreed.
ASKER
update - I figured out why Profile was a greyed out option even though I had it installed and was running enterprise edition.
See this tech note:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q224382
See this tech note:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q224382
Howmany menus and widgets are we talking about? Unless you have hundreds or thousands of widgets, I doubt switching containers will make a noticeable difference.
It's really tough to say where the bottlenecks are without profiling the code. Do you have access to any performance measuring tools such as BoundsChecker?
What does the CheckMessage() function do?
How long does it take to paint the widget?
How much CPU is being used when no mouse activity is taking place?
One optimization that comes to mind would be for CViewFinderMenu to maintain a bounding box of all of its widgets. A quick check can then tell you if you need to send the message to any of the widgets in that menu. That would cut down a lot on how much message forwarding is going on.