Simple case but a whole lot of overhead?

Simple case but a whole lot of overhead?

Old forum URL: forums.lhotka.net/forums/t/7170.aspx


adamtuliper posted on Thursday, June 25, 2009

I'll describe a simple case and see if people think its correct:  I have a business object I want to know when its dirty and call into a DAL to update itself. In addition I'd like it to support collections.

So.. I have two classes.. much like the LineItem and LineItems samples.

public class BillingProgram : BusinessBase<BillingProgram

and

public class BillingPrograms : BusinessListBase<BillingPrograms, BillingProgram>

 

Properties are declared as such:

private static readonly PropertyInfo<int> _billingProgramIDProperty = RegisterProperty<int>(p => p.BillingProgramID);

public int BillingProgramID {get { return ReadProperty(_billingProgramIDProperty); }}

and set during loading in a similiar way:

private void Fetch(SafeDataReader dataReader)

{

LoadProperty(_billingProgramIDProperty, dataReader.GetInt32("BillingProgramID"));

MarkOld();

}

Im using my own Fetch since a parent object is passing in a data reader and DataPortal_Fetch() just takes an object criteria.. this didn't seem to fit. Id be happy to hear a different recommendation though.

Now from some top level method Im calling BillingProgram.Save();

It literally takes a full second to do a simple db update with all the behind the scenes calls to DataPortal_Update.

If I call the _exact_ same update code in another method and dont go through the portal - IE call it directly, it takes .02 seconds.

I cant imagine there is this much 'stuff' going on behind the scenes that makes would make it usable for anyone with this sort of performance tradeoff, so I must assume Im doing something improperly.

When I look at the SimpleNTier\BusinessLibrary sample though Im doing things in a similiar manner.

So can you point me to a good example where the performance wouldn't be so dismal for a simple case as I mentioned above? IE An object loads itself from the db. An object Saves itself. An object is not required to be cloned upon every save in this case. Upon save simply call into a data access layer that uses a stored proc to update a record.

Would the argument be "dont use the dataportal" then and write in our own marknew markold, etc and call our own update/delete/etc directly? If the argument is still use DataPortal, what should be 'disabled' for simple cases?

When I look at the samples, and codesmith templates I see the same type usage that would see these performance hits at least that I can tell.

//call my update directly results in a .02 second call

updateTimer.Start();

billingProgram.Update();

updateTimer.Stop();

//call using DataPortal_Update results in full one second call using the exact same method as my Update() call.

saveTimer.Start();

billingProgram.Save();

saveTimer.Stop();

 

Thoughts?

 

RockfordLhotka replied on Thursday, June 25, 2009

BusinessListBase can't contain root objects. Unless you are somehow calling MarkAsChild() when creating the BusinessBase subclass you are going to have trouble.

In current versions of CSLA .NET, you might consider using the child data portal concept to create the child objects (the BusinessBase subclass instances), because it will do the housekeeping for you.

You can avoid the child data portal if you'd like - but in that case you must ensure that you do everything the child data portal would have done - calling MarkOld() and MarkAsChild().

If you are calling DataPortal_XYZ methods on the items in the collection then you are absolutely doing something wrong. You are probably somehow calling the data portal directly from your parent object's DataPortal_Update(), which is entirely incorrect. Again, you should either do direct calls (replacing the normal behavior of BusinessListBase.Child_Update()) or you should use the child data portal, or better yet just use base.Child_Update() and let CSLA do the right thing.

 

I recommend using the data portal in all cases. If you bypass the data portal, you lock yourself into a 1- or 2-tier deployment model with no hope of ever being 3- or 4-tier. However, if that is an acceptable price to pay, you certainly could bypass the data portal and do your own thing. In such a case, your data portal replacement will need to manage the metastate of all business objects exactly as the data portal does for you today, or the rest of the framework will become problematic.

adamtuliper replied on Thursday, June 25, 2009

hmm when I call Save on my item (BusinessBase) object it looks for the dataportal_update method automatically when I step through the code.

So:

1. I have two classses here. The collection and the item. These are or are not defined correctly as per above?

I am calling save on the root invididual object individually because I have performed a LINQ operation on the BillingPrograms class to get certain BillingProgram items. Then I save each one after doing some work on it, since at that point I know I have to save it. This saves having to enumerate over the large collection again to determine what is/is not dirty. Yes it would be pretty quick to enumerate and call save on the collection, but its not necessary in this case since Im working with the object directly and transactional support is not required.

2. I just profiled the method and the majority of the time (even including database updates) occur inside of SetProperty, is there a way to speed this up or is this incorrect:

private static PropertyInfo<ProcessingStatusType> _processingStatusProperty = RegisterProperty(new PropertyInfo<ProcessingStatusType>("ProcessingStatus"));


