Link to home
Start Free TrialLog in
Avatar of funcoding
funcoding

asked on

multithreading .net vc++ windows form application

I have several tabs in my main form. at form load, each one of the tabs get filed by calling separate functions for what goes in each tab:

Form1_Load( )
 tab1();
tab2();
 ....
 tab7();

the problem right now is that since each tab is getting loaded at form load, the program takes good 15-20 secs to start. i was wondering if there is a way to split this task in multiple threads or multiple processes  to get it done faster?
I am using windows forms application, vc++ and VS2008
Avatar of funcoding
funcoding

ASKER

forgot to mention that the application takes all this long to load up and then when each tab is clicked, it takes another few seconds for the tools on that tab to appear, very slow. Mainly because i have for loops at each tab load to generate tools in multiple rows and columns. but once i have clicked on one tab and all the tools have loaded, i can go to another tab and come back and it doesn't any time for the tools to come up. So i guess the overhead is only at the first time the form loads and also when a tab is clicked on for the first time.
Avatar of HooKooDooKu
Rule #1 about Multithreading, it doesn't make things run faster.  It just makes an application more responsive.  As an example, if you load all the tabs inside of a thread, your application will respond the things like mouse clicks and form resize events because the main processing thread isn't tied up in the logic loading tabs.

Now if something else is going on, like say the application is displaying a splash screen or prompting the user for a password, while the application does those things, it's not going to be using much processor power.  That is when it's a good idea to use multithreading.

So unless the current application has some dead time in it, the only thing you will accomplish running in multithreading is all the tabs will load at the same time, but each tab will load slower because they are all sharing the same CPU and will still take a total of about 15 seconds.

The bigger question is what is taking so long to load the tabs?  if the answer is that it"s waiting to retrieve information from a database located on another computer, then multithreading might make things faster.  The reason would be that Tab1 might be able to do some of the work running calculations to layout the tab, while Tab2 is waiting on the DB server to respond.  That's an example where multithreading speeds things up, because while one thread is "stuck" waiting for something to happen, other threads can use that wait time to do something.

But if all the processing is on your machine and there isn't dead time, multithreading won't help.
thanks for the clarification. at the moement, I am not trying to retrieve info from server, this is all from my pc. Part of the problem is that i am using older code for generating the tabs. Below is code for just one tab.


