Slow Win Form Data Binding

Posts   
 
    
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 03-Sep-2009 15:19:06   

I'm using a LlblGen runtime version 2.6.9.703, Developer's Express XtraGrid v9.24 in a windows form application. My entity has a lot of fields, around 200.

I retrieve a entity collection then bind it to a binding source. The grid is configured to use the binding source and has the columns predefined and the grid is not used to change, add or delete the data. My screen would become non-responsive for a long time, then the data would appear in the grid. For example, if the entity collection has 12,000 entities, the freeze lasts more than** 4 minutes**. Note the entity collection is being retrieved quickly from the database, the freeze happens at the data binding step.

To try to find out if this was due to entities and entity collections or the grid, I created a simple DTO that corresponds to the entity and a BindingList to hold them. The DTO has a auto-property for each data field in the entity. I then use automapper to transfer the entities in the collection to DTOs in the binding list. I then set the binding list to be the datasource of the binding source.

In my example with 12,000 entities it then takes 8-17 seconds to transfer the entities into the DTOs and 1 or 2 seconds to bind, for a total of 8 to 20 seconds of non-responsiveness. I've tested this many times with entity collections holding from 200 to 12,000 items. The freeze length corresponds pretty well to the number of entities in the collection.

So binding using an entity collection directly takes 10 times as long as transferring all the entity data into another container/list and binding to it! confused

What can be done to improve the binding performance of the entity collection/ entity combination? I would really hate to have to set up the DTO solution when I want to get acceptable performance.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 03-Sep-2009 20:30:28   

Hi Al,

  • Are you using Adapter or SelfServicing?

  • Did you try with a .net Grid to see whether the slowness is on the XtraGrid?

  • How are you configuring the databing? Are you using BindingContext or just attaching the collection into the grid?

  • Does the collection contain prefetched elements?

David Elizondo | LLBLGen Support Team
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 03-Sep-2009 20:54:37   

1) Adapter 2) No, the .net grid doesn't have the features I need. I'll see if I can test with the standard grid, but won't be able to use it even if it is fast. 2) Yes, but the dataGridView doesn't have the features I need. In my test with the standard grid, binding speed is very close (and quite fast) no matter if I use the EntityCollection<T> or the binding list. 3) Binding context I guess, though it's called BindingSource in my system (System.Windows.Forms.BindingSource). 4) No, not in this case, but I do often use them in other cases. The fetching speed is ok and has been completed by the time I begin my timing.

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 03-Sep-2009 22:43:10   

I'll raise a support issue with developer's express to see what they have to say, since the DataGridView is fast with both collections.

In the mean time how can I trace or log what the grid is asking the EntityCollection<T> to do that takes it so long?

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 03-Sep-2009 23:11:06   

I've opened a issue at Developer's Express http://www.devexpress.com/Support/Center/p/B139758.aspx

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39861
Joined: 17-Aug-2003
# Posted on: 04-Sep-2009 11:01:52   

arschr wrote:

I'll raise a support issue with developer's express to see what they have to say, since the DataGridView is fast with both collections.

In the mean time how can I trace or log what the grid is asking the EntityCollection<T> to do that takes it so long?

Binding is done through the EntityView2, so a collection returns its DefaultView instance as it implements IListSource. EntityView2 implements ITypedList, which should be consulted once by the bound control. ITypedList builds the list of property descriptors to use for the columns in the grid.

As you said the normal .net gridview binds very fast to the entity collection (as in, close to the dto stuff), it's not likely it's the code in the entityview2 as the datagridview calls the same code: IListSource.GetList(), which returns the DefaultView instance (which is an EntityView2 instance with the collection GetList is called on), that instance's ITypedList.GetItemProperties() is called, which produces the property descriptors and returns them to the caller. Then the grid builds the column with these descriptors. It will then fetch the data through the view by iterating over it.

If devexpess needs info from us regarding internals, just post in this thread and we'll let them/you know.