public ProcessingStatusType ProcessingStatus
{
    get{  return ReadProperty(_processingStatusProperty);}

    set{  SetProperty(_processingStatusProperty, value);}
}

 

Thanks!

 

 

adamtuliper replied on Thursday, June 25, 2009

to be more specific in setproperty -

the profiler says 99% of the time is here:

 

[EditorBrowsable(EditorBrowsableState.Advanced)]

protected virtual void OnPropertyChanged(string propertyName)

{

if (_nonSerializableChangedHandlers != null)

_nonSerializableChangedHandlers.Invoke(this,

new PropertyChangedEventArgs(propertyName));

RockfordLhotka replied on Thursday, June 25, 2009

adamtuliper:

to be more specific in setproperty -

the profiler says 99% of the time is here:

 

protected virtual void OnPropertyChanged(string propertyName)

There's not a lot we can do about this - that's .NET data binding plumbing. Take this away and your objects will quit working with any data binding infrastructure out there...

You don't somehow have your objects bound to the UI when you are doing your save? That's one positive side-effect of the clone/serialization btw, because the clone is what's saved, it can't be accidentally left bound to the UI.

adamtuliper replied on Thursday, June 25, 2009

In this case Im doing no data binding, its a backend service.  So I have an issue then with performance.. so I dont mind taking OnPropertyChanged away - the overhead is huge. Is there an easy way to disable this, or is it a case of just create separate 'pereformance optimized' code generation templates to do something like:

set { LoadProperty(ValueProperty, value); MarkDirty(); }

oh wow. I just set

<add key="CslaAutoCloneOnUpdate" value="false"/>

on the sample code you sent and it sped up by a factor of 300.

RockfordLhotka replied on Thursday, June 25, 2009

I don’t know of any easy way to disable PropertyChanged notification. It is such an integral part of most applications this hasn’t come up.

 

I am left wondering, though, why it is so slow if you have no data binding involved. Something must be occurring when the event is raised or it would be much faster.

 

Rocky

RockfordLhotka replied on Thursday, June 25, 2009

One question I guess I have though.

 

If this is a backend service, do you really need BusinessBase? Are you using the business/validation/authorization rules infrastructure?

 

CSLA gives you three main things (and lots of smaller things):

 

1.       Mobile objects via the data portal (n-tier independence)

2.       Data binding support

3.       Business/validation/authorization rules engines

 

You can use the data portal without using the other two. What the BusinessListBase and BusinessBase classes provide are features 2 and 3, and a little integration with the data portal.

 

But if you are doing some back-office processing engine, you might be better off using a subset of CSLA, or creating your own base classes for your stereotypes.

 

If you read chapters 3-5 in the book you’ll see that I talk about stereotypes quite a lot. The CSLA base classes exist to support the existing set of stereotypes, and those stereotypes exist primarily to support the creation of interactive applications (applications with a UI).

 

If your application is primarily non-interactive batch processing, then you clearly have objects with other stereotypes, and it may make sense to consider creating your own base classes to efficiently support those stereotypes.

 

Rocky

RockfordLhotka replied on Friday, June 26, 2009

OK, there is a workable answer - at least to make the load process much faster.

If you don't need the business/validation/authorization processing provided by BusinessBase when a property is changed, you can disable it by using BypassPropertyChecks. This is protected, but you can express it as a public member by adding this to your child class

    public new IDisposable BypassPropertyChecks
    {
      get
      {
        return base.BypassPropertyChecks;
      }
    }

Then in the loop where you update every value, do this

      Console.WriteLine("Change data loop");
      start = DateTime.Now;
      foreach (var item in obj)
      {
        using (item.BypassPropertyChecks)
          item.Value = 1;
      }
      end = DateTime.Now;
      Console.WriteLine("Time elapsed {0}", end - start);

This switches the behavior of SetProperty() to be that of LoadProperty(), so nearly all the functionality of BusinessBase is disabled.

I still think that if your usage scenario/use case is some sort of batch processing, that BusinessBase may not be the correct base class, because it supports a stereotype of editing an object with the primary goal of making it easy to create a rich, interactive UI. If you have no UI, then it does a lot of things (and so does BusinessListBase) that are entirely unnecessary.

adamtuliper replied on Wednesday, July 01, 2009

thanks for the info. What recommendations might you have then rather than BusinessListBase in the case of service type applications?

tmg4340 replied on Wednesday, July 01, 2009

I think Rocky's question centers more around why you're using CSLA classes in a batch-style back-end service at all.  CSLA is primarily designed around providing objects that support user interaction in UI's.  If you don't have any user interaction with your objects, then what's in CSLA that you do need?

If there is something in CSLA that is useful to you, then I think Rocky's other comment about creating your own base classes is potentially worth some investigation.  Depending on what in CSLA you want to leverage, you may be able to implement some interfaces/inherit from some core base classes and create your own "BusinessBase".  However, doing so may be a non-trivial exercise, so you'll need to have a good understanding of the inner workings of the framework, to know (a) whether you can build your own base classes, and (b) how long that will take.  That should help to clarify a decision about whether CSLA is the right tool in this situation.

FWIW, don't let the sections that talk about utilizing CSLA as the "back end" to a web service fool you.  It's a perfectly valid technique, but there are a limited number of situations where that really works effectively, and there's a fair amount of work involved in setting that up.

HTH

- Scott

RockfordLhotka replied on Thursday, June 25, 2009

Fwiw, here's a parent-child test console app useful for basic timings. Notice that it doesn't use a database, because that introduces uncertainty into the test because you end up timing ADO.NET, the database and various other elements that have nothing to do with CSLA.

(there's enough uncertainy on a normal computer anyway - with anti-virus, Outlook, VS or other apps running you never know what's going to spike your CPU at any given time)

You can change the collection's DataPortal_Fetch() method to use either the child data portal or a direct call, but there's very little performance difference between those options. Thank's to Ricky Supit's help a while back, the child data portal uses dynamic method invocation and so is nearly as fast as normal method calling.

The foreach loop through all items is important, because it illustrates how long it takes .NET to do the iteration - it is a baseline test for comparison, and you can basically subtract this time from the other times to understand real timings for the fetch/save operations.

Also note that none of the child objects are modified, so the save operation reveals the cost of looping through all child objects checking their IsDirty/IsNew/IsDeleted values to determine that no work should be done. So the difference in time between this and the baseline loop is mostly the cost of serialization.

I picked 75,000 child items because (on my machine) that comes close to taking 2 seconds to fetch.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Loading");
      var start = DateTime.Now;
      var obj = DataPortal.Fetch<ParentList>();
      var end = DateTime.Now;
      Console.WriteLine("Time elapsed {0}", end - start);

      Console.WriteLine("Baseline loop");
      start = DateTime.Now;
      foreach (var item in obj)
      {
        int x = item.Value;
      }
      end = DateTime.Now;
      Console.WriteLine("Time elapsed {0}", end - start);

      Console.WriteLine("Saving");
      start = DateTime.Now;
      obj = obj.Save();
      end = DateTime.Now;
      Console.WriteLine("Time elapsed {0}", end - start);

     
      Console.ReadLine();
    }
  }

  [Serializable]
  public class ParentList : BusinessListBase<ParentList, ChildItem>
  {
    private void DataPortal_Fetch()
    {
      var rlce = RaiseListChangedEvents;
      RaiseListChangedEvents = false;
      for (int i = 0; i < 75000; i++)
      {
        // use child data portal
        Add(DataPortal.FetchChild<ChildItem>(i));

        // don't use child data portal
        //Add(ChildItem.GetChildItem(i));
      }
      RaiseListChangedEvents = rlce;
    }

    protected override void DataPortal_Update()
    {
      var rlce = RaiseListChangedEvents;
      RaiseListChangedEvents = false;
      base.Child_Update();
      RaiseListChangedEvents = rlce;
    }
  }

  [Serializable]
  public class ChildItem : BusinessBase<ChildItem>
  {
    private static PropertyInfo<int> ValueProperty = RegisterProperty<int>(c => c.Value);
    public int Value
    {
      get { return GetProperty(ValueProperty); }
      set { SetProperty(ValueProperty, value); }
    }

    /// <summary>
    /// Factory that doesn't use child data portal
    /// </summary>
    public static ChildItem GetChildItem(int value)
    {
      var item = new ChildItem();
      item.LoadProperty(ValueProperty, value);
      item.MarkOld();
      item.MarkAsChild();
      return item;
    }

    private void Child_Fetch(int value)
    {
      LoadProperty(ValueProperty, value);
    }

    private void Child_Insert()
    {
    }

    private void Child_Update()
    {
    }

    private void Child_DeleteSelf()
    {
    }
  }
}
 

adamtuliper replied on Thursday, June 25, 2009

The timing I did was over a bunch of records and I did both with including/excluding db call (Ie profiling over and over into the method call to reduce "other garbage time" to near zero.)

 

Your example is good.. it shows exactly what I want to ask : )

lets modify it slightly and change all the values and then save it.

Add this to the bottom of your method and it will come to a screeching halt. It took my machine 41 seconds for the save as opposed to <1 second in your cases. So whats wrong with this?

If I change the call to SetProperty to LoadProperty I get .12 seconds instead of 41 seconds. So there is a definite performance issue in doing it this way. So how can one speed this up and still know 'its dirty' since LoadProperty doesnt do that.

foreach (var item in obj)

{

item.Value += 1;

}

Console.WriteLine("Saving");

start = DateTime.Now;

obj = obj.Save();

end = DateTime.Now;

 

Console.WriteLine("Time elapsed {0}", end - start);

 

Copyright (c) Marimer LLC