private: System::Void Filltab()
			{
				// Fill in the following variables to customize the table
				int NumCols = 8;
				int NumRows = 10;
				int ColWidth[] = {130, 50, 150, 50, 175, 125, 100, 80};
				
				int i, Spacing;
				int TableX = 30;  // distance between table and left of window
				int TableY = 60;  // distance between table and top of window
				int TableHeight = this->tabPage2->Height - 100;  //Fill in tabPage
				int TitleX = 30;  // distance between left column title and left of window
				int TitleY = 40;  // distance between column titles and top of window
				int RowHeight = 25;
				StringCollection^ ColumnTitles = gcnew StringCollection;
				// Fill in the definition of controls for the table
				System::Windows::Forms::Label^ Title;
				System::Windows::Forms::Button^ button1;
				System::Windows::Forms::Button^ button2;
				System::Windows::Forms::CheckBox^ checkbox1;
				System::Windows::Forms::ComboBox^ combobox2;
				System::Windows::Forms::ComboBox^combobox3;
				System::Windows::Forms::ComboBox^ combobox4;
				System::Windows::Forms::TextBox^ textbox1;
				System::Windows::Forms::TableLayoutPanel^  table1;
 
				ColumnTitles->AddRange( myArr );
				tabletab = gcnew System::Windows::Forms::TableLayoutPanel();
				tabletab->Location = System::Drawing::Point(TableX, TableY);
				tabletab->ColumnCount = NumCols;
				tabletab->RowCount = NumRows;
				tabletab->AutoScroll = true;
 
				this->SuspendLayout();
 
				// Create column titles and format widths of table columns
				for ( i=0; i<NumCols; i++  )
				{
					Title = gcnew System::Windows::Forms::Label();
					Title->Text = ColumnTitles[i];
					Title->AutoSize = true;
					if ( i == 0 )
					{
						Title->Location = System::Drawing::Point(TitleX+10, TitleY);  // The 10 is added to line it up with the text on the buttons directly below it
						Spacing = ColWidth[i];
					}
					else
					{
						Title->Location = System::Drawing::Point(TitleX + Spacing, TitleY);
						Spacing += ColWidth[i];
					}
					tabletab->ColumnStyles->Add(gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Absolute, (float) ColWidth[i]));
					this->tabPage7->Controls->Add(Title);  // Fill in correct tabPage number
				}
 
				// make sure that the table doesn't over run the size of the tab control
				if ( Spacing > (tabControl1->Width - 100) )
				{
					Spacing = tabControl1->Width - 100;
				}
				tabletab->Size = System::Drawing::Size(Spacing + 50, TableHeight);
 
				// Fill in code for each control in the table
				for ( i=0; i<NumRows; i++  )
				{
					tabletab->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Absolute, (float) RowHeight)));
 
					 button1 = gcnew System::Windows::Forms::Button();
					if ( i < 9 )
					{
						 button1->Text = "text" + (i+1).ToString();
					}
					else
					{
						 button1->Text = "text" + (i+1).ToString();
					}
					 button1->AutoSize = true;
					 button1->FlatAppearance->BorderSize = 0;
					 button1->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
					 button1->TextAlign = System::Drawing::ContentAlignment::TopLeft;
					
					 button2 = gcnew System::Windows::Forms::Button();
					 button2->Text = "J" + (i+1).ToString();
					 button2->AutoSize = true;
					 button2->FlatAppearance->BorderSize = 0;
					 button2->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
					 button2->TextAlign = System::Drawing::ContentAlignment::TopLeft;
					
					Textbox1 = gcnew System::Windows::Forms::TextBox();
					Textbox1->Text = "";
					Textbox1->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					tableResOut->Controls->Add(TextControl, 2, i);
 
					// Enable CheckBox
					checkbox1 = gcnew System::Windows::Forms::CheckBox();
					checkbox1->Checked = false;
					
 
					// Drop Down
					combobox2= gcnew System::Windows::Forms::ComboBox();
					combobox2->DropDownStyle = ComboBoxStyle::DropDownList;
					combobox2->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					combobox2->Items->Add("324-178800");
					combobox2->Items->Add("324-178801");
					combobox2->Items->Add("324-178802");
					combobox2->Items->Add("324-178803");
					combobox2->Items->Add("324-178804");
					tabletab->Controls->Add(combobox2, 4, i);
 
					// Layout Name
					combobox3 = gcnew System::Windows::Forms::ComboBox();
					combobox3->DropDownStyle = ComboBoxStyle::DropDownList;
					combobox3->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					combobox3->Items->AddRange( this->DefaultLayouts );
					tabletab->Controls->Add(combobox3, 5, i);
 
					// Instrument Type
					combobox4 = gcnew System::Windows::Forms::ComboBox();
					combobox4->DropDownStyle = ComboBoxStyle::DropDownList;
					combobox4->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					combobox4->Items->Add("NumericInput");
					combobox4->Items->Add("Slider");
					combobox4->Items->Add("Knob");
					combobox4->Text = "NumericInput";
					tabletab->Controls->Add(combobox4, 6, i);
 
					Textbox1 = gcnew System::Windows::Forms::TextBox();
					Textbox1->Text = "";
					tabletab->Controls->Add(Textbox1, 7, i);
				}
 
				// Fill in correct tabPage and Index
				this->tabPage7->Controls->Add(tabletab);
				Table7Index = this->tabPage7->Controls->Count-1;
				this->ResumeLayout(false);
			}

Open in new window

this much is just for one tab and then there are about 7 of them. It takes a good few seconds for the main window to come up and as i said each tab takes its own time afterwards
The "golden rule of optimization" is to use a profiler to measure how much time is spent in the different parts of your code.
The only profiler I have had good experiences with is shiny:

http://sourceforge.net/docman/?group_id=208504

It's a bit of pain that it does not work "out of the box". I had to compile it into a separate library (in the same way as you would create any other C++ library). A quick and dirty approach - if you want to play with it - would be to add all Shiny source files directly to your project.  However, after that, it is a great tool.

To use it, you should include Shiny.h in your main.cpp file (or similar), and at the end of the main function put the lines

PROFILER_UPDATE()
PROFILER_OUTPUT()

Then in your code, to know how much time is spent in a particular section, you can surround that section with PROFILE_BEGIN( name ) and PROFILE_END macros.

PROFILE_BEGIN ( fillDropDown )
combobox2= gcnew System::Windows::Forms::ComboBox();
combobox2->DropDownStyle = ComboBoxStyle::DropDownList;
// etc, etc
PROFILE_END ()

