-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 41 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Lightweight Code Generation in NHibernate 1.0.2.0 (.NET 2.0)
PostPosted: Sat Mar 18, 2006 12:29 am 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
[From the developer mailing list...]

All,

I have completed work on a lightweight code generation (LCG) version of
NHibernate which removed the need to use reflection at runtime.
Reflection is only used during startup time to help compile the
generated IL code. There is no temporary assembly with this
approach. Instead, it is using the new DynamicMethod class in .NET
2.0.

IL opcodes are emitted at runtime in dynamically created methods.
These methods have direct access to the member data of a class,
without concern for private vs. public accessibility on those
fields. It is as if these methods were hand written to get/set data
to/from the class.

I have included a link to the source tree for a modified NHibernate 1.0.2.0
codebase. I am using VS 2005, so the project has been converted. The
important information to get out of this source tree is the changes
to the followings files:

\Cfg\Environment.cs - in particular, the new
UseLightweightCodeGeneration property.
\Persister\GetSetHelperFactory.cs - added ability to use the LCG
version of the GetSetHelper.
\Persister\LCGSetSetHelper.cs - where all the magic occurs
\Property\IGetter.cs - added two members, CanEmit and Emit
\Property\ISetter.cs - added two members, CanEmit and Emit
(...there may be others, but these are the big ones...)

The biggest change that you may notice is the change the IGetter and
ISetter interfaces. This change allows a getter and setter
implementation to contribute the generation of IL code to get/set a
value. If an implementation chooses not to support IL generation
(i.e. CanEmit returns false), then the generater IL code will just
call into the getter/setter at runtime, just like before. In that
scenario, you have a mixed use of generated IL and reflection
(depending on how your getter/setter works).

Finally, I have also added support for implicit/explicit operator
overloads. If during IL generation, we can detect that a given type
supports an implicit/explicit cast to/from the database data type,
then the generated IL code will use those operator overloads.

In my testing, the use of lightweight code generated methods greatly
improves the performance of NHibernate. Of course, debugging LCG is
a bear!

In order to turn on this new functionality, you need to turn on both
the UseReflectionOptimizer flag and the UseLightweightCodeGeneration
flags.

I hope you find this new functionality useful. Let me know if you
have any questions. My hope is that this functionality will be integrated with the main NHibernate codebase. I am interested in hearing about anyone's trial of this new functionality.

Here is the link:
http://nhibernate.sourceforge.net/contr ... ernate.zip

Enjoy!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 21, 2006 2:22 pm 
Newbie

Joined: Tue Mar 21, 2006 2:20 pm
Posts: 2
Location: T.O.
smudges,

Did you get any response?

Did anyone try this?

Seems like very nice utilization of 2.0 features!

TiA,
E.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 21, 2006 5:43 pm 
Newbie

Joined: Tue Mar 21, 2006 5:07 pm
Posts: 1
Location: Ukraine
I turn on LCG and achieve 2-3x gain in performance. It's very usefull feature!!! So i'm waiting for integration in main codebase :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 10:59 am 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
There has been some discussion on the development mailing list, but nothing that lead to the integration of the code changes in the mainline. These features are strictly .NET 2.0, so integration in NHibernate 1.x is unlikely because a lot of users are still on .NET 1.1.

In order to get this feature into NHibernate today, someone needs to change the IGetSetHelper mechanism to allow for specifing an implementation that lives outside of the NHibernate core library. Something like the contrib libraries.

Sergey, what are your thoughts on this idea?

John


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 12:57 pm 
Newbie

Joined: Wed Mar 22, 2006 12:52 pm
Posts: 5
I tried this code and timed it. It results in a 2x performance improvement at initialization (from 3.3 to 1.5seconds)

I noticed not much in the way of actual querying/hydrating of objects, but that is because the project I'm using it on has relatively few properties per object. I'd imagine the performance improvement is more dramatic the more properties that need to be set.

This is definately something that needs to be considered for a 2.0 based release of NHibernate, but I can certanly see why this won't make it into the 1.x branch. (.NET 2.0 only feature)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 2:28 pm 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
If you were using the ReflectionOptimizer setting before AND you have a lot of public properties/fields, then I wouldn't expect to see a huge performance improvement.

However, if you have a good deal of private member fields that are mapped (i.e. using an access strategy), then I would expect a large performance improvment.

You are correct, however, in that the more properties/fields you have mapped, the bigger the performance benefit is going to be.

Thanks for testing this code out!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 2:45 pm 
Newbie

Joined: Wed Mar 22, 2006 12:52 pm
Posts: 5
Initialization is a huge gain with your code however. I suspect this is because creating DynamicMethods is much less costly than their current ReflectionOptimizer in terms of setup.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 5:06 pm 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
I just remembered something about the this implementation that I forgot to mention.

Along with support for implicit and explicit cast operators, I also added support for TypeConverters and the IConvertible interface!

So, if you have a domain class mapped that doesn't match up with a database type, we will look for different ways of obtaining the assignment. The order of precedence is:

1) Nullables are assignment compatible with their underlying types. In this case, we will use the operator overload for Nullables.

2) If Type.IsAssignableFrom() returns true, then we just assign the values.

