Performance issue (bug?) with .NET 2 ListView

  • Thread starter Thread starter mrshrinkray
  • Start date Start date
M

mrshrinkray

Are there any know bugs with the ListView in .NET 2? I'm having
problems with an application that takes 15 seconds in 1.1, and now
takes over a minute. The code in question uses:

listViewItem = this.listView1.Items.Add(...)

followed by 7 calls to

listViewItem.SubItems.Add(...)

When I comment out this code, the application runs in the same time as
the 1.1 version.

Any suggestions? I can provide the source code if required.
 
I would suggest a: using the ListViewItem ctor that allows you to specify
the sub-items in one go, and b: using the {Begin|End}Update syntax (around a
block of such) - i.e.

listview.BeginUpdate();
try {
ListViewItem newItem = new ListViewItem(new string[] {
"text1","text2","text3","text4"});
listview.Items.Add(newItem);
// and again...
// and again...
// and again...
// (or use AddRange)
} finally {
listview.EndUpdate();
}

See if that works any quicker... you could also try {Suspend|Resume}Layout.

Marc
 
What's happening in .Add(...)?
Please post a complete sample that illustraties the issue, without seeing
any code it's hard to tell what's happening.

Willy.

| Are there any know bugs with the ListView in .NET 2? I'm having
| problems with an application that takes 15 seconds in 1.1, and now
| takes over a minute. The code in question uses:
|
| listViewItem = this.listView1.Items.Add(...)
|
| followed by 7 calls to
|
| listViewItem.SubItems.Add(...)
|
| When I comment out this code, the application runs in the same time as
| the 1.1 version.
|
| Any suggestions? I can provide the source code if required.
|
 
It's over 6000 items I'm adding, here's a snippet:

ListViewItem listViewItem =
this.listView1.Items.Add(e.RequestInfo.IP);
listViewItem.SubItems.Add(e.RequestInfo.DateAndTime.ToString("U"));
listViewItem.SubItems.Add(e.RequestInfo.TimeDifference+ "");
listViewItem.SubItems.Add(e.RequestInfo.Request);
listViewItem.SubItems.Add(e.RequestInfo.Status+" ");
listViewItem.SubItems.Add(e.RequestInfo.Bytes+ "");
listViewItem.SubItems.Add(e.RequestInfo.Referer);
listViewItem.SubItems.Add(e.RequestInfo.Browser);

which is taken from a small project I'm writing, full source is at:

http://www.gotdotnet.com/Workspaces/Workspace.aspx?id=7c44d6aa-770e-45d4-a391-dbe878a161b7
 
Items.AddRange() will be your friend.

If you use .NET2.0 the VirtualMode of the listview class could also be a
great idea.
 
I also tried changing

// ListViewItem listViewItem =
this.listView1.Items.Add(e.RequestInfo.IP);
ListViewItem listViewItem = new ListViewItem(e.RequestInfo.IP);
this.listView1.Items.Add(listViewItem);

and also used BeginUpdate,EndUpdate but with no improvement
 
I should've posted the whole method, here it is:

private void OnLineMatched(object sender,LogReaderLineEventArgs e)
{
if ( this.InvokeRequired )
{
OnLineMatchedDelegate d1 = new
OnLineMatchedDelegate(OnLineMatched);
this.Invoke(d1, new object[] { sender, e });
}
else
{
// ListViewItem listViewItem =
this.listView1.Items.Add(e.RequestInfo.IP);
ListViewItem listViewItem = new ListViewItem(e.RequestInfo.IP);
listViewItem.SubItems.Add(e.RequestInfo.DateAndTime.ToString("U"));
listViewItem.SubItems.Add(e.RequestInfo.TimeDifference + "");
listViewItem.SubItems.Add(e.RequestInfo.Request);
listViewItem.SubItems.Add(e.RequestInfo.Status + " ");
listViewItem.SubItems.Add(e.RequestInfo.Bytes + "");
listViewItem.SubItems.Add(e.RequestInfo.Referer);
listViewItem.SubItems.Add(e.RequestInfo.Browser);
this.listView1.Items.Add(listViewItem);
this.listView1.EndUpdate();

this.statusBar1.Panels[1].Text = e.CurrentLine + "/" +
e.TotalLines;
this.progressBar1.PerformStep();
}
}

public class LogReaderLineEventArgs : EventArgs
{
...
public ServerRequestInfo RequestInfo
{
get
{
return this.serverRequestDetails;
}
}
}

public struct ServerRequestInfo
{
public DateTime DateAndTime;
public string IP;
public string Request;
public int Status;
public string Browser;
public string Referer;
public string Method;
public int TimeDifference;
public int Bytes;
}
 
The fact it's working in 15 seconds in 1.1 and 1 minute in .NET 2 seems
to point to something else I feel
 
Just a thought:

Do you have any try catches that might be swallowing an exception?
Perhaps an exception is being generated and swallowed.
 
Chris said:
Just a thought:

Do you have any try catches that might be swallowing an exception?
Perhaps an exception is being generated and swallowed.

Spot on! For some reason the following code:

private int ConvertToInt(string input)
{
if ( input != null && input != "" )
{
try
{
int retvalue = Convert.ToInt32(input);
return retvalue;
}
catch
{
return 0;
}
}
else
{
return 0;
}
}