The measured times for the different sections will be printed when the program exits the main function.
I have tried commercial profiler tools which have a GUI, but they would crash or give >very< inaccurate results.
thanks for the suggestion maartennieber. I will check it out, would be useful for other projects also.
Any suggestions for making for loops of all the tabs like the one i have posted earlier any better/faster?
When i posted this question, i was thinking the program download accelerator, that splits the file to be downloaded. Is there not a way to do something like that here to speed the startup of form faster?
Well, I must say that a brief overview of the tab loading logic doesn't show any obvious places where things are slowing down.  But it is obviously doing a lot of updating the user interface.  A lot of your time MIGHT simply be in refreshing the window over and over again.  If that is indeed the issue, then a typical solution is to hide the window while you make a bunch of updates, and then show the window once all the updates have been made.  That way the window and the controls only get drawn once.
Is there any other way of populating the tools in each row that may speed up the load process?
I have static texts, drop down menus, and checkboxes. I think a total of about 8, 9 of the toolboxes in each row and about 20 rows on each tab. What would be an efficient way of populating such tools on project startup?
I guess i would have to start a new question and maybe ask my question in a better way...
I would like to help you, but in my experience, it pays to do things systematically, which means in this case: use a profiler to find out where the application is spending too much time.
Just guessing what needs to be changed will likely produce sub-optimal results and will probably cost you more time than learning how to use the profiler.

A note on the Shiny profiler: if you decide to put the Shiny code in a library, then create a DLL and not a static lib. A Shiny DLL will be shared by all libraries in your solution (whereas a static lib will not), and this will allow you to obtain profiling data from different libraries in a single report.
maartennieber, I am trying to build the shiny project. the project itself has Shiny, Recursion and Simple in it. I edited Shiny properties under Project Deafults->Configuration type, i changed that to Dynamic library. Now when i build the project it only generates the dll and not the lib or header with it for me to add to my project. what do i need to do for the project to export symbols also? and if you could comment on how i would add it to my project also. Thanks
Following is the output of the profiler:
flat profile                               hits       self time      total time
<root>                                      1.0     11 s    67%     27 s   167%
GUI_IP::Form1::Form1_Load                   1.0     20 ms    0%     16 s   100%
GUI_IP::Form1::tab1                      1.0      3 s    19%      3 s    19%
GUI_IP::Form1::tab2                      1.0    604 ms    4%    604 ms    4%
GUI_IP::Form1::tab3                   1.0    258 ms    2%    258 ms    2%
GUI_IP::Form1::tab4                   1.0      4 s    23%      4 s    23%
GUI_IP::Form1::tab5                    1.0    643 ms    4%    643 ms    4%
GUI_IP::Form1::tab6                   1.0      4 s    24%      4 s    24%
GUI_IP::Form1::tab7                    1.0      4 s    25%      4 s    25%

call tree                                  hits       self time      total time
<root>                                      1.0     11 s    67%     27 s   167%
  GUI_IP::Form1::Form1_Load                 1.0     20 ms    0%     16 s   100%
    GUI_IP::Form1::tab1                  1.0      3 s    19%      3 s    19%
    GUI_IP::Form1::tab2                  1.0    604 ms    4%    604 ms    4%
    GUI_IP::Form1::tab3               1.0    258 ms    2%    258 ms    2%
    GUI_IP::Form1::tab4               1.0      4 s    23%      4 s    23%
    GUI_IP::Form1::tab5                1.0    643 ms    4%    643 ms    4%
    GUI_IP::Form1::tab6               1.0      4 s    24%      4 s    24%
    GUI_IP::Form1::tab7                1.0      4 s    25%      4 s    25%
I forgot to mention that i didn't use the way you specified for the profiler call. you had said profiler_begin(func_name). Instead, I called PROFILER_FUNC() at the beginning of the each function
I was looking at the time each one of these took and it makes sense for diff times for each tab because i have diff number of rows and elements in each tab and the time shown by profiler is proportional to what i have in each tab
Great, this already tells us that it is not a single tab that acts as the bottleneck.

I would now suggest to select the simplest one of the slow functions (either tab1, tab4, tab6 or tab7, whichever is simplest), and try to detail the profile by creating profiled sections using PROFILE_BEGIN (nameOfSubSection) and PROFILE_END().

Hopefully there is a bottleneck somewhere within this tab's code that we can eliminate. If you post the code for the selected tab function, I can give some hints as to which subsections to create.
alright, so i split tab1 function into 4 sub-groups and i got the following time:

call tree                                  hits       self time      total time
<root>                                      1.0     35 s   205%     52 s   305%
  GUI_IP::Form1::Form1_Load                 1.0     14 s    80%     17 s   100%
    first                              1.0    174 us    0%    174 us    0%
    second                              1.0    258 us    0%    258 us    0%
    third                               1.0      3 s    20%      3 s    20%
    fourth                              1.0    256 us    0%    256 us    0%

