[FIXED] Remoting performance?

Posts   
 
    
ctadlock avatar
ctadlock
User
Posts: 60
Joined: 12-Feb-2004
# Posted on: 03-Jun-2004 02:01:36   

Ive got a remoting server (IIS 6, binary) which is returning about ~750 entities for a given method call. Ive found that 14 of the 16 total seconds is spent serializing/deserializing the data. Has anyone else come across this issue? My entities aren't unusually large. Any recomendations to improve the performance?

Thanks CT

UPDATE...

Its serializing 11 MB of data. That seems like a lot for 750 entities, somethings got to be wrong somewhere....

Note, 10,000 serialized Point objects come out to 216KB of data.

BlackMamba avatar
BlackMamba
User
Posts: 34
Joined: 30-Apr-2004
# Posted on: 03-Jun-2004 03:41:47   

ctadlock wrote:

Ive got a remoting server (IIS 6, binary) which is returning about ~750 entities for a given method call. Ive found that 14 of the 16 total seconds is spent serializing/deserializing the data. Has anyone else come across this issue? My entities aren't unusually large. Any recomendations to improve the performance?

Thanks CT

UPDATE...

Its serializing 11 MB of data. That seems like a lot for 750 entities, somethings got to be wrong somewhere....

Note, 10,000 serialized Point objects come out to 216KB of data.

I read an article just today about inter process communication in .NET applications. The guy who wrote the article chose a named pipes solution to communicate between two .net applications instead of remoting because from his tests (on a LAN) the remoting solution was 6-7 times slower. Apparently this was caused by slow serialization in binary format and his solution using named pipes and xml serialization instead worked much faster, especially when serializing small (as in not too much data to serialize) objects, like entities tend to be I think. I'm not a remoting expert so I can't be much more of help, just wanted to point out that the performance problem most probably relies in the binary serialization which probably adds a lot of data on top of the 'entity data' during the serialization, and since your objects are small and lots of them this ends up in 1) big file and 2) slow performance.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 03-Jun-2004 08:50:42   

Are you sure the entities aren't populated with related objects as well?

IIS with remoting has its advantages but it's slow(er) as well. When you try TCP/IP remoting, does it speed up the serialization? 11MB binary serialized data is a lot. To see how much it is: an llblgen pro poject is also binary serialized with all catalog information as well. If that's 2MB or less in your case, it still might be a large project. So I wonder if the entities aren't populated with related objects as well....

(to test that, simply pull solely entities into an entitycollection and send that collection directly over the wire)

btw, binary serialization is very fast, as it simply dumps the memory block of a class into the stream.

Frans Bouma | Lead developer LLBLGen Pro
Fabrice
User
Posts: 180
Joined: 25-May-2004
# Posted on: 03-Jun-2004 10:06:36   

In generated code, collections are created for each relations 1-n/m-n Collections are always instanciated, it'll maybe be more efficient to instanciate it only on the fist access, because object with lot of relations are bigger even when all these relations are unfetched.


protected virtual void InitClassMembers()
{
    _contractElements = new EntityCollection(new ContractEntityFactory());
    ...
    _timetableElements = new EntityCollection(new TimetableEntityFactory());
}

...

public virtual EntityCollection ContractElements
{
    get
    {
        return _contractElements;
    }
}

InitClassMembers is called from each ctor (via InitClassEmpty) It's nice when there is not a lot of relations

why not create the collection on the 1st access :


public virtual EntityCollection ContractElements
{
    get
    {
        if (_contractElements == null)
            _contractElements = new EntityCollection(new ContractEntityFactory());
        return _contractElements;
    }
}

It's just an idea of course, I'm quite new to llblgen so I can miss something.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 03-Jun-2004 11:16:40   

Fabrice wrote:

In generated code, collections are created for each relations 1-n/m-n Collections are always instanciated, it'll maybe be more efficient to instanciate it only on the fist access, because object with lot of relations are bigger even when all these relations are unfetched.


