Speed Comparison: Dapper vs Entity Framework

A few months ago I ran across a blog post about getting started with Dapper.NET. I had heard of Dapper before but had never actually tried to use it. Dapper is known for being fast, but I was curious just how much faster it would be in the situations I typically find myself: writing database queries. I’ve long used Entity Framework for data layers, which is often criticized for being slow.

To test out Dapper’s speed, I built four basic tests so I could compare its performance to Entity Framework. If you’d like to run these experiments yourself, all the code used for this blog post is available on GitHub.

Data Model

The data model is relatively small and simple. I’ve worked on a few projects involving athletes so that was my starting point.

[sourcecode language=”plain”]

public class Athlete
{
public long Id { get; set;}

public string FirstName { get; set; }

public string LastName { get; set; }

public string Position { get; set; }
}

[/sourcecode]

An athlete is nothing without a team, which leads to the next class.

[sourcecode language=”plain”]
public class Team
{
public long Id { get; set; }

public string Name { get; set; }
}

[/sourcecode]

An athlete can, of course, be on multiple teams so a third class is needed to link the two.

[sourcecode language=”plain”]
public class AthleteTeam
{
public long AthleteId { get; set; }

public long TeamId { get; set; }
}
[/sourcecode]

I’ve never had a good experience when using Entity Framework to manage the relationships between objects, so there are no navigation properties on these objects. They are a straight mapping of class to database table. I mostly use ORMs to reduce the amount of SQL I write, not necessarily to manage the relationships between my objects.

Test Setup

Before starting any tests, I seeded my database with three sports teams with each containing 1,000 athletes. The first thing I found was both Entity Framework and Dapper take longer with the first query than with subsequent queries. For Entity Framework, I know a query has to be complied on the first execution, but for Dapper I’m not sure what it’s doing during the first execution. For all tests, I ran each query twice and ran each test 10 times before averaging out the results. All times are in milliseconds.

Loading 1 Athlete

In the first test, I loaded a single athlete by Id from the database.

Test Iteration Entity Framework First Entity Framework Second Dapper First Dapper Second
1 348 13 83 6
2 361 15 81 5
3 332 13 67 6
4 312 11 67 6
5 313 14 69 6
6 334 14 69 6
7 368 12 75 7
8 331 13 72 9
9 333 13 72 6
10 321 14 69 8
Average (ms) 335.3 13.1 72.6 6.7

There’s no denying that Dapper is faster than Entity Framework, especially on the first execution. Compiling the LINQ query is an expensive event.

Loading Many Athletes by Position

In the second test, I loaded all the athletes for a specific position to see how each framework fared loading multiple records and searching on a string field instead of a primary key.

Test Iteration Entity Framework First Entity Framework Second Dapper First Dapper Second
1 80 35 13 7
2 83 33 19 10
3 81 33 12 8
4 75 33 11 7
5 69 33 11 7
6 79 33 11 7
7 78 38 12 7
8 72 32 12 8
9 76 32 12 8
10 65 34 14 6
Average (ms) 75.8 33.8 12.7 7.5

The differences between the first and second queries were less extreme for the second test. My suspicion is both frameworks are mapping the database on the first query and pay an extra time penalty. Loading more results also increased the gap in speed between the second Entity Framework and Dapper queries.

Loading Teams with Athletes

I wanted to see how well each framework would handle joins which led to the third test: loading a team with all 1,000 athletes.

Test Iteration Entity Framework First Entity Framework Second Dapper First Dapper Second
1 143 38 23 16
2 144 42 27 20
3 127 39 21 16
4 132 29 21 26
5 136 38 28 22
6 142 37 24 20
7 132 35 22 16
8 133 39 22 16
9 127 36 24 18
10 121 37 21 21
Average (ms) 133.7 37 23.3 19.1

The results are pretty consistent with the other tests. Entity Framework is still lagging behind, but there was a Dapper run that took longer on the second query rather than the first. On average though the second Dapper query still ran 4.2 milliseconds faster.

Inserting Athletes

In the last test, I wanted to look at the speed differences inserting records into the database. I inserted one athlete at a time and for Dapper I used the Dapper-Extensions nuget package to handle the insert. The package reduces an insert to a single method call. In the past, the insert code I’ve seen has always looked a little weird and was one of my main complaints about Dapper.

Test Iteration Entity Framework First Entity Framework Second Dapper First Dapper Second
1 284 14 85 6
2 278 11 91 7
3 252 12 83 5
4 249 11 106 6
5 268 10 76 7
6 268 10 80 8
7 266 12 92 6
8 267 13 86 8
9 256 11 80 7
10 253 11 78 6
Average (ms) 264.1 11.5 85.7 6.6

Surprisingly, the first Dapper queries took around as long as the first queries for loading a single athlete. I don’t know enough about the internals of the Dapper-Extensions, but it seems likely there is an additional step taken when using its insert functionality. Otherwise the results are consistent with the trends seen across the other tests.

Conclusion

Dapper was written with speed as a priority and the tests definitely prove this out. However, if we ignore the overhead of the first query, the difference between the two ranged from 5-25 milliseconds. The largest site I know of using Dapper is Stack Overflow and I would think they definitely benefit from saving milliseconds anywhere they can. In a smaller application I’m not sure it makes sense to make an ORM decision solely for the sake of a few milliseconds. Instead I would pick based on the style of the code written for either framework. I like Entity Framework for its LINQ integration and will continue to favor it for that. To me using Dapper felt like writing SQL, which I try to avoid when possible.

In the past when a query starts to take too long executing through Entity Framework I have replaced it with either a view or a stored procedure, depending on the situation. Going forward, Dapper will be a valuable tool that could be used before going straight to raw SQL.