as you can see, the third group is taking the most time.  I *think* the statement:
Title = gcnew System::Windows::Forms::Label();
input1 = gcnew System::Windows::Forms::Button();
input2 = gcnew System::Windows::Forms::CheckBox();      
input3 = gcnew System::Windows::Forms::ComboBox();

and so on until input6 are causing the most delay because of the gcnew. what would you recommend to optimize this? Thanks

				PROFILE_BEGIN(first);
				int i, Spacing;
				int TableX = 30;
				int TableY = 60;
				int TableHeight = this->tabPage1->Height - 100;
				int NumCols = 6;
				ADC_NumCols=NumCols;
				int NumRows = 47;
				ADC_NumRows=NumRows;
				int TitleX = 30;
				int TitleY = 40;
				int ColWidth[] = {150, 50, 200, 150, 100, 100};
				int RowHeight = 25;
				StringCollection^ ColumnTitles = gcnew StringCollection;
				array<String^>^myArr = {"A", "B", "C", "D", "E", "F"};
				System::Windows::Forms::Label^ Title;
				System::Windows::Forms::Button^ input1;
				System::Windows::Forms::CheckBox^ input2;
				System::Windows::Forms::ComboBox^ input3;
				System::Windows::Forms::ComboBox^ input4;
				System::Windows::Forms::ComboBox^ input5;
				System::Windows::Forms::TextBox^ input6;
				System::Windows::Forms::TableLayoutPanel^  table;
				PROFILE_END();
 
				
				PROFILE_BEGIN(second_ADC);
				ColumnTitles->AddRange( myArr );
				table = gcnew System::Windows::Forms::TableLayoutPanel();
				table->Location = System::Drawing::Point(TableX, TableY);
				table->ColumnCount = NumCols;
				table->RowCount = NumRows;
				table->AutoScroll = true;
				PROFILE_END();
 
 
 
				PROFILE_BEGIN(third_ADC);
				this->SuspendLayout();
 
				// Create column titles and format widths of table columns
				for ( i=0; i<NumCols; i++  )
				{
					Title = gcnew System::Windows::Forms::Label();
					Title->Text = ColumnTitles[i];
					Title->AutoSize = true;
					if ( i == 0 )
					{
						Title->Location = System::Drawing::Point(TitleX+10, TitleY);
						Spacing = ColWidth[i];
					}
					else
					{
						Title->Location = System::Drawing::Point(TitleX + Spacing, TitleY);
						Spacing += ColWidth[i];
					}
					table->ColumnStyles->Add(gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Absolute, (float) ColWidth[i]));
					this->tabPage1->Controls->Add(Title);
				}
				table->Size = System::Drawing::Size(Spacing + 50, TableHeight);
 
				for ( i=0; i<NumRows; i++  )
				{
					table->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Absolute, (float) RowHeight)));
 
					
					input1 = gcnew System::Windows::Forms::Button();
					if ( i < 9 )
					{
						input1->Text = "inupt_0" + (i+1).ToString();
					}
					else if ( i < 32 )
					{
						input1->Text = "inupt_" + (i+1).ToString();
					}
					else if ( i < 41 )
					{
						input1->Text = "inupt1_0" + (i-31).ToString();
					}
					else
					{
						input1->Text = "inupt1_" + (i-31).ToString();
					}
					input1->AutoSize = true;
					input1->FlatAppearance->BorderSize = 0;
					input1->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
					input1->TextAlign = System::Drawing::ContentAlignment::TopLeft;
					
					table->Controls->Add(input1, 0, i);
 
					input2 = gcnew System::Windows::Forms::CheckBox();
					input2->Checked = false;
					table->Controls->Add(input2, 1, i);
 
					input3 = gcnew System::Windows::Forms::ComboBox();
					input3->DropDownStyle = ComboBoxStyle::DropDownList;
					input3->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					input3->Items->Add("a");
					input3->Items->Add("b");
					input3->Items->Add("c");
					input3->Items->Add("d");
					input3->Items->Add("e");
					input3->Items->Add("f");
					input3->Items->Add("g");
					input3->Items->Add("h");
					input3->Items->Add("i");
					table->Controls->Add(input3, 2, i);
 
					input4 = gcnew System::Windows::Forms::ComboBox();
					input4->DropDownStyle = ComboBoxStyle::DropDownList;
					input4->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					input4->Items->AddRange( this->DefaultLayouts );
					table->Controls->Add(input4, 3, i);
 
					input5 = gcnew System::Windows::Forms::ComboBox();
					input5->DropDownStyle = ComboBoxStyle::DropDownList;
					input5->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					input5->Items->Add("1");
					input5->Items->Add("2");
					table->Controls->Add(input5, 4, i);
 
					input6 = gcnew System::Windows::Forms::TextBox();
					input6->Text = "";
					table->Controls->Add(input6, 5, i);
				}
				PROFILE_END();
 
				PROFILE_BEGIN(fourth_ADC);
				this->tabPage1->Controls->Add(table);
				Table1Index = this->tabPage1->Controls->Count-1;
				this->ResumeLayout(false);
				PROFILE_END();

