From 1a6986cfcfac4610678077e07f39dcb5b5cea8dc Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 25 Oct 2016 12:34:06 +0200 Subject: [PATCH] U4-9111 Benchmark BulkCopy changes from U4-9107 --- src/Umbraco.Core/Properties/AssemblyInfo.cs | 1 + src/Umbraco.Tests.Benchmarks/App.config | 48 ++++ .../BulkInsertBenchmarks.cs | 210 ++++++++++++++++++ src/Umbraco.Tests.Benchmarks/Program.cs | 19 ++ .../Properties/AssemblyInfo.cs | 36 +++ .../TraceEvent.ReleaseNotes.txt | 61 +++++ .../Umbraco.Tests.Benchmarks.csproj | 149 +++++++++++++ src/Umbraco.Tests.Benchmarks/packages.config | 25 +++ src/umbraco.sln | 9 +- 9 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Tests.Benchmarks/App.config create mode 100644 src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs create mode 100644 src/Umbraco.Tests.Benchmarks/Program.cs create mode 100644 src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs create mode 100644 src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt create mode 100644 src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj create mode 100644 src/Umbraco.Tests.Benchmarks/packages.config diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index d1ddadde37..dbc1ab6c93 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -32,6 +32,7 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("umbraco.editorControls")] [assembly: InternalsVisibleTo("Umbraco.Tests")] +[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")] [assembly: InternalsVisibleTo("Umbraco.Core")] [assembly: InternalsVisibleTo("Umbraco.Web")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] diff --git a/src/Umbraco.Tests.Benchmarks/App.config b/src/Umbraco.Tests.Benchmarks/App.config new file mode 100644 index 0000000000..a988966f23 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/App.config @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs new file mode 100644 index 0000000000..5212a4f524 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlServerCe; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Xml; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Diagnostics.Windows; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Validators; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Migrations.Initial; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Tests.TestHelpers; +using ILogger = Umbraco.Core.Logging.ILogger; + +namespace Umbraco.Tests.Benchmarks +{ + [Config(typeof(Config))] + public class BulkInsertBenchmarks + { + private class Config : ManualConfig + { + public Config() + { + Add(new MemoryDiagnoser()); + //Add(ExecutionValidator.FailOnError); + + //The 'quick and dirty' settings, so it runs a little quicker + // see benchmarkdotnet FAQ + Add(Job.Default + .WithLaunchCount(1) // benchmark process will be launched only once + .WithIterationTime(100) // 100ms per iteration + .WithWarmupCount(3) // 3 warmup iteration + .WithTargetCount(3)); // 3 target iteration + } + } + + private static byte[] _initDbBytes = null; + + [Setup] + public void Setup() + { + var logger = new DebugDiagnosticsLogger(); + var path = TestHelper.CurrentAssemblyDirectory; + + _sqlCeSyntax = new SqlCeSyntaxProvider(); + _sqlServerSyntax = new SqlServerSyntaxProvider(); + + SetupSqlCe(path, logger); + SetupSqlServer(logger); + + + } + + private void SetupSqlServer(ILogger logger) + { + //create the db + _dbSqlServer = new UmbracoDatabase( + "server=.\\SQLExpress;database=YOURDB;user id=YOURUSER;password=YOURPASS", + Constants.DatabaseProviders.SqlServer, + logger); + + //drop the table + _dbSqlServer.Execute("DROP TABLE [umbracoServer]"); + + //re-create it + _dbSqlServer.Execute(@"CREATE TABLE [umbracoServer]( + [id] [int] IDENTITY(1,1) NOT NULL, + [address] [nvarchar](500) NOT NULL, + [computerName] [nvarchar](255) NOT NULL, + [registeredDate] [datetime] NOT NULL CONSTRAINT [DF_umbracoServer_registeredDate] DEFAULT (getdate()), + [lastNotifiedDate] [datetime] NOT NULL, + [isActive] [bit] NOT NULL, + [isMaster] [bit] NOT NULL, + CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +)"); + } + + private void SetupSqlCe(string path, ILogger logger) + { + var dbName = string.Concat("Umb", Guid.NewGuid(), ".sdf"); + AppDomain.CurrentDomain.SetData("DataDirectory", path); + var sqlCeConnectionString = $"Datasource=|DataDirectory|\\{dbName};Flush Interval=1;"; + + _dbFile = Path.Combine(path, dbName); + + //only create the db one time + if (_initDbBytes == null) + { + using (var engine = new SqlCeEngine(sqlCeConnectionString)) + { + engine.CreateDatabase(); + } + + //use the db to create the initial schema so we can reuse in each bench + using (_dbSqlCe = new UmbracoDatabase( + sqlCeConnectionString, + Constants.DatabaseProviders.SqlCe, + logger)) + { + var creation = new DatabaseSchemaCreation(_dbSqlCe, logger, _sqlCeSyntax); + creation.InitializeDatabaseSchema(); + } + _initDbBytes = File.ReadAllBytes(_dbFile); + } + else + { + File.WriteAllBytes(_dbFile, _initDbBytes); + } + + //create the db + _dbSqlCe = new UmbracoDatabase( + sqlCeConnectionString, + Constants.DatabaseProviders.SqlCe, + logger); + } + + private List GetData() + { + var data = new List(); + for (var i = 0; i < 1000; i++) + { + data.Add(new ServerRegistrationDto + { + ServerAddress = "address" + Guid.NewGuid(), + ServerIdentity = "computer" + Guid.NewGuid(), + DateRegistered = DateTime.Now, + IsActive = true, + DateAccessed = DateTime.Now + }); + } + return data; + } + + [Cleanup] + public void Cleanup() + { + _dbSqlCe.Dispose(); + _dbSqlServer.Dispose(); + File.Delete(_dbFile); + } + + private string _dbFile; + private UmbracoDatabase _dbSqlCe; + private UmbracoDatabase _dbSqlServer; + private ISqlSyntaxProvider _sqlCeSyntax; + private ISqlSyntaxProvider _sqlServerSyntax; + + /// + /// Tests updating the existing XML way + /// + [Benchmark(Baseline = true)] + public void SqlCeOneByOne() + { + using (var tr = _dbSqlCe.GetTransaction()) + { + _dbSqlCe.BulkInsertRecords(GetData(), tr, _sqlCeSyntax, useNativeSqlPlatformBulkInsert: false); + tr.Complete(); + } + } + + /// + /// Tests updating with only the object graph + /// + [Benchmark] + public void SqlCeTableDirect() + { + using (var tr = _dbSqlCe.GetTransaction()) + { + _dbSqlCe.BulkInsertRecords(GetData(), tr, _sqlCeSyntax, useNativeSqlPlatformBulkInsert: true); + tr.Complete(); + } + } + + [Benchmark] + public void SqlServerBulkInsertStatements() + { + using (var tr = _dbSqlServer.GetTransaction()) + { + _dbSqlServer.BulkInsertRecords(GetData(), tr, _sqlServerSyntax, useNativeSqlPlatformBulkInsert: false); + tr.Complete(); + } + } + + [Benchmark] + public void SqlServerBulkCopy() + { + using (var tr = _dbSqlServer.GetTransaction()) + { + _dbSqlServer.BulkInsertRecords(GetData(), tr, _sqlServerSyntax, useNativeSqlPlatformBulkInsert: true); + tr.Complete(); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs new file mode 100644 index 0000000000..37c1ccd853 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Running; + +namespace Umbraco.Tests.Benchmarks +{ + class Program + { + static void Main(string[] args) + { + var summary = BenchmarkRunner.Run(); + + Console.ReadLine(); + } + } +} diff --git a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..fb00b8d4e9 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Umbraco.Tests.Benchmarks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Umbraco.Tests.Benchmarks")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("86deb346-089f-4106-89c8-d852b9cf2a33")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt b/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt new file mode 100644 index 0000000000..21fcb5d0ca --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt @@ -0,0 +1,61 @@ +Version 1.0.0.3 - Initial release to NuGet, pre-release. + + TraceEvent has been available from the site http://bcl.codeplex.com/wikipage?title=TraceEvent for some time now + this NuGet Version of the library supersedes that one. WHile the 'core' part of the library is unchanged, + we did change lesser used features, and change the namespace and DLL name, which will cause break. We anticipate + it will take an hour or so to 'port' to this version from the old one. Below are specific details on what + has changed to help in this port. + + * The DLL has been renamed from TraceEvent.dll to Microsoft.Diagnostics.Tracing.TraceEvent.dll + * The name spaces for all classes have been changed. The easiest way to port is to simply place + the following using clauses at the top of any file that uses TraceEvent classes + using Microsoft.Diagnostics.Symbols; + using Microsoft.Diagnostics.Tracing; + using Microsoft.Diagnostics.Tracing.Etlx; + using Microsoft.Diagnostics.Tracing.Parsers.Clr; + using Microsoft.Diagnostics.Tracing.Parsers.Kernel; + using Microsoft.Diagnostics.Tracing.Session; + using Microsoft.Diagnostics.Tracing.Stacks; + * Any method with the name RelMSec in it has been changed to be RelativeMSec. The easiest port is to + simply globally rename RelMSec to RelativeMSec + * Any property in the Trace* classes that has the form Max*Index has been renamed to Count. + * A number of methods have been declared obsolete, these are mostly renames and the warning will tell you + how to update them. + * The following classes have been rename + SymPath -> SymbolPath + SymPathElement -> SymbolPathElement + SymbolReaderFlags -> SymbolReaderOptions + * TraceEventSession is now StopOnDispose (it will stop the session when TraceEventSesssion dies), by default + If you were relying on the kernel session living past the process that started it, you must now set + the StopOnDispose explicitly + * There used to be XmlAttrib extensions methods on StringBuilder for use in manifest generated TraceEventParsers + These have been moved to protected members of TraceEvent. The result is that in stead of writing + sb.XmlAttrib(...) you write XmlAttrib(sb, ...) + * References to Pdb in names have been replaced with 'Symbol' to conform to naming guidelines. + + *********************************************************************************************** +Version 1.0.0.4 - Initial stable release + + Mostly this was insuring that the library was cleaned up in preparation + for release the TraceParserGen tool + + Improved the docs, removed old code, fixed some naming convention stuff + + * Additional changes from the PreRelease copy to the first Stable release + + * The arguments to AddCallbackForProviderEvent were reversed!!!! (now provider than event) + * The arguments to Observe(string, string)!!!! (now provider than event) + * Event names for these APIs must include a / between the Task and Opcode names + + * Many Events in KernelTraceEventParser were harmonized to be consistent with other conventions + * Events of the form PageFault* were typically renamed to Memory* + * The 'End' suffix was renamed to 'Stop' (its official name) + * PerfInfoSampleProf -> PerfInfoSample + * PerfInfoSampleProf -> PerfInfoSample + * ReadyThread -> DispatcherReadyThread + * StackWalkTraceData -> StackWalkStackTraceData + * FileIo -> FileIO + * DiskIo -> DiskIO + + * Many Events in SymbolTraceEventParser were harmonized to be consistent with other conventions + * names with Symbol -> ImageID diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj new file mode 100644 index 0000000000..66033ba08e --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -0,0 +1,149 @@ + + + + + Debug + AnyCPU + {86DEB346-089F-4106-89C8-D852B9CF2A33} + Exe + Properties + Umbraco.Tests.Benchmarks + Umbraco.Tests.Benchmarks + v4.5 + 512 + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + Always + + + + ..\packages\BenchmarkDotNet.0.9.9\lib\net45\BenchmarkDotNet.dll + True + + + ..\packages\BenchmarkDotNet.Core.0.9.9\lib\net45\BenchmarkDotNet.Core.dll + True + + + ..\packages\BenchmarkDotNet.Diagnostics.Windows.0.9.9\lib\net45\BenchmarkDotNet.Diagnostics.Windows.dll + True + + + ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll + True + + + ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll + True + + + ..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll + True + + + ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll + True + + + + ..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll + True + + + + ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll + True + + + ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll + True + + + + ..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll + True + + + ..\packages\System.Threading.Tasks.Extensions.4.0.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + Umbraco.Core + + + {5d3b8245-ada6-453f-a008-50ed04bfe770} + Umbraco.Tests + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /I /C /D +xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D + + + \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/packages.config b/src/Umbraco.Tests.Benchmarks/packages.config new file mode 100644 index 0000000000..c4d2ba1df2 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/packages.config @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/umbraco.sln b/src/umbraco.sln index f9267c0412..8648f6ed5c 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}" EndProject @@ -114,6 +114,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5B03EF4E ..\build\NuSpecs\build\UmbracoCms.targets = ..\build\NuSpecs\build\UmbracoCms.targets EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{86DEB346-089F-4106-89C8-D852B9CF2A33}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -178,6 +180,10 @@ Global {07FBC26B-2927-4A22-8D96-D644C667FECC}.Debug|Any CPU.Build.0 = Debug|Any CPU {07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.ActiveCfg = Release|Any CPU {07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.Build.0 = Release|Any CPU + {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -187,5 +193,6 @@ Global {5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} {5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} + {86DEB346-089F-4106-89C8-D852B9CF2A33} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} EndGlobalSection EndGlobal