protected virtual void InitClassMembers()
{
    _contractElements = new EntityCollection(new ContractEntityFactory());
    ...
    _timetableElements = new EntityCollection(new TimetableEntityFactory());
}

...

public virtual EntityCollection ContractElements
{
    get
    {
        return _contractElements;
    }
}

These collection objects are empty, so have a very small footprint, as they're build upon ArrayList (collection base, which includes an arraylist). Furthermore, with serialization, the class isn't serialized, the data is. Empty objects are not containing a lot of data, so serialization of these should be very fast.

InitClassMembers is called from each ctor (via InitClassEmpty) It's nice when there is not a lot of relations why not create the collection on the 1st access :


public virtual EntityCollection ContractElements
{
    get
    {
        if (_contractElements == null)
            _contractElements = new EntityCollection(new ContractEntityFactory());
        return _contractElements;
    }
}

It's just an idea of course, I'm quite new to llblgen so I can miss something.

It's an idea, however the codebase is currently thouroughly tested with teh current setup. If I change this, I have to test all code from the beginning again, as the internal code doesn't go through the properties but refers to the members and assumes the collections are present after instantiation (for example the syncing setup code). This can lead to problems along the way.

(I've removed your empty posting simple_smile )

Frans Bouma | Lead developer LLBLGen Pro
ctadlock avatar
ctadlock
User
Posts: 60
Joined: 12-Feb-2004
# Posted on: 03-Jun-2004 19:38:06   

Well this is what I found...

My method is returning 250 "Location" objects, each which have an "Organization" object. The "Organization" object is a centrally related object within my system, so without hiding any of the relationships (new beta), about 100 relationship collections are generated. I found that just serializing 1 "Organization" object takes 43KB, while 1 "Location" object takes 8KB. So 250 * 8KB + 250 * 43KB = 12,750KB. I can send the serialized data if anyone is interested. As for improving this, there are several fronts to attack this. If possible something should be done in the base classes to ensure only the needed data is serialized. Also, I might be able to change my architecture such that all of the "location" objects point to the same "Organization" in memory, instead of a copy. Something else Im going to look into is adding a compression layer to the remoting. The data is highly compressable due to its nature. Check out the link bellow for more info on compression.

So any ideas on how to improve the base classes or any other smart idea?

http://www.genuinechannels.com/

Thanks CT

ctadlock avatar
ctadlock
User
Posts: 60
Joined: 12-Feb-2004
# Posted on: 03-Jun-2004 19:39:41   

Here's the serialized data for 1 "Organization" object...

(I've the data copied locally in a file here, as it messes up the forum completely. -- Otis.)

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 03-Jun-2004 20:48:39   

ctadlock wrote:

Well this is what I found... My method is returning 250 "Location" objects, each which have an "Organization" object. The "Organization" object is a centrally related object within my system, so without hiding any of the relationships (new beta), about 100 relationship collections are generated.

This shouldn't be a problem, as serialization serializes data, and these are empty. (but it is a lot...). If you have the time, could you test if with the beta, (make precautions that you work with a project copy!) the data is much less when you hide relations?

I found that just serializing 1 "Organization" object takes 43KB, while 1 "Location" object takes 8KB. So 250 * 8KB + 250 * 43KB = 12,750KB. I can send the serialized data if anyone is interested. As for improving this, there are several fronts to attack this. If possible something should be done in the base classes to ensure only the needed data is serialized.

This can't be controlled, as every member should be serializable, so every member is 'needed'. (in theory)

Also, I might be able to change my architecture such that all of the "location" objects point to the same "Organization" in memory, instead of a copy. Something else Im going to look into is adding a compression layer to the remoting. The data is highly compressable due to its nature. Check out the link bellow for more info on compression.

Well, instead of returning a collection of Location objects which all have the same Organization object, you could also return a single Organization object, which has a collection 'Locations' with all the locations you want to return, saves you 249*45 = 11205 KB simple_smile

Frans Bouma | Lead developer LLBLGen Pro
ctadlock avatar
ctadlock
User
Posts: 60
Joined: 12-Feb-2004
# Posted on: 03-Jun-2004 21:14:47   

Ive been playing with chaning the entityAdapter.template to lazy create the relation fields and have the class access the relation property instead of the field when it makes sense. The old "Organization" object was 43KB, it's now down to 5KB. I still have some work to do because I use the extended adapter config, but it shouldnt be a problem. I can send anyone the template file if they want. Would there be a reason to not include this change into the official version if it proves out to be bug free?

Yes, the data is much less if you hide relations.

Thanks CT

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 03-Jun-2004 21:24:10   

ctadlock wrote:

Ive been playing with chaning the entityAdapter.template to lazy create the relation fields and have the class access the relation property instead of the field when it makes sense. The old "Organization" object was 43KB, it's now down to 5KB. I still have some work to do because I use the extended adapter config, but it shouldnt be a problem. I can send anyone the template file if they want. Would there be a reason to not include this change into the official version if it proves out to be bug free?

You mean, create the collection object on demand? This will lead to problems in some scenarios (as in: a lot of tests (if(_col == null) { // create it} are required).

You did try the other way around as I suggested? (which saves you 11MB of data)

Frans Bouma | Lead developer LLBLGen Pro
ctadlock avatar
ctadlock
User
Posts: 60
Joined: 12-Feb-2004
# Posted on: 03-Jun-2004 22:43:23   

Yes, Im talking about creating the collections on demand. I dont think performance would be an issue, a null reference check is a fast operation. In fact, this concept is used extensivly throughtout the .NET framework. The code bellow is the implementation of the DataSet.Tables property. What other problems could this cause?

Im sure that returning parent Organization object would be faster, but its not guarenteed that every Location in the collection has the same Organization (example method signature bellow). Also, there could be other objects that I would want to return in this set, so I wouldnt know which was the best 'parent' object to return.

// return all the location matching the keyword that belong to one of the organizations; also returns the Location's addresses, phone, people, events EntityCollection SearchLocations(int[]organizationIDs, string locationNameKeyword);

public DataTableCollection get_Tables() { if (this.tableCollection == null) { this.tableCollection = new DataTableCollection(this); } return this.tableCollection; }

Thanks CT

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 03-Jun-2004 23:53:26   

I'll see what I can do in the beta templates to make them create the collections on demand.

Frans Bouma | Lead developer LLBLGen Pro
ctadlock avatar
ctadlock
User
Posts: 60
Joined: 12-Feb-2004
# Posted on: 04-Jun-2004 00:05:27   

Thanks, once again the best application support!

Let me know if you want the templates I built for this.

CT

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 04-Jun-2004 08:21:40   

ctadlock wrote:

Thanks, once again the best application support!

Let me know if you want the templates I built for this.

CT

I'll try to go through the property internally, and build the creation code and test into the property. This way the code will be as clean as possible.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 04-Jun-2004 14:00:44   

I will not make the change in selfservicing templates, as this will require too much work (the property descriptor factory passed into the constructor of the entity is passed to the collection constructor as well. It will then require to store this internally and this is too messy, plus the selfservicing code can't use the property, it has thus call a test method before accessing the private member, which is IMHO crappy code -> not done).

I'll try to make a change to the adapter templates. However it this too is resulting in spagetti I'll also cut it for adapter, however it seems to look good, as I can put the test in the property for teh collection and all code in the entity will just have to use the property.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 04-Jun-2004 14:32:43   

Ok, I've added this to the adapter beta templates in the GUI beta. These templates will be available with beta 5, which will likely be released tomorrow or later this day.

In short, it initializes collection members to null/Nothing, it will refer directly to these members in serialization/deserialization code (otherwise it wouldn't have any effect wink ) so null/Nothing will be serialized if the collection is empty. All code in the entity refering to the member itself is now using the property (which contains just a getter). The getter in the property checks for null, if the member is null, it creates the collection with the right factory and sets the properties of the collection to the right values as it was previously done in the InitClassMembers() routine.

Frans Bouma | Lead developer LLBLGen Pro