Open in new window

Is there a way to preallocate all those buttons and check boxes etc.. ?
Is there a way to preallocate all those buttons and check boxes etc.. ?
Just to check that I'm following how you proceeded:

- did you remove the Shiny macro calls you were previously using? That's okay in itself, I just wondered why the GUI_IP::Form1::tab1 disappeared in the Shiny output.

- how come the Shiny output contains the label 'third', wheras in your code the section is called 'third_ADC'? (did you edit the shiny output?)

Since your third section (which takes up all the processing time) is still quite big, I would proceed by making yet more sub-sections within the third section. We should try to pinpoint the bottlenecks, we have not yet done that.

About your hypothesis that the time is spent in gcnew: that's possible, but before trying to optimize these calls you should verify your hypothesis by placing Shiny macro calls around the calls to these constructors. Again, I would not choose this option right now, but instead go for the option mentioned above: splitting the third group into smaller sections.


yea, i realized that the two were different as soon as i posted it here, sorry about the confusion. I had taken the GUI IP line out of the shiny output while posting it on here. I have tried to stay consistent with last time. So i kept the first, second and fourth call where they were. The third one is the one that i split up further. Basically i called the profiler before almost every gcnew call. And following is the output:

call tree                                  hits       self time      total time
<root>                                      1.0     15 s    72%     36 s   172%
  GUI_IP::Form1::Form1_Load                 1.0     17 s    84%     21 s   100%
    first                                   1.0    394 us    0%    394 us    0%
    second                                  1.0     49 us    0%     49 us    0%
    third                                   1.0     34 us    0%    552 us    0%
      third_a                               6.0    113 us    0%    113 us    0%
      third_b                               6.0    404 us    0%    404 us    0%
    third_c                                 1.0    725 us    0%      4 s    17%
      third_d                              47.0    825 ms    4%    825 ms    4%
      third_e                              47.0    557 ms    3%    557 ms    3%
      third_f                              47.0    560 ms    3%    560 ms    3%
      third_g                              47.0    534 ms    3%    534 ms    3%
      third_h                              47.0      1 s     5%      1 s     5%
    fourth                                  1.0    250 us    0%    250 us    0%

