Performance
For each ORM, we ran these tests several times with the same random seed and chose +/- the median results. The deviations of these results comparing to the most recent results are insignificant. The biggest deviation was measured for the tests "Scheme2-100-roots".
Database Schemes
- Scheme1: 250,000 rows/independent-table, 200,000 rows/dependent-table
- Big data with less relations - not so many joins, no so many additional collection rows to root rows in queries.
- Scheme2: 25,000 rows/independent-table, 50,000 rows/dependent-table
- Less data, many relations - many joins, many additional collection rows to root rows in queries.
- Scheme3: 200,000 rows/independent-table, 300,000 rows/dependent-table
- Big data, many relations...
Test Types
- The load tests itself has 2x5 iterations, each iteration takes 20 seconds. So, 5 iterations should be performed in 01:40 minutes (100 seconds).
- We also measure the real test time, that is usually longer, because of multitasking of a single computer that the tests are run on. Many of tasks has to be switched between from one to another, because of limited number of CPU threads.
- For every test, The first 5 iterations simulates 50 users while the last 5 iterations 100 users.
- Filtering is done by index range lookup of primary or foreign keys.
- None of the tests generates an empty query result.
- Scheme1-100-roots
- Generates heavy relation (join) queries for 100 roots upon the Scheme1. Expected about 500 rows.
- Scheme1-400-roots
- Generates heavy relation (join) queries for 400 roots upon the Scheme1. Expected about 1500 rows.
- Scheme2-100-roots
- Generates heavy relation (join) queries for 100 roots upon the Scheme2. Expected about 1200 rows.
- Scheme2-400-roots
- Generates heavy relation (join) queries for 400 roots upon the Scheme2. Expected about 7000 rows.
- Scheme3-100-roots
- Generates heavy relation (join) queries for 100 roots upon the Scheme3. Expected about 900 rows.
- Scheme3-400-roots
- Generates heavy relation (join) queries for 400 roots upon the Scheme3. Expected about 6000 rows.
ORM Overview
Many developers use Dapper and critic Entity Framework Core for performance. Dapper declares in its Nuget package description the following: "A high performance Micro-ORM..." The only truth is, that it is micro-ORM, because the only thing it does is the mapping of columns from a flat single table SELECT into entity properties of the same name (and you have to define SQL by yourself). So, if it selects only a few data, it is surely faster.
Entity Framework and D3ORM are also able to select only a few columns in queries... :-D
The developers should think about how to tune or simplify their queries due to performance issues rather, then searching for another ORM. Entity Framework and D3ORM are both high performance tools in this field.
D3ORM
+
Entity aggregations, light layered architecture of a target project, quick development, support of own or generated entities, stored procedures, JSON serialization, DTOs, asynchronous and anonymous bulk operations. No nuget package dependencies. High performance - among other things, thanks to caching entities once aggregated for going through repeating columns much more faster and still consuming not much RAM...
-
Learning new technology, poor support over the Internet, yet. Some claims on db schema design (no support of complex primary keys). All the versions so far contains obfuscated compilations and may be slightly slower than declared. No support of lazy loading (it is a bad proof of concept for loading data from a database and we want to keep your project clean).
Dapper
+
Maps single table column names and values into entity or DTO properties.
-
Does not aggregate entities, does not help with constructing SQL in any way, hard to maintain those SQL queries (plain text), consumes a lot of RAM according to data it selects, low performance...
Entity Framework
+
Entity aggregations, support of complex primary keys, sophisticated query building using LINQ, wide support over the Internet, quick development, high performance, caching entities once aggregated.
-
Some small impact on an architecture through all layers - less usable for well designed project (harder separation of queries and repositories and also queries and database connections), database first concept generated entities are hardly convertible into JSON in an automated way, enables beginners to build performance issue queries by an accident.
NHibernate (DNF)
Since we were being giving parsing exceptions for such a complex queries we use in these tests after conversion from SQL -> HQL, this ORM is not included in the tests. We were only able to parse HQL and retrieve results for eager loading from not so complicated nested queries.
+
Entity aggregations, support of complex primary keys, bulk operations, eager and lazy loading, many years of experience.
-
Very hard to maintain: User have to maintain duplicitous information of mappings in xml (or in entity attributes using Fluent NHibernate) when entity changes (no generator), eager loading possible only with several queries or by one query using HQL, which is similar to maintain as SQL (plain text). Slow and architecture impacting development in most of the cases. HQL cannot be used for some more complex queries with nested selects - you might join collections in a flat way, that could mean an performance issue or inability to resolve some queries. Pure documentation, pure support of .NET 6.0 (i.e.: Fluent NHibernate is missing completely).
Pure SQL
+
Full control of queries, direct and quick data fetching 1:1.
-
No mapping between SQL table <-> entity, hard to maintain queries (plain text), no caching of repeating columns' data.
Telerik OpenAccess (DNF)
Since this ORM is not supported for .NET 6.0, it has not been included in the tests.
+
Uses LINQ.
-
Heavy impact on any architecture using classes such as TrackedList or DataSetResult, no support of eager loading - collections are loaded in a lazy way in separate queries into TrackedList - useful only for a few cases... every time you need to respond with a full result, the database is called many times. No support for .NET 6+.
MySQL Performance Test Results
Transactions | RAM | Errors | Multitasking | Transactions | RAM | Errors | Multitasking | Transactions | RAM | Errors | Multitasking | Transactions | RAM | Errors | Multitasking | |||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Scheme1 100r 50u | D3 | 6473 | 128 | 0 | 00:02:21.85 | DPR | 6092 | 222 | 0 | 00:02:24.05 | EF6 | 6340 | 169 | 0 | 00:02:23.55 | SQL | 6041 | 165 | 0 | 00:02:24.06 |
Scheme1 100r 100u | D3 | 5888 | 128 | 0 | 00:02:34.26 | DPR | 6277 | 222 | 0 | 00:02:35.39 | EF6 | 6180 | 169 | 0 | 00:02:35.02 | SQL | 5958 | 165 | 0 | 00:02:32.24 |
Scheme1 400r 50u | D3 | 3821 | 534 | 0 | 00:02:21.40 | DPR | 3677 | 1471 | 0 | 00:02:20.99 | EF6 | 3084 | 1358 | 0 | 00:02:21.40 | SQL | 3984 | 338 | 0 | 00:02:21.93 |
Scheme1 400r 100u | D3 | 3831 | 534 | 0 | 00:02:31.77 | DPR | 3617 | 1471 | 0 | 00:02:33.07 | EF6 | 3440 | 1358 | 0 | 00:02:15.46 | SQL | 3816 | 338 | 0 | 00:02:37.92 |
Scheme2 100r 50u | D3 | 4040 | 467 | 0 | 00:02:19.26 | DPR | 2092 | 3555 | 0 | 00:02:39.36 | EF6 | 4378 | 411 | 0 | 00:02:17.86 | SQL | 2963 | 613 | 0 | 00:02:28.92 |
Scheme2 100r 100u | D3 | 3987 | 467 | 0 | 00:02:31.04 | DPR | 1988 | 3555 | 0 | 00:02:58.21 | EF6 | 3993 | 411 | 3 | 00:02:26.76 | SQL | 2757 | 613 | 0 | 00:02:41.61 |
Scheme2 400r 50u | D3 | 1422 | 476 | 0 | 00:02:43.59 | DPR | 891 | 9994 | 0 | 00:05:36.88 | EF6 | 1531 | 1287 | 0 | 00:02:37.71 | SQL | 986 | 3808 | 0 | 00:03:28.59 |
Scheme2 400r 100u | D3 | 1468 | 476 | 0 | 00:03:09.57 | DPR | 1181 | 9994 | 47 | 00:07:58.10 | EF6 | 1606 | 1287 | 0 | 00:02:58.87 | SQL | 1122 | 3808 | 0 | 00:04:17.45 |
Scheme3 100r 50u | D3 | 3119 | 198 | 0 | 00:02:35.04 | DPR | 3402 | 581 | 0 | 00:02:29.23 | EF6 | 3553 | 214 | 0 | 00:02:30.82 | SQL | 2989 | 261 | 0 | 00:02:36.11 |
Scheme3 100r 100u | D3 | 3711 | 198 | 0 | 00:02:41.78 | DPR | 3278 | 581 | 0 | 00:02:38.15 | EF6 | 3643 | 214 | 1 | 00:02:57.06 | SQL | 3535 | 261 | 0 | 00:02:31.94 |
Scheme3 400r 50u | D3 | 1486 | 202 | 51 | 00:03:48.31 | DPR | 1416 | 2526 | 21 | 00:04:00.48 | EF6 | 1562 | 354 | 20 | 00:03:22.18 | SQL | 1341 | 655 | 37 | 00:03:50.27 |
Scheme3 400r 100u | D3 | 1848 | 202 | 1 | 00:03:10.16 | DPR | 358 | 2526 | 16 | 00:03:31.00 | EF6 | 1640 | 354 | 3 | 00:03:06.01 | SQL | 1640 | 655 | 50 | 00:03:57.70 |
OVERALL | D3 | 41094 (1) | 314.17 (1) | 52 (2) | 00:02:44.00 (2) | DPR | 35269 | 3058.17 | 84 (3) | 00:03:28.71 | EF6 | 40750 (2) | 632.17 (2) | 27 (1) | 00:02:39.39 (1) | SQL | 37082 (3) | 973.33 (3) | 87 | 00:02:59.06 (3) |
Benchmark
Benchmark computation include all the criteria in the following pattern.
- Benchmark = Transactions - RAM/4 - (Errors)*8 - (multitaskingIteration(seconds) - 140)*4