was slowing down the application hugely ("A first chance exception of
System.FormatException was caught") in .NET 2.0 but not 1.1. This might
be the debugger, I didn't check. Anyway as I'm using 2.0 I just changed
the above method to:

private int ConvertToInt(string input)
{
if ( input != null && input != "" )
{
int result = 0;
int.TryParse(input, out result);
return result;
}
else
{
return 0;
}
}
 
Just a quick note - for performance and also readability reasons, you should
use string.IsNullOrEmpty() instead of checking for null and string.Empty
equality.
 
Most likely this was the debugger, then; exceptions are actually damned
fast - but the IDE makes 'em look slow.

You may also be able to simplify this to:

private int ConvertToInt32(string input) {
int result;
int.TryParse(input, out result); // discard returned bool
return result;
}

(I also hacked the name just to be pedantic with the CLR guidelines - but
since this method is private it is purely academic)

Marc
 
Lebesgue,

While I agree that IsNullOrEmpty does improve readability, I fail to see
how it is an improvement to performance. The code is pretty much the same
in the IsNullOrEmpty method and what is being done here.
 
Nicholas,

While I understand that == call is delegated to Equals, which checks for
string length equality in the first place, which has basically the same
effect as IsNullOrEmpty call, according to FxCop rules, it yields execution
of significantly more MSIL instructions, thus should be avoided to achieve
the best peformance [1].

I believe the difference would be very subtle - but there certainly is some,
when it's listed as FxCop rule.

[1] FxCop Documentation 1.312.0:
http://www.gotdotnet.com/team/fxcop...nce/TestForEmptyStringsUsingStringLength.html


Nicholas Paldino said:
Lebesgue,

While I agree that IsNullOrEmpty does improve readability, I fail to
see how it is an improvement to performance. The code is pretty much the
same in the IsNullOrEmpty method and what is being done here.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Lebesgue said:
Just a quick note - for performance and also readability reasons, you
should use string.IsNullOrEmpty() instead of checking for null and
string.Empty equality.
 
Ok, I see what you are saying, yes, it will delegate to Equals, but
ultimately, the first check in the Equals overload is against the length.

It might be a few more IL statements, but I don't know how much exactly.

In the end, it's a moot point, since the readability aspect definitely
makes it more attractive.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Lebesgue said:
Nicholas,

While I understand that == call is delegated to Equals, which checks
for string length equality in the first place, which has basically the
same effect as IsNullOrEmpty call, according to FxCop rules, it yields
execution of significantly more MSIL instructions, thus should be avoided
to achieve the best peformance [1].

I believe the difference would be very subtle - but there certainly is
some, when it's listed as FxCop rule.

[1] FxCop Documentation 1.312.0:
http://www.gotdotnet.com/team/fxcop...nce/TestForEmptyStringsUsingStringLength.html


Nicholas Paldino said:
Lebesgue,

While I agree that IsNullOrEmpty does improve readability, I fail to
see how it is an improvement to performance. The code is pretty much the
same in the IsNullOrEmpty method and what is being done here.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Lebesgue said:
Just a quick note - for performance and also readability reasons, you
should use string.IsNullOrEmpty() instead of checking for null and
string.Empty equality.

Chris Dunaway wrote:
Just a thought:

Do you have any try catches that might be swallowing an exception?
Perhaps an exception is being generated and swallowed.

Spot on! For some reason the following code:

private int ConvertToInt(string input)
{
if ( input != null && input != "" )
{
try
{
int retvalue = Convert.ToInt32(input);
return retvalue;
}
catch
{
return 0;
}
}
else
{
return 0;
}
}

was slowing down the application hugely ("A first chance exception of
System.FormatException was caught") in .NET 2.0 but not 1.1. This might
be the debugger, I didn't check. Anyway as I'm using 2.0 I just changed
the above method to:

private int ConvertToInt(string input)
{
if ( input != null && input != "" )
{
int result = 0;
int.TryParse(input, out result);
return result;
}
else
{
return 0;
}
}
 
So can anyone shed light here on why the ConvertToInt was taking .NET
2.0 so much longer to run than 1.1? Is it the changes to the VS.NET
debugger? Or something in the framework?
 
Well, the saga continues. The problem was being veiled by the
ConvertToInt inside the business logic part of the code. I have now
added:


Application.EnableVisualStyles();

inside Main. This is actually the source of my problem. Remove it and
the application runs the same speed as the .NET 1.1. Include it and it
runs 4x as slow.



Source code is at:
http://www.google.com/url?sa=D&q=ht....aspx?id=7c44d6aa-770e-45d4-a391-dbe878a161b7

If anyone can spare 5 mins to try running it on .NET 1.1 and .NET 2.0,
and add Application.EnableVisualStyles(); to Main() and see if they get
the same speed difference. Use testlog.log as the example log.
 
It's over 6000 items I'm adding, here's a snippet:

If you are adding 6000 items I really recommend that you use AddRange.
It is significantly (several times) faster than Add when dealing with
large numbers of items. I don't usually use SubItems.Add, but instead
use the constructor that takes a string array.

Here is a some code (slightly modified for simplicity) from one of my
applications that has a ListView with about 10000 items. It takes
about a second to fill it.


private void FillListView()
{
listView.BeginUpdate();
listView.Items.Clear();

//We add to a List(Array) first because AddRange is a lot faster
//than Add when dealing with lots of elements in ListView
//Also, we don't seem to have to turn off the sorter when adding
//all the elements at once

List<ListViewItem> items = new List<ListViewItem>();
foreach (Episode episode in episodes)
items.Add(CreateEpisodeListViewItem(episode));

listView.Items.AddRange(items.ToArray());

listView.EndUpdate();
}

private static ListViewItem CreateEpisodeListViewItem(Episode
episode)
{
ListViewItem item = new ListViewItem(
new string[]
{
episode.OriginalAirdate.HasValue?
episode.OriginalAirdate.Value.ToShortDateString():"",
episode.Show.Name,
episode.Season.ToString(),
episode.EpisodeNumber.ToString(),
episode.Title
});
item.Tag = episode;
return item;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top