"third" is a wrap around third_a and third_b. third_c is a wrap around the big for loop that has the rest of the third_  within it.  So as it appears, this tab itself is taking around 6secs out of the 21 total the form_load is taking.
				PROFILE_BEGIN(first);
				int i, Spacing;
				int TableX = 30;
				int TableY = 60;
				int TableHeight = this->tabPage1->Height - 100;
				int NumCols = 6;
				ADC_NumCols=NumCols;
				int NumRows = 47;
				ADC_NumRows=NumRows;
				int TitleX = 30;
				int TitleY = 40;
				int ColWidth[] = {150, 50, 200, 150, 100, 100};
				int RowHeight = 25;
				StringCollection^ ColumnTitles = gcnew StringCollection;
				array<String^>^myArr = {"A", "B", "C", "D", "E", "F"};
				System::Windows::Forms::Label^ Title;
				System::Windows::Forms::Button^ input1;
				System::Windows::Forms::CheckBox^ input2;
				System::Windows::Forms::ComboBox^ input3;
				System::Windows::Forms::ComboBox^ input4;
				System::Windows::Forms::ComboBox^ input5;
				System::Windows::Forms::TextBox^ input6;
				System::Windows::Forms::TableLayoutPanel^  table;
				PROFILE_END();
 
				
				PROFILE_BEGIN(second);
				ColumnTitles->AddRange( myArr );
				table = gcnew System::Windows::Forms::TableLayoutPanel();
				table->Location = System::Drawing::Point(TableX, TableY);
				table->ColumnCount = NumCols;
				table->RowCount = NumRows;
				table->AutoScroll = true;
				PROFILE_END();
 
 
 
				this->SuspendLayout();
				PROFILE_BEGIN(third);
 
				// Create column titles and format widths of table columns
				for ( i=0; i<NumCols; i++  )
				{
PROFILE_BEGIN(third_a);
					Title = gcnew System::Windows::Forms::Label();
					Title->Text = ColumnTitles[i];
					Title->AutoSize = true;
					if ( i == 0 )
					{
						Title->Location = System::Drawing::Point(TitleX+10, TitleY);
						Spacing = ColWidth[i];
					}
					else
					{
						Title->Location = System::Drawing::Point(TitleX + Spacing, TitleY);
						Spacing += ColWidth[i];
					}
					PROFILE_END();
					PROFILE_BEGIN(third_b);
					table->ColumnStyles->Add(gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Absolute, (float) ColWidth[i]));
					this->tabPage1->Controls->Add(Title);
					PROFILE_END();
				}
				PROFILE_END();
				table->Size = System::Drawing::Size(Spacing + 50, TableHeight);
 
				PROFILE_BEGIN(third_c); 
				for ( i=0; i<NumRows; i++  )
				{
					PROFILE_BEGIN(third_d);
					table->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Absolute, (float) RowHeight)));
 
					
					input1 = gcnew System::Windows::Forms::Button();
					if ( i < 9 )
					{
						input1->Text = "inupt_0" + (i+1).ToString();
					}
					else if ( i < 32 )
					{
						input1->Text = "inupt_" + (i+1).ToString();
					}
					else if ( i < 41 )
					{
						input1->Text = "inupt1_0" + (i-31).ToString();
					}
					else
					{
						input1->Text = "inupt1_" + (i-31).ToString();
					}
					input1->AutoSize = true;
					input1->FlatAppearance->BorderSize = 0;
					input1->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
					input1->TextAlign = System::Drawing::ContentAlignment::TopLeft;
					
					table->Controls->Add(input1, 0, i);
					PROFILE_END();
 
					PROFILE_BEGIN(third_e);
					input2 = gcnew System::Windows::Forms::CheckBox();
					input2->Checked = false;
					table->Controls->Add(input2, 1, i);
					PROFILE_END();
 
					PROFILE_BEGIN(third_f);
					input3 = gcnew System::Windows::Forms::ComboBox();
					input3->DropDownStyle = ComboBoxStyle::DropDownList;
					input3->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					input3->Items->Add("a");
					input3->Items->Add("b");
					input3->Items->Add("c");
					input3->Items->Add("d");
					input3->Items->Add("e");
					input3->Items->Add("f");
					input3->Items->Add("g");
					input3->Items->Add("h");
					input3->Items->Add("i");
					table->Controls->Add(input3, 2, i);
					PROFILE_END();
 
					PROFILE_BEGIN(third_g); 
					input4 = gcnew System::Windows::Forms::ComboBox();
					input4->DropDownStyle = ComboBoxStyle::DropDownList;
					input4->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					input4->Items->AddRange( this->DefaultLayouts );
					table->Controls->Add(input4, 3, i);
					PROFILE_END();
 
					PROFILE_BEGIN(third_h); 
					input5 = gcnew System::Windows::Forms::ComboBox();
					input5->DropDownStyle = ComboBoxStyle::DropDownList;
					input5->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
					input5->Items->Add("1");
					input5->Items->Add("2");
					table->Controls->Add(input5, 4, i);
 
					input6 = gcnew System::Windows::Forms::TextBox();
					input6->Text = "";
					table->Controls->Add(input6, 5, i);
					PROFILE_END();
				}
				PROFILE_END();
 
				PROFILE_BEGIN(fourth);
				this->tabPage1->Controls->Add(table);
				Table1Index = this->tabPage1->Controls->Count-1;
				this->ResumeLayout(false);
				PROFILE_END();

Open in new window

Moderator: we are working on this thread. I am requesting not to close it. Thanks
I may be wrong in my hypothesis that gcnew were causing the most delay. what i did this time is wrapping ONLY the gcnew statement with the profiler call and wrapping the rest of the code seperately and this is what i noticed:
  third_d                                 1.0     43 us    0%     43 us    0%
  outofline                               1.0     10 us    0%     10 us    0%
  non_gc_d                                1.0    424 us    0%    424 us    0%

third_d is the gcnew statement, outofline is where text is getting assigned to the button that was created in third_d and non_gc_d is the statement that does not have gcnew statement that is adding control Controls->Add...
As it appears, the last one is taking the most time and not the gcnew..
                                for ( i=0; i<NumRows; i++  )
				{
 
	                                PROFILE_BEGIN(third_d);
					table->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Absolute, (float) RowHeight)));
 					PROFILE_END();
					PROFILE_BEGIN(outofline);
					input1 = gcnew System::Windows::Forms::Button();
					PROFILE_END();
					if ( i < 9 )
					{
						input1->Text = "inupt_0" + (i+1).ToString();
					}
					else if ( i < 32 )
					{
						input1->Text = "inupt_" + (i+1).ToString();
					}
					else if ( i < 41 )
					{
						input1->Text = "inupt1_0" + (i-31).ToString();
					}
					else
					{
						input1->Text = "inupt1_" + (i-31).ToString();
					}
					input1->AutoSize = true;
					input1->FlatAppearance->BorderSize = 0;
					input1->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
					input1->TextAlign = System::Drawing::ContentAlignment::TopLeft;
					PROFILE_BEGIN(non_gc_d);
					table->Controls->Add(input1, 0, i);
					PROFILE_END();
                                   }