3) Look for an implicit cast operator overload. If found, use it.

4) Look for an explicit cast operator overload. If found, use it.

5) Look for a TypeConverter for the type. If found and it can convert to the target type, then use it.

6) Finally look for the IConvertible interface. If found and it can convert to the target type, then use it.

7) Error out with an InvalidCastException.

Just an FYI!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 5:09 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
smudges, I will certainly try to integrate either your or Matthew Mastracci's reflection optimizer into the code base in the next major version of NH.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 22, 2006 8:36 pm 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
Sergey,

I would think it best to implement a configuration setting that allows the user to specify which IGetSetHelper methology they prefer to use. NHibernate can have one out of the box, but we shouldn't force the implementation on anyone.


Last edited by smudges on Thu Mar 23, 2006 10:20 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 23, 2006 3:42 am 
Senior
Senior

Joined: Thu Aug 25, 2005 3:35 am
Posts: 160
+1 ;-)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 03, 2006 10:44 am 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
Has anyone else tried out this code yet? I am *really* interested in learning of your success and/or failure using this code.


Top
 Profile  
 
 Post subject: Alternate Approach
PostPosted: Tue Apr 04, 2006 12:44 pm 
Newbie

Joined: Thu Jan 05, 2006 11:52 am
Posts: 11
My 2c... we were just starting with NHibernate on a database with hundreds of tables, some with over 100 fields. I first adapted a code-gen tool/template (mygeneration) to generate the hbm.xml files (one-class-per-table as a starting point). As a result I have about 250-300 xml files - all up about 1Mb of mapping.

Using the original reflection optimization it would take approx 90 seconds (from memory) to create the session factory. I stepped through the source and it seemed to mostly relate to my use of composite-id without specifying a class (i.e. requiring NHibernate to create one). I then decided to create my own composite id classes in these cases! However I still kept the xml files around as a test bed as a knew people were working on alternate optimizers! (I do not have a full test-bed yet using the composite-id classes.)

I tried the newer reflection optimizer one on the original test set but found that startup times were at least equal and even longer and comsuming lots of memory. It could be that my testing was flawed, we could try to code-generate a bunch of 'random' mapping files and PONOs as a test bed. Perhaps one test bed without composite-ids, and another with composite-ids (without id classes).

After stepping through the source code again though I came up with a different approach. It appears that aside from components, the reflection optimizer is only used for GetPropertyValues() and SetPropertyValues() in the persister. As I was already code-generating a persister class for each class for other reasons, I added overrides for these two methods. (The implementation trick being to process the properties in the right order - based on their order as if their name is added to a HashTable instead of their actual mapped order. (This may make this approach vulnerable to future NHibernate implementation changes however.)

So with reflection optimizer turned off I _think_ I am getting the best of both worlds: fast initialization and avoiding reflection (except for private-access properties where I have to call the base GetPropertyValue()/SetPropertyValue()). The maintenance of the persister overrides is not an issue in my case as I am code-generating the persister and not using Components so far.

Does this make sense or has anyone had any success or failure with this approach?

Cheers,
-Matthew Hobbs


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 04, 2006 2:42 pm 
Beginner
Beginner

Joined: Tue May 17, 2005 7:25 pm
Posts: 43
Location: Somewhere, USA
Thanks for the feedback! This is really interesting.

The memory consuption you mentioned makes some sense. The dynamic method approach, that this optimizer is using, creates setter and getter methods in memory. Although, the original optimizer used a generated assembly approach, it too would eventually load that assembly, requiring a similar memory footprint.

How did you profile the load times? Can you provide a sample project that I can use to profile against and retreive similar results as you?

I am curious as to where the time was being spent during load time. If it's during method compilation or during emit time. I can imagine delaying the 'compilation' of the delegate until it's actually needed.

What we really need it a profiler output to get a good idea of where the time is being spent.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 05, 2006 10:49 am 
Newbie

Joined: Thu Jan 05, 2006 11:52 am
Posts: 11
Hi, good news! My apologies, my testing was done using the other reflection optimizier posted on the dev list. I figured from the emails that they were similar in approach but the results suggest some differences! I generated some 'random' classes/maps and re-ran using the original reflection optimizer, the one I tried, and then your one.

I found that the reflection optimizer one I originally tried did speed up the xml loading/configuration but the config.BuildSessionFactory() was the step that took much longer with a seemingly increasing memory load until I killed it.

Then I tried your one and both the xml loading/configuration and BuildSessionFactory() steps were very much faster than the original, I had to run the test again to be sure! Going from 1-2 minutes for each of the two steps down to a few seconds for both steps.

I tried the LCG stuff on my main working project but did get an InvalidCastException in EmitUnboxConvertBoxIL() with sourceType=System.Collections.ICollection and targetType=System.Collections.IList but this may be due to me not integrating all the files I need from your zip? So far I took GetSetHelperFactory (commented out the Environment check), LCGGetSetHelper, IGetter, ISetter, BasicGetter, BasicSetter, FieldGetter, FieldSetter. Let me know if I need more.

Cheers,
-Matthew Hobbs

PS: I can post my 'random class/mapping' generator code if anyone would like it.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 41 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.