In the meantime, you could try profiling the application you wrote, and especially this form, it would give great insights in what it might be. - change your code a bit so that it won't fetch 12000 rows but say 500. Profiling is slow, so 12000 rows will likely take ages and it's not really necessary to fetch all of them to spot a hotspot. - If you don't have a .net profiler installed, please go to www.jetbrains.com and download the dottrace trial and install it. - Run the profiler (dottrace or e.g. ants.net) and order it to start your application but NOT to start profiling (so you can switch on profiling right before when you fetch the rows). Also make sure you don't filter out calls to the ORMSupportClasses dll. Eventually, use the debug build ormsupportclasses dll in the runtime lib folder. - your application is now running and the profiler is too, but profiling hasn't started yet. You go in your application to the form which fetches the rows and which is very slow. Right before opening it (or clicking the button which triggers the fetch + binding, i.e. which is slow), on the profiler switch ON profiling. This makes the profiler record what's going on. Open the form and bind the data. This will take some time. - after the data is bound, order the profiler to get a snapshot. Don't close the profiler yet, get a snapshot first. - order the profiler to quit the application. - the profiler now has a snapshot loaded which contains all activity between when you started profiling and when you stopped. You can now browse the tree of calls to see where most of the time was spent. This is valuable information for both devexpress (if it's in their code for example) or us (if it's in our code).

Good luck. If you need info about profiling etc. let me know as well simple_smile

Frans Bouma | Lead developer LLBLGen Pro
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 05-Sep-2009 16:29:12   

I've some tentative results, of DotTrace profiling comparing DataGridView to XtraGrid.

Method w DGV w XtraGrid EntittFields2.get_Item() 75 649,083 EntityViewBase<>.get_Count() 289 1,324,387 EntityViewBase<>.GetEntityAtIndex() 186 661,329 EntityFields2.get_State 150 1,298,166

These numbers are crazy. DGV must be just reading what it needs for the first page, while XtraGrid is reading whole collection?

I'm not comparing apples to apples Xtragrid is displaying grand totals, I don't know if dgv supports that?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39861
Joined: 17-Aug-2003
# Posted on: 05-Sep-2009 16:45:38   

arschr wrote:

I've some tentative results, of DotTrace profiling comparing DataGridView to XtraGrid.

Method w DGV w XtraGrid EntittFields2.get_Item() 75 649,083 EntityViewBase<>.get_Count() 289 1,324,387 EntityViewBase<>.GetEntityAtIndex() 186 661,329 EntityFields2.get_State 150 1,298,166

These numbers are crazy. DGV must be just reading what it needs for the first page, while XtraGrid is reading whole collection?

I'm not comparing apples to apples Xtragrid is displaying grand totals, I don't know if dgv supports that?

I can imagine if you want grand totals, it has to enumerate all 12K rows, and DGV doesn't do that obviously. What's odd is why it reads the Count() 1.3 million times, that shouldn't be done that much, just once. Also the GetEntityAtIndex() suggests it isn't enumerating, but doing index based lookups. The Count getter might suggest a call to Find (I don't think this is the case) or Sort, which might be the case, but why it applies sorting that many times is beyond me (could you check what the paths are to the get_Count() call? Also the get_State call, what are the paths (direct calling methods) to that method ?

I'd also think that the get_Item is the slowest, where 'slow' is subjective, as it's a direct index fetch from the collection so it shouldn't be that slow.

How many rows did you test with? 12,000? simple_smile It's for these things better to run with a smaller number, then you can still see the hotspots. With a smaller number of rows (e.g. 500) the trace log is also much smaller, so if you can get it below 5MB or so (zipped) you can mail it to support AT llblgen DOT com so I can have a look at it as well. (just the xtragrid one)

Frans Bouma | Lead developer LLBLGen Pro
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 05-Sep-2009 17:18:54   

Sent trace. I think I mentioned I using a windows forms BindingSource. I also do have a default sort specified in the XtraGrid.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39861
Joined: 17-Aug-2003
# Posted on: 05-Sep-2009 17:39:26   

arschr wrote:

Sent trace. I think I mentioned I using a windows forms BindingSource. I also do have a default sort specified in the XtraGrid.

Got it.

The timings are indeed insane smile . Ignoring those, looking at the get_Count() call, I see it's called 324,459 times through a call chain originating from the CalcSummaryValue inside the XtraGrid (which is called 27 times). So that method calls 324,459 times a method which ultimately reads Count 324,459 times on the datasource... that's a bug for DevExpress to fix. That CalcSummaryValue method calls Count multiple times it seems: first through GetListSourceRowIndex (called 324,459 times), and then through GetRowValue (also called 324,459 times, as it calls GetRow on DevExpress' BaseListDataControllerHelper, which also calls Count again. Count is a sequential calculation (O(n)) so it's likely that this caused the problem.

The get_State() is now solved, that's called by GetValue to check if the entity is valid for value retrieval, so that's not a problem. (and it's 1 getter call, so not a bottleneck).

I think devexpress also would like to see this trace, if they can bare the trace output from a competing company wink )

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 07-Sep-2009 09:22:53   

You could try wrapping the code where you set the BindingSource's DataSource with BaseView.BeginDataUpdate() and BaseView.EndDataUpdate(). e.g.

gridView1.BeginDataUpdate();
try {
    bindingSource1.DataSource=data;
}
finally {
gridView1.EndDataUpdate();
}

Cheers Simon

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39861
Joined: 17-Aug-2003
# Posted on: 08-Sep-2009 10:00:48   

From the looks of the devexpress reply, they didn't even read this thread.

Frans Bouma | Lead developer LLBLGen Pro