Open in new window

Keep in mind that 1 us is only one millionth of a second.

third_h is taking one second, let's find out which calls inside this block take the most time, by replacing the third_h block by this one:


PROFILE_BEGIN(third_h);
 
PROFILE_BEGIN(third_h1);
input5 = gcnew System::Windows::Forms::ComboBox();
input5->DropDownStyle = ComboBoxStyle::DropDownList;
input5->Anchor = System::Windows::Forms::AnchorStyles::Left | System::Windows::Forms::AnchorStyles::Right | System::Windows::Forms::AnchorStyles::Top;
PROFILE_END();
 
PROFILE_BEGIN(third_h2);
input5->Items->Add("1");
input5->Items->Add("2");
PROFILE_END();
 
PROFILE_BEGIN(third_h3);
table->Controls->Add(input5, 4, i);
PROFILE_END();
 
PROFILE_BEGIN(third_h4);
input6 = gcnew System::Windows::Forms::TextBox();
input6->Text = "";
PROFILE_END();
 
PROFILE_BEGIN(third_h5);
table->Controls->Add(input6, 5, i);
PROFILE_END();
 
PROFILE_END();

Open in new window

I had to take out some of the previous profiler calls because the compiler was complaining that there are too many constructor and destructor calls in the function. The new output is:

      third_h                                47.0    981 us    0%    979 ms    6%
      third_h1                             47.0      3 ms    0%      3 ms    0%
      third_h2                             47.0    303 us    0%    303 us    0%
      third_h3                             47.0    486 ms    3%    486 ms    3%
      third_h4                             47.0      2 ms    0%      2 ms    0%
      third_h5                             47.0    487 ms    3%    487 ms    3%

If you are interested in the total time also, its:

call tree                                  hits       self time      total time
<root>                                      1.0     17 s   106%     33 s   206%
  GUI_IP::Form1::Form1_Load                 1.0     14 s    84%     16 s   100%
     first                                   1.0    445 us    0%    445 us    0%
    second                                  1.0     49 us    0%     49 us    0%
    third_d                                47.0    155 ms    1%    155 ms    1%
    outofline                              47.0    506 us    0%    506 us    0%
    non_gc_d                               47.0    475 ms    3%    475 ms    3%
    third_e                                47.0    579 us    0%    579 us    0%
    non_gc_e                               47.0    477 ms    3%    477 ms    3%
    third_f                                47.0      3 ms    0%      3 ms    0%
    non_gc_f                               47.0    526 ms    3%    526 ms    3%
    third_h                                47.0    981 us    0%    979 ms    6%
      third_h1                             47.0      3 ms    0%      3 ms    0%
      third_h2                             47.0    303 us    0%    303 us    0%
      third_h3                             47.0    486 ms    3%    486 ms    3%
      third_h4                             47.0      2 ms    0%      2 ms    0%
      third_h5                             47.0    487 ms    3%    487 ms    3%
It seems table->Controls->Add is the culprit. We can do a final analysis on how much time is spent in this function for all the tabs, using a small wrapper function around table->Controls->Add, as indicated below  (sorry, I have not .NET experience yet, this is pseudo code!).

Then, replace all direct calls to table->Controls->Add (in all the tabs) with calls to the wrapper function.
For example,

table->Controls->Add(input6, 5, i);

would be replaced with

AddControlToTable(input6, table, 5, i);

If this confirms that table->Controls->Add is taking all the time, then we need to find a way to speed it up, but let's first get the final confirmation.


// may not compile, but the idea of the wrapper should be clear...
void AddControlToTable(System::Windows::Forms::Control* control, System::Windows::Forms::TableLayoutPanel* table, int column, int row)
{
    PROFILE_FUNC(); // times how much time is taken by table->Controls->Add
    table->Controls->Add(control, column, row);
}

Open in new window

that does seem to be the case:

      third_h                                47.0      1 ms    0%    971 ms    6%
      third_h1                             47.0      3 ms    0%      3 ms    0%
      third_h2                             47.0    297 us    0%    297 us    0%
      GUI_IP::Form1::AddControlToTable     94.0    966 ms    6%    966 ms    6%
      third_h4                             47.0      1 ms    0%      1 ms    0%

