diff --git a/Dapper.Tests.Performance/Benchmarks.Belgrade.cs b/Dapper.Tests.Performance/Benchmarks.Belgrade.cs new file mode 100644 index 000000000..08239bb37 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.Belgrade.cs @@ -0,0 +1,44 @@ +using BenchmarkDotNet.Attributes; +using Belgrade.SqlClient.SqlDb; +using System.Threading.Tasks; + +namespace Dapper.Tests.Performance +{ + public class BelgradeBenchmarks : BenchmarkBase + { + private QueryMapper _mapper; + + [Setup] + public void Setup() + { + BaseSetup(); + _mapper = new QueryMapper(ConnectionString); + } + + [Benchmark(Description = "Belgrade: ExecuteReader", OperationsPerInvoke = Iterations)] + public Task ExecuteReader() + { + Step(); + // TODO: How do you get a Post out of this thing? + return _mapper.ExecuteReader("SELECT TOP 1 * FROM Posts WHERE Id = " + i, + reader => + { + var post = new Post(); + post.Id = reader.GetInt32(0); + post.Text = reader.GetString(1); + post.CreationDate = reader.GetDateTime(2); + post.LastChangeDate = reader.GetDateTime(3); + + post.Counter1 = reader.IsDBNull(4) ? null : (int?)reader.GetInt32(4); + post.Counter2 = reader.IsDBNull(5) ? null : (int?)reader.GetInt32(5); + post.Counter3 = reader.IsDBNull(6) ? null : (int?)reader.GetInt32(6); + post.Counter4 = reader.IsDBNull(7) ? null : (int?)reader.GetInt32(7); + post.Counter5 = reader.IsDBNull(8) ? null : (int?)reader.GetInt32(8); + post.Counter6 = reader.IsDBNull(9) ? null : (int?)reader.GetInt32(9); + post.Counter7 = reader.IsDBNull(10) ? null : (int?)reader.GetInt32(10); + post.Counter8 = reader.IsDBNull(11) ? null : (int?)reader.GetInt32(11); + post.Counter9 = reader.IsDBNull(12) ? null : (int?)reader.GetInt32(12); + }); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.Dapper.cs b/Dapper.Tests.Performance/Benchmarks.Dapper.cs new file mode 100644 index 000000000..96307d289 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.Dapper.cs @@ -0,0 +1,64 @@ +using BenchmarkDotNet.Attributes; +using Dapper.Contrib.Extensions; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class DapperBenchmarks : BenchmarkBase + { + [Setup] + public void Setup() + { + BaseSetup(); + } + + [Benchmark(Description = "Dapper: Query (buffered)", OperationsPerInvoke = Iterations)] + public Post QueryBuffered() + { + Step(); + return _connection.Query("select * from Posts where Id = @Id", new { Id = i }, buffered: true).First(); + } + + [Benchmark(Description = "Dapper: Query (unbuffered)", OperationsPerInvoke = Iterations)] + public Post QueryUnbuffered() + { + Step(); + return _connection.Query("select * from Posts where Id = @Id", new { Id = i }, buffered: false).First(); + } + + [Benchmark(Description = "Dapper: QueryFirstOrDefault", OperationsPerInvoke = Iterations)] + public Post QueryFirstOrDefault() + { + Step(); + return _connection.QueryFirstOrDefault("select * from Posts where Id = @Id", new { Id = i }); + } + + [Benchmark(Description = "Dapper: Query (buffered)", OperationsPerInvoke = Iterations)] + public object QueryBufferedDynamic() + { + Step(); + return _connection.Query("select * from Posts where Id = @Id", new { Id = i }, buffered: true).First(); + } + + [Benchmark(Description = "Dapper: Query (unbuffered)", OperationsPerInvoke = Iterations)] + public object QueryUnbufferedDynamic() + { + Step(); + return _connection.Query("select * from Posts where Id = @Id", new { Id = i }, buffered: false).First(); + } + + [Benchmark(Description = "Dapper: QueryFirstOrDefault", OperationsPerInvoke = Iterations)] + public object QueryFirstOrDefaultDynamic() + { + Step(); + return _connection.QueryFirstOrDefault("select * from Posts where Id = @Id", new { Id = i }).First(); + } + + [Benchmark(Description = "Dapper: Contrib Get", OperationsPerInvoke = Iterations)] + public object ContribGet() + { + Step(); + return _connection.Get(i); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.EntityFramework.cs b/Dapper.Tests.Performance/Benchmarks.EntityFramework.cs new file mode 100644 index 000000000..dee82ccd5 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.EntityFramework.cs @@ -0,0 +1,43 @@ +using BenchmarkDotNet.Attributes; +using Dapper.Tests.Performance.Linq2Sql; +using System; +using System.Data.Linq; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class EntityFrameworkBenchmarks : BenchmarkBase + { + private EntityFramework.EFContext Context; + private static readonly Func compiledQuery = + CompiledQuery.Compile((DataClassesDataContext ctx, int id) => ctx.Posts.First(p => p.Id == id)); + + [Setup] + public void Setup() + { + BaseSetup(); + Context = new EntityFramework.EFContext(_connection); + } + + [Benchmark(Description = "EF6: Normal", OperationsPerInvoke = Iterations)] + public Post Normal() + { + Step(); + return Context.Posts.First(p => p.Id == i); + } + + [Benchmark(Description = "EF6: SqlQuery", OperationsPerInvoke = Iterations)] + public Post SqlQuery() + { + Step(); + return Context.Database.SqlQuery("select * from Posts where Id = {0}", i).First(); + } + + [Benchmark(Description = "EF6: No Tracking", OperationsPerInvoke = Iterations)] + public Post NoTracking() + { + Step(); + return Context.Posts.AsNoTracking().First(p => p.Id == i); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.HandCoded.cs b/Dapper.Tests.Performance/Benchmarks.HandCoded.cs new file mode 100644 index 000000000..7dd45a66b --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.HandCoded.cs @@ -0,0 +1,93 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Data; +using System.Data.SqlClient; + +namespace Dapper.Tests.Performance +{ + public class HandCodedBenchmarks : BenchmarkBase + { + private SqlCommand _postCommand; + private SqlParameter _idParam; +#if !COREFX + private DataTable _table; +#endif + + [Setup] + public void Setup() + { + BaseSetup(); + _postCommand = new SqlCommand() + { + Connection = _connection, + CommandText = @"select Id, [Text], [CreationDate], LastChangeDate, + Counter1,Counter2,Counter3,Counter4,Counter5,Counter6,Counter7,Counter8,Counter9 from Posts where Id = @Id" + }; + _idParam = _postCommand.Parameters.Add("@Id", SqlDbType.Int); +#if !COREFX + _table = new DataTable + { + Columns = + { + {"Id", typeof (int)}, + {"Text", typeof (string)}, + {"CreationDate", typeof (DateTime)}, + {"LastChangeDate", typeof (DateTime)}, + {"Counter1", typeof (int)}, + {"Counter2", typeof (int)}, + {"Counter3", typeof (int)}, + {"Counter4", typeof (int)}, + {"Counter5", typeof (int)}, + {"Counter6", typeof (int)}, + {"Counter7", typeof (int)}, + {"Counter8", typeof (int)}, + {"Counter9", typeof (int)}, + } + }; +#endif + } + + [Benchmark(Description = "HandCoded: SqlCommand", OperationsPerInvoke = Iterations)] + public dynamic SqlCommand() + { + Step(); + _idParam.Value = i; + + using (var reader = _postCommand.ExecuteReader()) + { + reader.Read(); + var post = new Post(); + post.Id = reader.GetInt32(0); + post.Text = reader.GetNullableString(1); + post.CreationDate = reader.GetDateTime(2); + post.LastChangeDate = reader.GetDateTime(3); + + post.Counter1 = reader.GetNullableValue(4); + post.Counter2 = reader.GetNullableValue(5); + post.Counter3 = reader.GetNullableValue(6); + post.Counter4 = reader.GetNullableValue(7); + post.Counter5 = reader.GetNullableValue(8); + post.Counter6 = reader.GetNullableValue(9); + post.Counter7 = reader.GetNullableValue(10); + post.Counter8 = reader.GetNullableValue(11); + post.Counter9 = reader.GetNullableValue(12); + return post; + } + } + + [Benchmark(Description = "HandCoded: DataTable", OperationsPerInvoke = Iterations)] + public dynamic DataTable() + { + Step(); + _idParam.Value = i; + var values = new object[13]; + using (var reader = _postCommand.ExecuteReader()) + { + reader.Read(); + reader.GetValues(values); + _table.Rows.Add(values); + return _table.Rows[_table.Rows.Count-1]; + } + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.Linq2Sql.cs b/Dapper.Tests.Performance/Benchmarks.Linq2Sql.cs new file mode 100644 index 000000000..c8a3b67fa --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.Linq2Sql.cs @@ -0,0 +1,43 @@ +using BenchmarkDotNet.Attributes; +using Dapper.Tests.Performance.Linq2Sql; +using System; +using System.Data.Linq; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class Linq2SqlBenchmarks : BenchmarkBase + { + private DataClassesDataContext Linq2SqlContext; + private static readonly Func compiledQuery = + CompiledQuery.Compile((DataClassesDataContext ctx, int id) => ctx.Posts.First(p => p.Id == id)); + + [Setup] + public void Setup() + { + BaseSetup(); + Linq2SqlContext = new DataClassesDataContext(_connection); + } + + [Benchmark(Description = "Linq2Sql: Normal", OperationsPerInvoke = Iterations)] + public Linq2Sql.Post Normal() + { + Step(); + return Linq2SqlContext.Posts.First(p => p.Id == i); + } + + [Benchmark(Description = "Linq2Sql: Compiled", OperationsPerInvoke = Iterations)] + public Linq2Sql.Post Compiled() + { + Step(); + return compiledQuery(Linq2SqlContext, i); + } + + [Benchmark(Description = "Linq2Sql: ExecuteQuery", OperationsPerInvoke = Iterations)] + public Post ExecuteQuery() + { + Step(); + return Linq2SqlContext.ExecuteQuery("select * from Posts where Id = {0}", i).First(); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.Massive.cs b/Dapper.Tests.Performance/Benchmarks.Massive.cs new file mode 100644 index 000000000..839e22b23 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.Massive.cs @@ -0,0 +1,25 @@ +using BenchmarkDotNet.Attributes; +using Massive; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class MassiveBenchmarks : BenchmarkBase + { + private DynamicModel _model; + + [Setup] + public void Setup() + { + BaseSetup(); + _model = new DynamicModel(ConnectionString); + } + + [Benchmark(Description = "Massive: Query (dynamic)", OperationsPerInvoke = Iterations)] + public dynamic Query() + { + Step(); + return _model.Query("select * from Posts where Id = @0", _connection, i).First(); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.NHibernate.cs b/Dapper.Tests.Performance/Benchmarks.NHibernate.cs new file mode 100644 index 000000000..2f12d8861 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.NHibernate.cs @@ -0,0 +1,69 @@ +using BenchmarkDotNet.Attributes; +using Dapper.Tests.Performance.NHibernate; +using NHibernate; +using NHibernate.Criterion; +using NHibernate.Linq; +using NHibernate.Transform; +using NHibernate.Util; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class NHibernateBenchmarks : BenchmarkBase + { + private IStatelessSession _sql, _hql, _criteria, _linq, _get; + + [Setup] + public void Setup() + { + BaseSetup(); + _sql = NHibernateHelper.OpenSession(); + _hql = NHibernateHelper.OpenSession(); + _criteria = NHibernateHelper.OpenSession(); + _linq = NHibernateHelper.OpenSession(); + _get = NHibernateHelper.OpenSession(); + } + + [Benchmark(Description = "NHibernate: SQL", OperationsPerInvoke = Iterations)] + public Post SQL() + { + Step(); + return _sql.CreateSQLQuery(@"select * from Posts where Id = :id") + .SetInt32("id", i) + .SetResultTransformer(Transformers.AliasToBean()) + .List()[0]; + } + + [Benchmark(Description = "NHibernate: HQL", OperationsPerInvoke = Iterations)] + public Post HQL() + { + Step(); + return _hql.CreateQuery(@"from Post as p where p.Id = :id") + .SetInt32("id", i) + .List()[0]; + } + + [Benchmark(Description = "NHibernate: Criteria", OperationsPerInvoke = Iterations)] + public Post Criteria() + { + Step(); + return _criteria.CreateCriteria() + .Add(Restrictions.IdEq(i)) + .List()[0]; + } + + [Benchmark(Description = "NHibernate: LINQ", OperationsPerInvoke = Iterations)] + public Post LINQ() + { + Step(); + return _linq.Query().First(p => p.Id == i); + } + + [Benchmark(Description = "NHibernate: Get", OperationsPerInvoke = Iterations)] + public Post Get() + { + Step(); + return _get.Get(i); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.PetaPoco.cs b/Dapper.Tests.Performance/Benchmarks.PetaPoco.cs new file mode 100644 index 000000000..d7e876bf8 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.PetaPoco.cs @@ -0,0 +1,38 @@ +using BenchmarkDotNet.Attributes; +using PetaPoco; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class PetaPocoBenchmarks : BenchmarkBase + { + private Database _db, _dbFast; + + [Setup] + public void Setup() + { + BaseSetup(); + _db = new Database(ConnectionString, "System.Data.SqlClient"); + _db.OpenSharedConnection(); + _dbFast = new Database(ConnectionString, "System.Data.SqlClient"); + _dbFast.OpenSharedConnection(); + _dbFast.EnableAutoSelect = false; + _dbFast.EnableNamedParams = false; + _dbFast.ForceDateTimesToUtc = false; + } + + [Benchmark(Description = "PetaPoco: Fetch", OperationsPerInvoke = Iterations)] + public dynamic Fetch() + { + Step(); + return _db.Fetch("SELECT * from Posts where Id=@0", i).First(); + } + + [Benchmark(Description = "PetaPoco: Fetch (Fast)", OperationsPerInvoke = Iterations)] + public dynamic FetchFast() + { + Step(); + return _dbFast.Fetch("SELECT * from Posts where Id=@0", i).First(); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.ServiceStack.cs b/Dapper.Tests.Performance/Benchmarks.ServiceStack.cs new file mode 100644 index 000000000..6033c118d --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.ServiceStack.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using ServiceStack.OrmLite; +using System.Data; + +namespace Dapper.Tests.Performance +{ + public class ServiceStackBenchmarks : BenchmarkBase + { + private IDbConnection _db; + + [Setup] + public void Setup() + { + BaseSetup(); + var dbFactory = new OrmLiteConnectionFactory(ConnectionString, SqlServerDialect.Provider); + _db = dbFactory.Open(); + } + + [Benchmark(Description = "ServiceStack.OrmLite: SingleById", OperationsPerInvoke = Iterations)] + public Post Query() + { + Step(); + return _db.SingleById(i); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.Soma.cs b/Dapper.Tests.Performance/Benchmarks.Soma.cs new file mode 100644 index 000000000..ddc94a950 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.Soma.cs @@ -0,0 +1,23 @@ +using BenchmarkDotNet.Attributes; + +namespace Dapper.Tests.Performance +{ + public class SomaBenchmarks : BenchmarkBase + { + private dynamic _sdb; + + [Setup] + public void Setup() + { + BaseSetup(); + _sdb = Simple.Data.Database.OpenConnection(ConnectionString); + } + + [Benchmark(Description = "Soma: FindById", OperationsPerInvoke = Iterations)] + public dynamic Query() + { + Step(); + return _sdb.Posts.FindById(i).FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.Susanoo.cs b/Dapper.Tests.Performance/Benchmarks.Susanoo.cs new file mode 100644 index 000000000..38bdb2330 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.Susanoo.cs @@ -0,0 +1,62 @@ +using BenchmarkDotNet.Attributes; +using Susanoo; +using Susanoo.Processing; +using System.Data; +using System.Linq; + +namespace Dapper.Tests.Performance +{ + public class SusanooBenchmarks : BenchmarkBase + { + private DatabaseManager _db; + private static readonly ISingleResultSetCommandProcessor _cmd = + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize(); + private static readonly ISingleResultSetCommandProcessor _cmdDynamic = + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize(); + + [Setup] + public void Setup() + { + BaseSetup(); + _db = new DatabaseManager(_connection); + } + + [Benchmark(Description = "Susanoo: Mapping Cache", OperationsPerInvoke = Iterations)] + public Post MappingCache() + { + Step(); + return CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize() + .Execute(_db, new { Id = i }).First(); + } + + [Benchmark(Description = "Susanoo: Mapping Cache (dynamic)", OperationsPerInvoke = Iterations)] + public dynamic MappingCacheDynamic() + { + Step(); + return CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize() + .Execute(_db, new { Id = i }).First(); + } + + [Benchmark(Description = "Susanoo: Mapping Static", OperationsPerInvoke = Iterations)] + public Post MappingStatic() + { + Step(); + return _cmd.Execute(_db, new { Id = i }).First(); + } + + [Benchmark(Description = "Susanoo: Mapping Static (dynamic)", OperationsPerInvoke = Iterations)] + public dynamic MappingStaticDynamic() + { + Step(); + return _cmdDynamic.Execute(_db, new { Id = i }).First(); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Benchmarks.cs b/Dapper.Tests.Performance/Benchmarks.cs new file mode 100644 index 000000000..402841713 --- /dev/null +++ b/Dapper.Tests.Performance/Benchmarks.cs @@ -0,0 +1,41 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using System; +using System.Configuration; +using System.Data.SqlClient; + +namespace Dapper.Tests.Performance +{ + [Config(typeof(Config))] + public abstract class BenchmarkBase + { + public const int Iterations = 50; + protected static readonly Random _rand = new Random(); + protected SqlConnection _connection; + public static string ConnectionString { get; } = ConfigurationManager.ConnectionStrings["Main"].ConnectionString; + + protected int i; + + protected void BaseSetup() + { + i = 0; + _connection = new SqlConnection(ConnectionString); + _connection.Open(); + } + + protected void Step() + { + i++; + if (i > 5000) i = 1; + } + } + + public class Config : ManualConfig + { + public Config() + { + Add(new MemoryDiagnoser()); + } + } +} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj b/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj index c1da1927d..4292bd4dd 100644 --- a/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj +++ b/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj @@ -12,21 +12,23 @@ + + - + - + - + - + @@ -37,5 +39,9 @@ + + + + diff --git a/Dapper.Tests.Performance/PerformanceTests.cs b/Dapper.Tests.Performance/LegacyTests.cs similarity index 73% rename from Dapper.Tests.Performance/PerformanceTests.cs rename to Dapper.Tests.Performance/LegacyTests.cs index 27acaff69..27c66cb56 100644 --- a/Dapper.Tests.Performance/PerformanceTests.cs +++ b/Dapper.Tests.Performance/LegacyTests.cs @@ -22,7 +22,7 @@ namespace Dapper.Tests.Performance { - public partial class PerformanceTests + public class LegacyTests { private class Test { @@ -81,7 +81,10 @@ public async Task RunAsync(int iterations) foreach (var test in this.OrderBy(t => t.Watch.ElapsedMilliseconds)) { - Console.WriteLine(test.Name + " took " + test.Watch.ElapsedMilliseconds + "ms"); + var ms = test.Watch.ElapsedMilliseconds.ToString(); + Console.Write(ms); + Program.WriteColor("ms ".PadRight(8 - ms.Length), ConsoleColor.DarkGray); + Console.WriteLine(test.Name); } } } @@ -114,6 +117,8 @@ public async Task RunAsync(int iterations) { using (var connection = GetOpenConnection()) { +#pragma warning disable IDE0017 // Simplify object initialization +#pragma warning disable RCS1121 // Use [] instead of calling 'First'. var tests = new Tests(); // Linq2SQL @@ -252,35 +257,37 @@ public async Task RunAsync(int iterations) }, "Belgrade Sql Client"); //Susanoo - var susanooDb = new DatabaseManager(connection); - - var susanooPreDefinedCommand = - CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) - .DefineResults() - .Realize(); - - var susanooDynamicPreDefinedCommand = - CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) - .DefineResults() - .Realize(); - - tests.Add(Id => - CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) - .DefineResults() - .Realize() - .Execute(susanooDb, new { Id }).First(), "Susanoo: Mapping Cache Retrieval"); - - tests.Add(Id => - CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) - .DefineResults() - .Realize() - .Execute(susanooDb, new { Id }).First(), "Susanoo: Dynamic Mapping Cache Retrieval"); - - tests.Add(Id => susanooDynamicPreDefinedCommand - .Execute(susanooDb, new { Id }).First(), "Susanoo: Dynamic Mapping Static"); - - tests.Add(Id => susanooPreDefinedCommand - .Execute(susanooDb, new { Id }).First(), "Susanoo: Mapping Static"); + Try(() => { + var susanooDb = new DatabaseManager(connection); + + var susanooPreDefinedCommand = + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize(); + + var susanooDynamicPreDefinedCommand = + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize(); + + tests.Add(Id => + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize() + .Execute(susanooDb, new { Id }).First(), "Susanoo: Mapping Cache Retrieval"); + + tests.Add(Id => + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + .DefineResults() + .Realize() + .Execute(susanooDb, new { Id }).First(), "Susanoo: Dynamic Mapping Cache Retrieval"); + + tests.Add(Id => susanooDynamicPreDefinedCommand + .Execute(susanooDb, new { Id }).First(), "Susanoo: Dynamic Mapping Static"); + + tests.Add(Id => susanooPreDefinedCommand + .Execute(susanooDb, new { Id }).First(), "Susanoo: Mapping Static"); + }, "Susanoo"); //ServiceStack's OrmLite: Try(() => @@ -291,58 +298,44 @@ public async Task RunAsync(int iterations) }, "ServiceStack.OrmLite"); // Hand Coded - var postCommand = new SqlCommand() - { - Connection = connection, - CommandText = @"select Id, [Text], [CreationDate], LastChangeDate, + Try(() => { + var postCommand = new SqlCommand() + { + Connection = connection, + CommandText = @"select Id, [Text], [CreationDate], LastChangeDate, Counter1,Counter2,Counter3,Counter4,Counter5,Counter6,Counter7,Counter8,Counter9 from Posts where Id = @Id" - }; - var idParam = postCommand.Parameters.Add("@Id", SqlDbType.Int); - - tests.Add(id => - { - idParam.Value = id; - - using (var reader = postCommand.ExecuteReader()) - { - reader.Read(); - var post = new Post(); - post.Id = reader.GetInt32(0); - post.Text = reader.GetNullableString(1); - post.CreationDate = reader.GetDateTime(2); - post.LastChangeDate = reader.GetDateTime(3); - - post.Counter1 = reader.GetNullableValue(4); - post.Counter2 = reader.GetNullableValue(5); - post.Counter3 = reader.GetNullableValue(6); - post.Counter4 = reader.GetNullableValue(7); - post.Counter5 = reader.GetNullableValue(8); - post.Counter6 = reader.GetNullableValue(9); - post.Counter7 = reader.GetNullableValue(10); - post.Counter8 = reader.GetNullableValue(11); - post.Counter9 = reader.GetNullableValue(12); - } - }, "Hand Coded"); + }; + var idParam = postCommand.Parameters.Add("@Id", SqlDbType.Int); - // Subsonic isn't maintained anymore - doesn't import correctly - //Try(() => - // { - // // Subsonic ActiveRecord - // tests.Add(id => 3SubSonic.Post.SingleOrDefault(x => x.Id == id), "SubSonic ActiveRecord.SingleOrDefault"); + tests.Add(id => + { + idParam.Value = id; - // // Subsonic coding horror - // SubSonic.tempdbDB db = new SubSonic.tempdbDB(); - // tests.Add(id => new SubSonic.Query.CodingHorror(db.Provider, "select * from Posts where Id = @0", id).ExecuteTypedList(), "SubSonic Coding Horror"); - //}, "Subsonic"); + using (var reader = postCommand.ExecuteReader()) + { + reader.Read(); + var post = new Post(); + post.Id = reader.GetInt32(0); + post.Text = reader.GetNullableString(1); + post.CreationDate = reader.GetDateTime(2); + post.LastChangeDate = reader.GetDateTime(3); - //// BLToolkit - doesn't import correctly in the new .csproj world - //var db1 = new DbManager(GetOpenConnection()); - //tests.Add(id => db1.SetCommand("select * from Posts where Id = @id", db1.Parameter("id", id)).ExecuteList(), "BLToolkit"); + post.Counter1 = reader.GetNullableValue(4); + post.Counter2 = reader.GetNullableValue(5); + post.Counter3 = reader.GetNullableValue(6); + post.Counter4 = reader.GetNullableValue(7); + post.Counter5 = reader.GetNullableValue(8); + post.Counter6 = reader.GetNullableValue(9); + post.Counter7 = reader.GetNullableValue(10); + post.Counter8 = reader.GetNullableValue(11); + post.Counter9 = reader.GetNullableValue(12); + } + }, "Hand Coded"); #if !COREFX - var table = new DataTable - { - Columns = + var table = new DataTable + { + Columns = { {"Id", typeof (int)}, {"Text", typeof (string)}, @@ -358,22 +351,41 @@ public async Task RunAsync(int iterations) {"Counter8", typeof (int)}, {"Counter9", typeof (int)}, } - }; - tests.Add(id => - { - idParam.Value = id; - object[] values = new object[13]; - using (var reader = postCommand.ExecuteReader()) + }; + tests.Add(id => { - reader.Read(); - reader.GetValues(values); - table.Rows.Add(values); - } - }, "DataTable via IDataReader.GetValues"); + idParam.Value = id; + object[] values = new object[13]; + using (var reader = postCommand.ExecuteReader()) + { + reader.Read(); + reader.GetValues(values); + table.Rows.Add(values); + } + }, "DataTable via IDataReader.GetValues"); #endif + }, "Hand Coded"); + + // Subsonic isn't maintained anymore - doesn't import correctly + //Try(() => + // { + // // Subsonic ActiveRecord + // tests.Add(id => 3SubSonic.Post.SingleOrDefault(x => x.Id == id), "SubSonic ActiveRecord.SingleOrDefault"); + + // // Subsonic coding horror + // SubSonic.tempdbDB db = new SubSonic.tempdbDB(); + // tests.Add(id => new SubSonic.Query.CodingHorror(db.Provider, "select * from Posts where Id = @0", id).ExecuteTypedList(), "SubSonic Coding Horror"); + //}, "Subsonic"); + + //// BLToolkit - doesn't import correctly in the new .csproj world + //var db1 = new DbManager(GetOpenConnection()); + //tests.Add(id => db1.SetCommand("select * from Posts where Id = @id", db1.Parameter("id", id)).ExecuteList(), "BLToolkit"); + Console.WriteLine(); Console.WriteLine("Running..."); await tests.RunAsync(iterations).ConfigureAwait(false); +#pragma warning restore RCS1121 // Use [] instead of calling 'First'. +#pragma warning restore IDE0017 // Simplify object initialization } } } diff --git a/Dapper.Tests.Performance/Program.cs b/Dapper.Tests.Performance/Program.cs index 56d967492..ef9b491da 100644 --- a/Dapper.Tests.Performance/Program.cs +++ b/Dapper.Tests.Performance/Program.cs @@ -1,37 +1,78 @@ -using System; -using System.Threading.Tasks; +using BenchmarkDotNet.Running; +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using System.Reflection; +using static System.Console; namespace Dapper.Tests.Performance { - // Note: VSTest injects an entry point in .NET Core land...so we have to split this out into - // a separate project...so here we are. - // See https://github.com/Microsoft/vstest/issues/636 for details public static class Program { - public static void Main() + public static void Main(string[] args) { #if DEBUG - var fg = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Warning: DEBUG configuration; performance may be impacted"); - Console.ForegroundColor = fg; - Console.WriteLine(); + WriteLineColor("Warning: DEBUG configuration; performance may be impacted!", ConsoleColor.Red); + WriteLine(); #endif - Console.WriteLine("Using ConnectionString: " + PerformanceTests.ConnectionString); + WriteLine("Welcome to Dapper's ORM performance benchmark suite, based on BenchmarkDotNet."); + Write(" If you find a problem, please report it at: "); + WriteLineColor("https://github.com/StackExchange/Dapper", ConsoleColor.Blue); + WriteLine(" Or if you're up to it, please submit a pull request! We welcome new additions."); + WriteLine(); + if (args.Length == 0) + { + WriteLine("Optional arguments:"); + WriteColor(" --all", ConsoleColor.Blue); + WriteLine(": run all benchmarks"); + WriteColor(" --legacy", ConsoleColor.Blue); + WriteLine(": run the legacy benchmark suite/format", ConsoleColor.Gray); + WriteLine(); + } + WriteLine("Using ConnectionString: " + BenchmarkBase.ConnectionString); EnsureDBSetup(); - RunPerformanceTestsAsync().GetAwaiter().GetResult(); + WriteLine("Database setup complete."); + + if (args.Any(a => a == "--all")) + { + WriteLine("Iterations: " + BenchmarkBase.Iterations); + var benchmarks = new List(); + var benchTypes = Assembly.GetEntryAssembly().DefinedTypes.Where(t => t.IsSubclassOf(typeof(BenchmarkBase))); + WriteLineColor("Running full benchmarks suite", ConsoleColor.Green); + foreach (var b in benchTypes) + { + benchmarks.AddRange(BenchmarkConverter.TypeToBenchmarks(b)); + } + BenchmarkRunner.Run(benchmarks.ToArray(), null); + } + else if (args.Any(a => a == "--legacy")) + { + var test = new LegacyTests(); + const int iterations = 500; + WriteLineColor($"Running legacy benchmarks: {iterations} iterations that load up a Post entity.", ConsoleColor.Green); + test.RunAsync(iterations).GetAwaiter().GetResult(); + WriteLine(); + WriteLineColor("Run complete.", ConsoleColor.Green); + } + else + { + WriteLine("Iterations: " + BenchmarkBase.Iterations); + BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly).Run(args); + } } private static void EnsureDBSetup() { - using (var cnn = PerformanceTests.GetOpenConnection()) + using (var cnn = new SqlConnection(BenchmarkBase.ConnectionString)) { + cnn.Open(); var cmd = cnn.CreateCommand(); cmd.CommandText = @" -if (OBJECT_ID('Posts') is null) -begin - create table Posts +If (Object_Id('Posts') Is Null) +Begin + Create Table Posts ( Id int identity primary key, [Text] varchar(max) not null, @@ -46,38 +87,36 @@ [Text] varchar(max) not null, Counter7 int, Counter8 int, Counter9 int - ) + ); - set nocount on - - declare @i int - declare @c int - - declare @id int + Set NoCount On; + Declare @i int = 0; - set @i = 0 - - while @i <= 5001 - begin - - insert Posts ([Text],CreationDate, LastChangeDate) values (replicate('x', 2000), GETDATE(), GETDATE()) - set @id = @@IDENTITY - - set @i = @i + 1 - end -end + While @i <= 5001 + Begin + Insert Posts ([Text],CreationDate, LastChangeDate) values (replicate('x', 2000), GETDATE(), GETDATE()); + Set @i = @i + 1; + End +End "; cmd.Connection = cnn; cmd.ExecuteNonQuery(); } } - private static async Task RunPerformanceTestsAsync() + public static void WriteLineColor(string message, ConsoleColor color) + { + var orig = ForegroundColor; + ForegroundColor = color; + WriteLine(message); + ForegroundColor = orig; + } + public static void WriteColor(string message, ConsoleColor color) { - var test = new PerformanceTests(); - const int iterations = 500; - Console.WriteLine("Running {0} iterations that load up a post entity", iterations); - await test.RunAsync(iterations).ConfigureAwait(false); + var orig = ForegroundColor; + ForegroundColor = color; + Write(message); + ForegroundColor = orig; } } } diff --git a/Dapper.Tests.Performance/Soma/SomaConfig.cs b/Dapper.Tests.Performance/Soma/SomaConfig.cs index 4eede2ae8..aa2dbcdd6 100644 --- a/Dapper.Tests.Performance/Soma/SomaConfig.cs +++ b/Dapper.Tests.Performance/Soma/SomaConfig.cs @@ -5,7 +5,7 @@ namespace Dapper.Tests.Performance.Soma { internal class SomaConfig : MsSqlConfig { - public override string ConnectionString => PerformanceTests.ConnectionString; + public override string ConnectionString => BenchmarkBase.ConnectionString; public override Action Logger => noOp;