?
Solved

Design critique question - UI performance

Posted on 2005-04-26
29
Medium Priority
?
367 Views
Last Modified: 2013-12-03
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::PreTranslateMessage()  (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->GetHeadPosition(); pos != NULL; )
            ((CWidget*)m_pWidgets->GetNext(pos))->CheckMessage(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

0
Comment
Question by:PMH4514
  • 15
  • 9
  • 5
29 Comments
 
LVL 14

Expert Comment

by:wayside
ID: 13867072
Dumb question, as I'm sure you've done this - are you running a release version?

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.

0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 13867228
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.
0
 

Author Comment

by:PMH4514
ID: 13867304
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.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 

Author Comment

by:PMH4514
ID: 13867371
hmm.. I've enabled profiling in my link settings and rebuilt the probjec.. but Build\Profile is still grayed out ??
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 13867466
I think you need the professional or higher version of VC++ (my memory could be wrong here).  Have you got a standard/student version?
0
 
LVL 14

Expert Comment

by:wayside
ID: 13867504
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.
0
 

Author Comment

by:PMH4514
ID: 13867561
I'm running Enterprise Edition.. is "professional" higher?
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 13867606
No, enterprise is higher than pro.  It ought to be enabled on your build menu then.
Is the profiler an installation option?
0
 
LVL 14

Expert Comment

by:wayside
ID: 13867614
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.
0
 

Author Comment

by:PMH4514
ID: 13867682
is that basically what profiler does wayside?

>>Is the profiler an installation option?
I'm not sure.. ??
0
 
LVL 14

Expert Comment

by:wayside
ID: 13867837
> 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.
0
 

Author Comment

by:PMH4514
ID: 13868231
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..
0
 
LVL 14

Expert Comment

by:wayside
ID: 13868485
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 (http://www.experts-exchange.com/Programming/Programming_Languages/MFC/Q_20981128.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.

0
 

Author Comment

by:PMH4514
ID: 13868933
>>I'm betting the time is being spent in the repaint code.

probably right.. I'll update when I've figured out something tangible.
0
 

Author Comment

by:PMH4514
ID: 13869801
nice loop tests by the way!
0
 

Author Comment

by:PMH4514
ID: 13869941
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

0
 
LVL 14

Accepted Solution

by:
wayside earned 800 total points
ID: 13870082
Sorry, I typed all that in without test-compiling it. I found a few other typos too.

Here's code that I have successfully compiled:

class Profiler {
  public:
   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);
   }
}

#if  PROFILE_MY_CODE == 1
#define PROFILE_FUNC(x) Profiler prof(x);
#else
#define PROFILE_FUNC(x)
#endif


0
 

Author Comment

by:PMH4514
ID: 13870149
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

0
 

Author Comment

by:PMH4514
ID: 13870232
oh ps. way above, wayside, you wrote:

>>What does the CheckMessage() function do?

that just does this:

      POSITION pos = NULL;          
      for( pos = m_pWidgets->GetHeadPosition(); pos != NULL; )
          ((CWidget*)m_pWidgets->GetNext(pos))->CheckMessage(pMSG);
0
 
LVL 14

Expert Comment

by:wayside
ID: 13870237
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::func1");
  ...
}

0
 

Author Comment

by:PMH4514
ID: 13870262
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!
0
 
LVL 45

Assisted Solution

by:AndyAinscow
AndyAinscow earned 200 total points
ID: 13870291
One teeny weeny point.  In
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);
   }
}

you may wish to use something like
Profiler::~Profiler() {
int iStop = GetTickCount();                                 <<-----------------------
   FILE *fp = fopen("c:/temp/time.log", "a+");
   if (fp) {
      fprintf(fp, "%s: start: %ld elasped: %ld\n", m_funcname,
                                m_start, iStop-m_start);
      fclose(fp);
   }
}

otherwise you are also timing the file open call as well as your function.  (Otherwise a very nice looking class)
0
 
LVL 14

Expert Comment

by:wayside
ID: 13870357
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.
0
 

Author Comment

by:PMH4514
ID: 13870382
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_CRITICAL  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
0
 
LVL 14

Expert Comment

by:wayside
ID: 13870476
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.
0
 

Author Comment

by:PMH4514
ID: 13870540
>>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..
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 13870605
Thread priority is probably a hack.  Find out what bit of code is slow then look at how to optimise it.
0
 

Author Comment

by:PMH4514
ID: 13870619
>> Thread priority is probably a hack.  Find out what bit of code is slow then look at how to optimise it.
agreed.
0
 

Author Comment

by:PMH4514
ID: 13998335
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
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Dynamic window placements and drawing on a form, simple usage of windows registry as a storage place for information. Continuing from the first article about sudoku.  There we have designed the application and put a lot of user int…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
Suggested Courses

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question