What I meant was to replace table->Controls->Add with AddControlToTable everywhere (in all the code for all the tabs).
so it does seem like the Controls->Add is the culprit. Its taking 17 out of 19 secs the app takes to come up.

flat profile                               hits       self time      total time
<root>                                      1.0     19 s    98%     38 s   198%
GUI_IP::Form1::Form1_Load                   1.0      2 s    11%     19 s   100%
first                                       1.0    422 us    0%    422 us    0%
second                                      1.0     60 us    0%     60 us    0%
third_d                                    47.0    188 ms    1%    188 ms    1%
outofline                                  47.0    522 us    0%    522 us    0%
non_gc_d                                   47.0    771 us    0%    591 ms    3%
GUI_IP::Form1::AddControlToTable         1599.0     17 s    88%     17 s    88%
third_e                                    47.0    622 us    0%    622 us    0%
non_gc_e                                   47.0    320 us    0%    564 ms    3%
third_f                                    47.0      3 ms    0%      3 ms    0%
non_gc_f                                   47.0    769 us    0%    525 ms    3%
third_h                                    47.0      1 ms    0%      1 s     6%
third_h1                                   47.0      3 ms    0%      3 ms    0%
third_h2                                   47.0    246 us    0%    246 us    0%
third_h4                                   47.0      2 ms    0%      2 ms    0%

call tree                                  hits       self time      total time
<root>                                      1.0     19 s    98%     38 s   198%
  GUI_IP::Form1::Form1_Load                 1.0      2 s    11%     19 s   100%
    first                                   1.0    422 us    0%    422 us    0%
    second                                  1.0     60 us    0%     60 us    0%
    third_d                                47.0    188 ms    1%    188 ms    1%
    outofline                              47.0    522 us    0%    522 us    0%
    non_gc_d                               47.0    771 us    0%    591 ms    3%
      GUI_IP::Form1::AddControlToTable     47.0    590 ms    3%    590 ms    3%
    third_e                                47.0    622 us    0%    622 us    0%
    non_gc_e                               47.0    320 us    0%    564 ms    3%
      GUI_IP::Form1::AddControlToTable     47.0    564 ms    3%    564 ms    3%
    third_f                                47.0      3 ms    0%      3 ms    0%
    non_gc_f                               47.0    769 us    0%    525 ms    3%
      GUI_IP::Form1::AddControlToTable     47.0    524 ms    3%    524 ms    3%
    GUI_IP::Form1::AddControlToTable     1364.0     14 s    74%     14 s    74%
    third_h                                47.0      1 ms    0%      1 s     6%
      third_h1                             47.0      3 ms    0%      3 ms    0%
      third_h2                             47.0    246 us    0%    246 us    0%
      GUI_IP::Form1::AddControlToTable     94.0      1 s     6%      1 s     6%
      third_h4                             47.0      2 ms    0%      2 ms    0%
According to this link

http://www.pcreview.co.uk/forums/thread-2667009.php

a way to speed up the dynamic adding of controls is to fill the containers bottom up instead of top-down.
So instead of adding a tab to the form, and then adding controls to the tab, you should first add the controls to the tab, and then add the tab to the form.

Also, according to the same link, it helps to not resize the form after you have added all the controls (because this triggers a repaint). Instead, you should set the desired size of the form before adding the tabs to the form.

The key part of the above link are these lines:

view.PerformLayout(); <--- (1)
splitContainer.Panel2.Controls.Add(view);
splitContainer.Panel2.ResumeLayout(false); <--- (2)

(1) = perform layout on the control before it's added to parent
(2) = after added to parent, do not do any layout processing and the
redundant re-paints dont get invoked (hopefully).

I cannot check these hints on my computer (I'm on linux), but it seems worth it to try this out (although in fact I would assume the SuspendLayout would already take care of all this...)

I would first try the first hint, and see if it improves things, before trying the second hint.
from what i saw, there wasn't much change with either of these approaches, unless i am implementing them wrong. So AddControlsToLayout is the function i am calling for each table->Controls->Add(control, column, row). I changed that function to:
control->PerformLayout();
table->Controls->Add(control,column,row);
and the time for this function is still the same as before. As you had suggested, i tried to just do a perform layout first and then do add control before setting the resume layout to false, but that had no difference.
ASKER CERTIFIED SOLUTION
Avatar of maartennieber
maartennieber

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks maartennieber. I'll have to repost this with focus on optimizing the controls. I appreciate all the help in narrowing down the problem cause!
You're welcome! I hope you will find a definite solution.