Battle of Micro-ORMs: Benchmarks and Observations

By Mike on 21 May 2012

Update: See this post for newer benchmarks

Since I did a bit of benchmarking for my SqlFu micro-orm, I thought how about I'd do a 'proper'  job and benchmark more micro-orms and more usage scenarios. I've included all the micro-orms I found via Nuget or I knew about, but I didn't include any of the heavy ORMs as they're aimed at a different target .


The participants:
- SqlFu
- Dapper.net with extensions
- PetaPoco
- Massive
- FluentData
- InsightDatabase
- ServiceStack.OrmLite

A few observations

Because every orm has its quirks, I had to remove enum handling and avoid returning null when the result is a nullable value.


PetaPoco
 -  doesn't like converting from tinyint to Enum when executing scalar.
 -  doesn't support mapping from string to Enum
 -  doesn't suport mapping dbnull to nullable when executing scalar
 -  handles up to 5 pocos and requires to put the column names in the same order as the properties of the result poco. A bit awkward but it works.


Dapper
- it doesn't support byte to enum when mapping result
- the multi mapping kinda works or I don't know how to write the sql in order for dapper to properly map it. Anyway, this is an issue for anyone using it: hit or miss depending on the sql.


Massive
- it's strange to work with, perhaps because I am not used to the Active Record pattern. I consider it a bit unintuitive to use compared to the others.
- it can't automatically map scalars to a type, you have to do the conversion yourself.
- couldn't figure how to do a proper insert


FluentData
- quite intuitive, I like using it
- doesn't automatically ignore properties not found in query result
- doesn't support dbnull to nullable when executing scalar
- doesn't support  byte to enum
- doesn't handle Inserts properly - SqlServer complains about the generated sql.


ServiceStack OrmLite
- it does support a form of multi mapping but it is specific to OrmLite.
- doesn't support Enum to byte (it's safe to assume it requires enums to be persisted as varchar)


InsightDatabase
- built mainly for stored procedures usage, annoying if you're using just sql
- interesting features (like async queries) which are outside of this benchmark

SqlFu
- Handles Enums properly and it doesn't choke on nullables. It's also the most handsome of them all.


Scenarios

Each use case is run 500 times, after 10 times of warm up. When the micro-orm doesn't support a feature out of the box (i.e you'd have to do it manually), I put it as Not supported action. Only suported actions are timed. I've run all the benchmarks a couple of times in order to get relevant results.

While there is no clear winner, especially since you have to consider all the features a package offers, you can clearly see a trend on who's in the top and who's lagging behind.

Executing scenario: FetchSingleEntity
-----------------------------------
Massive doesn't support the action. not explicit type support
Dapper entity - 500 iterations executed in 76,5396 ms
SqlFu Get - 500 iterations executed in 77,6155 ms
PetaPoco entity - 500 iterations executed in 78,8191 ms
SqlFu FirstOrDefault - 500 iterations executed in 80,1971 ms
OrmLite - 500 iterations executed in 80,2547 ms
InsightDatabase - 500 iterations executed in 160,2398 ms
FluentData - 500 iterations executed in 206,3038 ms

Executing scenario: FetchSingleDynamicEntity
-----------------------------------
SqlFu dynamic - 500 iterations executed in 74,6951 ms
Dapper dynamic - 500 iterations executed in 80,3824 ms
PetaPoco dynamic - 500 iterations executed in 83,6375 ms
Massive - 500 iterations executed in 130,8806 ms
OrmLite - 500 iterations executed in 146,8522 ms
InsightDatabase - 500 iterations executed in 176,2635 ms
FluentData - 500 iterations executed in 280,377 ms

Executing scenario: QueryTop10
-----------------------------------
Massive doesn't support the action. not explicit type support
SqlFu - 500 iterations executed in 90,7809 ms
PetaPoco - 500 iterations executed in 106,1067 ms
Dapper  - 500 iterations executed in 115,7401 ms
OrmLite - 500 iterations executed in 128,891 ms
InsightDatabase - 500 iterations executed in 203,417 ms
FluentData - 500 iterations executed in 445,0844 ms


Executing scenario: QueryTop10Dynamic
-----------------------------------
Dapper  - 500 iterations executed in 99,1737 ms
OrmLite - 500 iterations executed in 108,785 ms
Massive - 500 iterations executed in 116,3599 ms
SqlFu - 500 iterations executed in 116,4271 ms
PetaPoco dynamic - 500 iterations executed in 125,1672 ms
InsightDatabase - 500 iterations executed in 192,129 ms
FluentData - 500 iterations executed in 198,8402 ms

