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
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
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.
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.
ASKER
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);
}
ASKER
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::Co mboBox();
combobox2->DropDownStyle = ComboBoxStyle::DropDownLis t;
// 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.
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::Co
combobox2->DropDownStyle = ComboBoxStyle::DropDownLis
// 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.
ASKER
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?
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.
ASKER
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 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?
ASKER
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.
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.
ASKER
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
ASKER
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%
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%
ASKER
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
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.
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.
ASKER
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::La bel();
input1 = gcnew System::Windows::Forms::Bu tton();
input2 = gcnew System::Windows::Forms::Ch eckBox();
input3 = gcnew System::Windows::Forms::Co mboBox();
and so on until input6 are causing the most delay because of the gcnew. what would you recommend to optimize this? Thanks
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::La
input1 = gcnew System::Windows::Forms::Bu
input2 = gcnew System::Windows::Forms::Ch
input3 = gcnew System::Windows::Forms::Co
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();
ASKER
Is there a way to preallocate all those buttons and check boxes etc.. ?
ASKER
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.
- 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.
ASKER
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.
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();
ASKER
Moderator: we are working on this thread. I am requesting not to close it. Thanks
ASKER
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..
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();
}
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:
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();
ASKER
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%
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(input 6, 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.
Then, replace all direct calls to table->Controls->Add (in all the tabs) with calls to the wrapper function.
For example,
table->Controls->Add(input
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);
}
ASKER
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::AddControlT oTable 94.0 966 ms 6% 966 ms 6%
third_h4 47.0 1 ms 0% 1 ms 0%
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::AddControlT
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).
ASKER
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::AddControlT oTable 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::AddControlT oTable 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::AddControlT oTable 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::AddControlT oTable 47.0 524 ms 3% 524 ms 3%
GUI_IP::Form1::AddControlT oTable 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::AddControlT oTable 94.0 1 s 6% 1 s 6%
third_h4 47.0 2 ms 0% 2 ms 0%
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::AddControlT
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::AddControlT
third_e 47.0 622 us 0% 622 us 0%
non_gc_e 47.0 320 us 0% 564 ms 3%
GUI_IP::Form1::AddControlT
third_f 47.0 3 ms 0% 3 ms 0%
non_gc_f 47.0 769 us 0% 525 ms 3%
GUI_IP::Form1::AddControlT
GUI_IP::Form1::AddControlT
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::AddControlT
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.Cont rols.Add(v iew);
splitContainer.Panel2.Resu meLayout(f alse); <--- (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.
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.Cont
splitContainer.Panel2.Resu
(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.
ASKER
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(contr ol, column, row). I changed that function to:
control->PerformLayout();
table->Controls->Add(contr ol,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.
control->PerformLayout();
table->Controls->Add(contr
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
ASKER