So, mythz didnt like my no-so-scientific benchmark question in a previous SO question, but since I would like to switch over to OrmLite, I need to figure out if it is slow, and if so, why.
In my "research", I come to the conclusio that complex objects, that in OrmLite are blobbed to JSON, is the culprit of very slow SELECTs.
Therefore, I created a new project that focuses solely on OrmLite, and does not compare with anything else other than itself, and the aim here is to see the differences between having blobbed JSON objects, and not having them.
It can be found on GitHub:
https://github.com/tedekeroth/ormlitebenchmarking
Solution looks like this:
I am running OrmLite 5.1.1 on a Windows 7, 2.6Ghz, 24 GB RAM and no CPU load currently, using MySql 5.6. Application connects to 127.0.0.1 (root/root) and needs database "ormlite".
I have enabled ThrowOnError:
OrmLiteConfig.ThrowOnError = JsConfig.ThrowOnError = true;
The application looks like this:
- No data: just the object created, no properties has data
- Primitives: Just some of the simple primitive properties are populated
- Prim + one complex: all primitives as above + one blobbed complex object
- Full data: all of the above + another 2 complex blobbed objects
The Create button first creates 10 000 objects in a list, and then they are persisted using OrmLite Insert method. Time measurement is done only for the INSERTs, not creating the objects.
public void AddRow<T>(T coreObject) where T : CoreObject
{
long id = 0;
using (var _db = _dbFactory.Open())
{
id = _db.Insert<T>(coreObject, selectIdentity: true);
}
}
The Read button reads all rows in the table, and recreates the Customer objects:
public List<T> FetchAll<T>()
{
using (var _db = _dbFactory.Open())
{
List<T> list = _db.Select<T>();
return list;
}
}
So, testing should be done like:
- Select mode, and press Create, time took will be displayed
- Press Read to read back all the rows currently in the table
To test another mode, empty the db table (customer
) to have a clean one.
BENCHMARKING
INSERT
Creating 10 000 objects (not measured) and inserting them into database.
- No data: ~26-27 seconds
- Primitives: ~27.1-27.4 seconds
- Prim + one complex: ~27.5-29 seconds
- Full data: ~28 seconds
So, all in all, around the same, 26-29 seconds.
SELECT
Reading 10 000 objects from db, as inserted above.
- No data: ~460 ms
- Primitives: ~700-720 ms
- Prim + one complex: ~970-1030 ms
- Full data: 30000-32000 ms (30-32 seconds)
CONCLUSIONS
"Full data" is obviously where the big smack-in-the-face appears.
The complex blobbed objects that is added (ContactDetails
), seems to mess it up. I noticed this in a previous test, but the object itself isnt very complex, see below. So, I am not sure why it jumps like this, or if these numbers are reasonable.
Thats why I though maybe Mythz could take a look at this cleaner benchmark? =)
The question is: why does persisting an object (to JSON as per OrmLite) slow down SELECTs in this way?
[Serializable]
public class ContactDetails
{
public List<ContactItem> ContactItemList
{
get; set;
}
public ContactItem CurrentContactItem
{
get; set;
}
public ContactItem DefaultContactItem
{
get; set;
}
public bool IgnorePrimaryWaitBuffer
{
get; set;
}
public ContactDetails(List<ContactItem> contactItemList, ContactItem currentContactItem, ContactItem defaultContactItem)
{
ContactItemList = contactItemList;
CurrentContactItem = currentContactItem;
DefaultContactItem = defaultContactItem;
}
public ContactDetails()
{
}
}
I've managed to download and profile this solution which highlighted the cause of the issue of not caching the type accessors of late-bound types which is resolved with this commit.
With this change performance for loading 10000 rows with complex types reduced from 11,765ms to 669ms (on my iMac 5k) as seen below:
This change is available from v5.1.1 that's now available on MyGet.
Note: I've removed the JsConfig.IncludeTypeInfo
line below:
JsConfig.IncludeTypeInfo = true;
Which forces the Serializers to emit type info for every object which increases the payload size and decreases performance. ServiceStack.Text will already emit the type info when it's needed, i.e. for object
, interfaces
and abstract
classes, so you should rarely force it yourself unless it's absolutely needed, as it can have a major detrimental impact on performance.
Ideally your DTO's should not use interfaces, late-bound objects or inheritance but if you are consider making the base types abstract
to force the type info to only where it's needed, instead of always emitting them.
来源:https://stackoverflow.com/questions/51083201/servicestack-benchmark-continued-why-does-persisting-a-simple-complex-to-json