Executing scenario: PagedQuery_Skip0_Take10
-----------------------------------
Dapper  doesn't support the action. No implicit pagination support
FluentData doesn't support the action. No implicit pagination support
OrmLite doesn't support the action. No implicit pagination support
InsightDatabase doesn't support the action. No implicit pagination support
Massive - 500 iterations executed in 115,4747 ms
SqlFu - 500 iterations executed in 199,1776 ms
PetaPoco - 500 iterations executed in 292,4833 ms

Executing scenario: ExecuteScalar
-----------------------------------
OrmLite - 500 iterations executed in 59,7584 ms
InsightDatabase - 500 iterations executed in 64,2303 ms
PetaPoco int - 500 iterations executed in 66,3844 ms
SqlFu scalar int - 500 iterations executed in 77,7015 ms
Dapper scalar int - 500 iterations executed in 83,0212 ms
Massive - 500 iterations executed in 130,4026 ms
FluentData - 500 iterations executed in 157,6623 ms

Executing scenario: MultiPocoMapping
-----------------------------------
Massive doesn't support the action. Specified method is not supported.
OrmLite doesn't support the action. Suports only its own specific source format
InsightDatabase doesn't support the action. No implicit multi mapping support
SqlFu - 500 iterations executed in 75,7716 ms
Dapper  - 500 iterations executed in 88,3029 ms
PetaPoco - 500 iterations executed in 99,3068 ms
FluentData - 500 iterations executed in 236,9429 ms

Executing scenario: Inserts
-----------------------------------
Massive doesn't support the action. Couldn't figure how to insert pocos with auto increment id
FluentData doesn't support the action. Specified method is not supported.
InsightDatabase doesn't support the action. Specified method is not supported.
SqlFu - 500 iterations executed in 112,067 ms
PetaPoco - 500 iterations executed in 126,0432 ms
OrmLite - 500 iterations executed in 128,8205 ms
Dapper - 500 iterations executed in 264,1543 ms

Executing scenario: Updates
-----------------------------------
InsightDatabase doesn't support the action. Specified method is not supported.
SqlFu - 500 iterations executed in 75,1604 ms
PetaPoco - 500 iterations executed in 80,522 ms
Dapper  - 500 iterations executed in 102,6581 ms
OrmLite - 500 iterations executed in 195,2351 ms
massive - 500 iterations executed in 202,5537 ms
FluentData - 500 iterations executed in 249,3975 ms


You can find the benchmark project on GitHub (part of the SqlFu repository).

Comments (7) -

Brent
Brent
12 June 2012 #

How about adding a subjective conclusion at the end?

Reply

Admin
Admin
12 June 2012 #

Sure Brent. My subjective conclusion is that SqlFu is the most well rounded micro-orm and I recommend it to everyone.

Reply

Eric Swann
Eric Swann
27 June 2012 #

Nice job, just stumbled on this....thanks for the effort.

Reply

Eric Swann
Eric Swann
27 June 2012 #

Mike, one thing I was trying to access was the transaction isolation level.  It may be good enough for me to use the DB/Connection default, but sometimes it's necessary to elevate this for particular situations.  Is there a way to get to this?  Looking at the code, seems like an optional param on begintrans would be nice, but if you have other ideas, please let me know thanks.

Reply

Admin
Admin
28 June 2012 #

I think too that an optional argument to BeginTransaction is ok. I'll add that soon to SqlFu. In the mean time, if you have other suggestions, please post them on GitHub ( https://github.com/sapiens/SqlFu/issues ) to track them easily.

Reply

Eric Swann
Eric Swann
28 June 2012 #

Good point will do in the future.

Reply

Jon Wagner
Jon Wagner
5 February 2013 #

Good work on this, Mike. After tuning Insight.Database in v2.0, I re-ran your benchmarks.

The results are here: code.jonwagner.com/.../
Here is an updated InsightDbTests.cs: https://gist.github.com/jonwagner/4714467

NOTE: When using Insight.Database with sql text instead of procs, just add "Sql" to the end of the method. e.g. Query => QuerySql. Not quite so "annoying" when working with sql text. Smile

Reply

Add comment

biuquote
Loading