فهرست منبع

Mono - Add Plat Updates Query + improvements + tweaks (#3789)

* Mono - Add Plat Updates + db tweaks

* Mono Naming

* fix url
Ben Adams 7 سال پیش
والد
کامیت
72a0d1ffff
58فایلهای تغییر یافته به همراه542 افزوده شده و 321 حذف شده
  1. 0 0
      frameworks/CSharp/aspnet-mono-ngx/README.md
  2. 0 0
      frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-jn.dockerfile
  3. 0 0
      frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-my-ef.dockerfile
  4. 0 0
      frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-my.dockerfile
  5. 0 0
      frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-ss.dockerfile
  6. 0 0
      frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx.dockerfile
  7. 10 12
      frameworks/CSharp/aspnet-mono-ngx/benchmark_config.json
  8. 0 0
      frameworks/CSharp/aspnet-mono-ngx/nginx.conf
  9. 0 0
      frameworks/CSharp/aspnet-mono-ngx/run.sh
  10. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Application.cs
  11. 55 55
      frameworks/CSharp/aspnet-mono-ngx/src/Benchmarks.build.proj
  12. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Benchmarks.csproj
  13. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Benchmarks.sln
  14. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Controllers/AdoController.cs
  15. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Controllers/EntityFrameworkController.cs
  16. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Controllers/HomeController.cs
  17. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Controllers/JsonController.cs
  18. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Controllers/MongoDBController.cs
  19. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Controllers/PlaintextController.cs
  20. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Models/EntityFramework.cs
  21. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Models/Fortune.cs
  22. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Models/MongoDB.cs
  23. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Models/World.cs
  24. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Properties/PublishProfiles/IIS.pubxml
  25. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Views/Fortunes.cshtml
  26. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Views/Index.cshtml
  27. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Views/Web.config
  28. 0 0
      frameworks/CSharp/aspnet-mono-ngx/src/Web.config
  29. 14 14
      frameworks/CSharp/aspnet-mono-ngx/src/packages.config
  30. 5 8
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleQueriesController.cs
  31. 5 8
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleUpdatesController.cs
  32. 4 8
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/SingleQueryController.cs
  33. 38 5
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/BatchUpdateString.cs
  34. 4 4
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/EfDb.cs
  35. 0 19
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IDb.cs
  36. 0 10
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IRandom.cs
  37. 15 3
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Random.cs
  38. 38 40
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/RawDb.cs
  39. 59 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/StringBuilderCache.cs
  40. 9 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/World.cs
  41. 4 4
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesEfMiddleware.cs
  42. 4 4
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesRawMiddleware.cs
  43. 7 5
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MiddlewareHelpers.cs
  44. 6 1
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Startup.cs
  45. 1 1
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Views/Fortunes/Fortunes.cshtml
  46. 47 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Updates.cs
  47. 37 19
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.cs
  48. 39 6
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/BatchUpdateString.cs
  49. 0 19
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/IDb.cs
  50. 0 10
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/IRandom.cs
  51. 15 3
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Random.cs
  52. 33 36
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/RawDb.cs
  53. 5 2
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/World.cs
  54. 4 2
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Program.cs
  55. 59 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/StringBuilderCache.cs
  56. 20 18
      frameworks/CSharp/aspnetcore-mono/benchmark_config.json
  57. 5 5
      frameworks/CSharp/nancy/benchmark_config.json
  58. 0 0
      frameworks/CSharp/nancy/nancy-mono.dockerfile

+ 0 - 0
frameworks/CSharp/aspnet/README.md → frameworks/CSharp/aspnet-mono-ngx/README.md


+ 0 - 0
frameworks/CSharp/aspnet/aspnet-mono-jsonnet.dockerfile → frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-jn.dockerfile


+ 0 - 0
frameworks/CSharp/aspnet/aspnet-mono-mysql-entityframework.dockerfile → frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-my-ef.dockerfile


+ 0 - 0
frameworks/CSharp/aspnet/aspnet-mono-mysql-raw.dockerfile → frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-my.dockerfile


+ 0 - 0
frameworks/CSharp/aspnet/aspnet-mono-servicestack.dockerfile → frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx-ss.dockerfile


+ 0 - 0
frameworks/CSharp/aspnet/aspnet.dockerfile → frameworks/CSharp/aspnet-mono-ngx/aspnet-mono-ngx.dockerfile


+ 10 - 12
frameworks/CSharp/aspnet/benchmark_config.json → frameworks/CSharp/aspnet-mono-ngx/benchmark_config.json

@@ -1,5 +1,5 @@
 {
-  "framework": "aspnet",
+  "framework": "aspnet-mono-ngx",
   "tests": [{
     "default": {
       "json_url": "/json/default",
@@ -16,11 +16,11 @@
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "ASP.NET",
+      "display_name": "ASP.NET MVC on Mono via NGINX FastCGI",
       "notes": "",
       "versus": "aspnet-mono"
     },
-    "mono-jsonnet": {
+    "jn": {
       "json_url": "/json/jsonnet",
       "port": 8080,
       "approach": "Realistic",
@@ -34,11 +34,11 @@
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "JSONNet",
+      "display_name": "Json.NET, ASP.NET MVC on Mono via NGINX FastCGI",
       "notes": "",
       "versus": "aspnet-mono"
     },
-    "mono-servicestack": {
+    "ss": {
       "json_url": "/json/servicestack",
       "port": 8080,
       "approach": "Realistic",
@@ -52,13 +52,11 @@
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "ServiceStack",
+      "display_name": "ServiceStack, ASP.NET MVC on Mono via NGINX FastCGI",
       "notes": "",
       "versus": "aspnet-mono"
     },
-    "mono-mysql-raw": {
-      "json_url": "/json/default",
-      "plaintext_url": "/plaintext",
+    "my": {
       "db_url": "/ado/mysql",
       "query_url": "/ado/mysql?queries=",
       "fortune_url": "/ado/mysql/fortunes",
@@ -75,11 +73,11 @@
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "ASP.NET-raw",
+      "display_name": "ADO, ASP.NET MVC on Mono via NGINX FastCGI",
       "notes": "",
       "versus": "aspnet"
     },
-    "mono-mysql-entityframework": {
+    "my-ef": {
       "db_url": "/entityframework/mysql",
       "query_url": "/entityframework/mysql?queries=",
       "fortune_url": "/entityframework/mysql/fortunes",
@@ -96,7 +94,7 @@
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "EntityFramework",
+      "display_name": "EntityFramework, ASP.NET MVC on Mono via NGINX FastCGI",
       "notes": "Entity framework",
       "versus": "aspnet"
     }

+ 0 - 0
frameworks/CSharp/aspnet/nginx.conf → frameworks/CSharp/aspnet-mono-ngx/nginx.conf


+ 0 - 0
frameworks/CSharp/aspnet/run.sh → frameworks/CSharp/aspnet-mono-ngx/run.sh


+ 0 - 0
frameworks/CSharp/aspnet/src/Application.cs → frameworks/CSharp/aspnet-mono-ngx/src/Application.cs


+ 55 - 55
frameworks/CSharp/aspnet/src/Benchmarks.build.proj → frameworks/CSharp/aspnet-mono-ngx/src/Benchmarks.build.proj

@@ -1,56 +1,56 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-
-  <PropertyGroup>
-    <SolutionDir>$(MSBuildThisFileDirectory)</SolutionDir>
-    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
-    <NuGetExe Condition=" '$(NuGetExe)'=='' ">packages/nuget.exe</NuGetExe>
-    <NuGetExeDir>packages/</NuGetExeDir>
-    <NuGetDownloadAddress Condition=" '$(NuGetDownloadAddress)'=='' ">http://nuget.org/nuget.exe</NuGetDownloadAddress>
-    <NuGetCommand Condition=" '$(NuGetCommand)'=='' AND '$(OS)' == 'Windows_NT'">"$(NuGetExe)"</NuGetCommand>
-    <NuGetCommand Condition=" '$(NuGetCommand)'=='' AND '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExe)"</NuGetCommand>
-    <Properties>Configuration=$(Configuration);SolutionDir=$(SolutionDir)</Properties>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Solution Include="*.sln" />
-  </ItemGroup>
-
-  <UsingTask TaskName="DownloadFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" Condition=" '$(OS)' == 'Windows_NT' ">
-    <ParameterGroup>
-      <Address ParameterType="System.String" Required="true"/>
-      <OutputFilename ParameterType="System.String" Required="true" />
-    </ParameterGroup>
-    <Task>
-      <Reference Include="System" />
-      <Code Type="Fragment" Language="cs">
-        <![CDATA[
-            new System.Net.WebClient().DownloadFile(Address, OutputFilename);
-        ]]>
-      </Code>
-    </Task>
-  </UsingTask>
-
-  <Target Name="DownloadNuGet">
-    <MakeDir Directories="$(NuGetExeDir)" Condition=" !Exists('$(NuGetExeDir)') " />
-    <DownloadFile Address="$(NuGetDownloadAddress)" OutputFilename="$(NuGetExe)" Condition=" '$(OS)' == 'Windows_NT' AND !Exists('$(NuGetExe)')" />
-    <Exec Command="wget $(NuGetDownloadAddress) -O $(NuGetExe)" Condition=" '$(OS)' != 'Windows_NT' AND !Exists('$(NuGetExe)') " />
-  </Target>
-
-  <Target Name="RestorePackages" DependsOnTargets="DownloadNuGet">
-    <Exec Command="$(NuGetCommand) restore &quot;%(Solution.Identity)&quot;" />
-  </Target>
-
-  <Target Name="Clean">
-    <MSBuild Targets="Clean" Projects="@(Solution)" Properties="$(Properties)" />
-  </Target>
-
-  <Target Name="Build" DependsOnTargets="RestorePackages">
-    <MSBuild Targets="Build" Projects="@(Solution)" Properties="$(Properties)" />
-  </Target>
-
-  <Target Name="Rebuild" DependsOnTargets="RestorePackages">
-    <MSBuild Targets="Rebuild" Projects="@(Solution)" Properties="$(Properties)" />
-  </Target>
-
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+  <PropertyGroup>
+    <SolutionDir>$(MSBuildThisFileDirectory)</SolutionDir>
+    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
+    <NuGetExe Condition=" '$(NuGetExe)'=='' ">packages/nuget.exe</NuGetExe>
+    <NuGetExeDir>packages/</NuGetExeDir>
+    <NuGetDownloadAddress Condition=" '$(NuGetDownloadAddress)'=='' ">http://nuget.org/nuget.exe</NuGetDownloadAddress>
+    <NuGetCommand Condition=" '$(NuGetCommand)'=='' AND '$(OS)' == 'Windows_NT'">"$(NuGetExe)"</NuGetCommand>
+    <NuGetCommand Condition=" '$(NuGetCommand)'=='' AND '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExe)"</NuGetCommand>
+    <Properties>Configuration=$(Configuration);SolutionDir=$(SolutionDir)</Properties>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Solution Include="*.sln" />
+  </ItemGroup>
+
+  <UsingTask TaskName="DownloadFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" Condition=" '$(OS)' == 'Windows_NT' ">
+    <ParameterGroup>
+      <Address ParameterType="System.String" Required="true"/>
+      <OutputFilename ParameterType="System.String" Required="true" />
+    </ParameterGroup>
+    <Task>
+      <Reference Include="System" />
+      <Code Type="Fragment" Language="cs">
+        <![CDATA[
+            new System.Net.WebClient().DownloadFile(Address, OutputFilename);
+        ]]>
+      </Code>
+    </Task>
+  </UsingTask>
+
+  <Target Name="DownloadNuGet">
+    <MakeDir Directories="$(NuGetExeDir)" Condition=" !Exists('$(NuGetExeDir)') " />
+    <DownloadFile Address="$(NuGetDownloadAddress)" OutputFilename="$(NuGetExe)" Condition=" '$(OS)' == 'Windows_NT' AND !Exists('$(NuGetExe)')" />
+    <Exec Command="wget $(NuGetDownloadAddress) -O $(NuGetExe)" Condition=" '$(OS)' != 'Windows_NT' AND !Exists('$(NuGetExe)') " />
+  </Target>
+
+  <Target Name="RestorePackages" DependsOnTargets="DownloadNuGet">
+    <Exec Command="$(NuGetCommand) restore &quot;%(Solution.Identity)&quot;" />
+  </Target>
+
+  <Target Name="Clean">
+    <MSBuild Targets="Clean" Projects="@(Solution)" Properties="$(Properties)" />
+  </Target>
+
+  <Target Name="Build" DependsOnTargets="RestorePackages">
+    <MSBuild Targets="Build" Projects="@(Solution)" Properties="$(Properties)" />
+  </Target>
+
+  <Target Name="Rebuild" DependsOnTargets="RestorePackages">
+    <MSBuild Targets="Rebuild" Projects="@(Solution)" Properties="$(Properties)" />
+  </Target>
+
 </Project>

+ 0 - 0
frameworks/CSharp/aspnet/src/Benchmarks.csproj → frameworks/CSharp/aspnet-mono-ngx/src/Benchmarks.csproj


+ 0 - 0
frameworks/CSharp/aspnet/src/Benchmarks.sln → frameworks/CSharp/aspnet-mono-ngx/src/Benchmarks.sln


+ 0 - 0
frameworks/CSharp/aspnet/src/Controllers/AdoController.cs → frameworks/CSharp/aspnet-mono-ngx/src/Controllers/AdoController.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Controllers/EntityFrameworkController.cs → frameworks/CSharp/aspnet-mono-ngx/src/Controllers/EntityFrameworkController.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Controllers/HomeController.cs → frameworks/CSharp/aspnet-mono-ngx/src/Controllers/HomeController.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Controllers/JsonController.cs → frameworks/CSharp/aspnet-mono-ngx/src/Controllers/JsonController.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Controllers/MongoDBController.cs → frameworks/CSharp/aspnet-mono-ngx/src/Controllers/MongoDBController.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Controllers/PlaintextController.cs → frameworks/CSharp/aspnet-mono-ngx/src/Controllers/PlaintextController.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Models/EntityFramework.cs → frameworks/CSharp/aspnet-mono-ngx/src/Models/EntityFramework.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Models/Fortune.cs → frameworks/CSharp/aspnet-mono-ngx/src/Models/Fortune.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Models/MongoDB.cs → frameworks/CSharp/aspnet-mono-ngx/src/Models/MongoDB.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Models/World.cs → frameworks/CSharp/aspnet-mono-ngx/src/Models/World.cs


+ 0 - 0
frameworks/CSharp/aspnet/src/Properties/PublishProfiles/IIS.pubxml → frameworks/CSharp/aspnet-mono-ngx/src/Properties/PublishProfiles/IIS.pubxml


+ 0 - 0
frameworks/CSharp/aspnet/src/Views/Fortunes.cshtml → frameworks/CSharp/aspnet-mono-ngx/src/Views/Fortunes.cshtml


+ 0 - 0
frameworks/CSharp/aspnet/src/Views/Index.cshtml → frameworks/CSharp/aspnet-mono-ngx/src/Views/Index.cshtml


+ 0 - 0
frameworks/CSharp/aspnet/src/Views/Web.config → frameworks/CSharp/aspnet-mono-ngx/src/Views/Web.config


+ 0 - 0
frameworks/CSharp/aspnet/src/Web.config → frameworks/CSharp/aspnet-mono-ngx/src/Web.config


+ 14 - 14
frameworks/CSharp/aspnet/src/packages.config → frameworks/CSharp/aspnet-mono-ngx/src/packages.config

@@ -1,15 +1,15 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="EntityFramework" version="6.1.1" targetFramework="net45" />
-  <package id="Microsoft.AspNet.Mvc" version="5.2.2" targetFramework="net45" />
-  <package id="Microsoft.AspNet.Razor" version="3.2.2" targetFramework="net45" />
-  <package id="Microsoft.AspNet.WebPages" version="3.2.2" targetFramework="net45" />
-  <package id="MSBuild.Microsoft.VisualStudio.Web.targets" version="11.0.2.1" />
-  <package id="mongocsharpdriver" version="1.9.2" targetFramework="net45" />
-  <package id="MySql.Data" version="6.9.5" targetFramework="net45" />
-  <package id="MySql.Data.Entity" version="6.9.5" targetFramework="net45" />
-  <package id="Npgsql" version="2.2.3" targetFramework="net45" />
-  <package id="Npgsql.EntityFramework" version="2.2.3" targetFramework="net45" />
-  <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net45" />
-  <package id="ServiceStack.Text" version="4.0.34" targetFramework="net45" />
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="EntityFramework" version="6.1.1" targetFramework="net45" />
+  <package id="Microsoft.AspNet.Mvc" version="5.2.2" targetFramework="net45" />
+  <package id="Microsoft.AspNet.Razor" version="3.2.2" targetFramework="net45" />
+  <package id="Microsoft.AspNet.WebPages" version="3.2.2" targetFramework="net45" />
+  <package id="MSBuild.Microsoft.VisualStudio.Web.targets" version="11.0.2.1" />
+  <package id="mongocsharpdriver" version="1.9.2" targetFramework="net45" />
+  <package id="MySql.Data" version="6.9.5" targetFramework="net45" />
+  <package id="MySql.Data.Entity" version="6.9.5" targetFramework="net45" />
+  <package id="Npgsql" version="2.2.3" targetFramework="net45" />
+  <package id="Npgsql.EntityFramework" version="2.2.3" targetFramework="net45" />
+  <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net45" />
+  <package id="ServiceStack.Text" version="4.0.34" targetFramework="net45" />
 </packages>

+ 5 - 8
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleQueriesController.cs

@@ -13,22 +13,19 @@ namespace Benchmarks.Controllers
     {
         [HttpGet("raw")]
         [Produces("application/json")]
-        public Task<World[]> Raw(int queries = 1)
+        public Task<WorldRaw[]> Raw(int queries = 1)
         {
-            return ExecuteQuery<RawDb>(queries);
+            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
+            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
+            return db.LoadMultipleQueriesRows(queries);
         }
 
         [HttpGet("ef")]
         [Produces("application/json")]
         public Task<World[]> Ef(int queries = 1)
-        {
-            return ExecuteQuery<EfDb>(queries);
-        }
-
-        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
         {
             queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
-            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
             return db.LoadMultipleQueriesRows(queries);
         }
     }

+ 5 - 8
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleUpdatesController.cs

@@ -13,22 +13,19 @@ namespace Benchmarks.Controllers
     {
         [HttpGet("raw")]
         [Produces("application/json")]
-        public Task<World[]> Raw(int queries = 1)
+        public Task<WorldRaw[]> Raw(int queries = 1)
         {
-            return ExecuteQuery<RawDb>(queries);
+            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
+            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
+            return db.LoadMultipleUpdatesRows(queries);
         }
 
         [HttpGet("ef")]
         [Produces("application/json")]
         public Task<World[]> Ef(int queries = 1)
-        {
-            return ExecuteQuery<EfDb>(queries);
-        }
-
-        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
         {
             queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
-            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
             return db.LoadMultipleUpdatesRows(queries);
         }
     }

+ 4 - 8
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/SingleQueryController.cs

@@ -13,21 +13,17 @@ namespace Benchmarks.Controllers
     {
         [HttpGet("raw")]
         [Produces("application/json")]
-        public Task<World> Raw()
+        public Task<WorldRaw> Raw()
         {
-            return ExecuteQuery<RawDb>();
+            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
+            return db.LoadSingleQueryRow();
         }
 
         [HttpGet("ef")]
         [Produces("application/json")]
         public Task<World> Ef()
         {
-            return ExecuteQuery<EfDb>();
-        }
-
-        private Task<World> ExecuteQuery<T>() where T : IDb
-        {
-            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
             return db.LoadSingleQueryRow();
         }
     }

+ 38 - 5
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/BatchUpdateString.cs

@@ -3,22 +3,55 @@
 
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 
 namespace Benchmarks.Data
 {
     internal class BatchUpdateString
     {
-        public static IList<BatchUpdateString> Strings { get;} = 
-            Enumerable.Range(0, 500)
+        private const int MaxBatch = 500;
+        private static string[] _queries = new string[MaxBatch];
+        
+        public static IList<BatchUpdateString> Strings { get; } =
+            Enumerable.Range(0, MaxBatch)
                       .Select(i => new BatchUpdateString
                       {
                           Id = $"Id_{i}",
                           Random = $"Random_{i}",
-                          UpdateQuery = $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"
+                          BatchSize = i
                       }).ToArray();
-                        
+
+        private int BatchSize { get; set; }
         public string Id { get; set; }
         public string Random { get; set; }
-        public string UpdateQuery { get; set; }
+        public string UpdateQuery => _queries[BatchSize] ?? CreateQuery(BatchSize);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private string CreateQuery(int batchSize)
+        {
+            var sb = StringBuilderCache.Acquire();
+            foreach (var q in Enumerable.Range(0, batchSize + 1)
+                .Select(i => $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"))
+            {
+                sb.Append(q);
+            }
+            var query = sb.ToString();
+            _queries[batchSize] = query;
+            return query;
+        }
+
+        public static void Initalize()
+        {
+            Observe(Strings[0].UpdateQuery);
+            Observe(Strings[4].UpdateQuery);
+            Observe(Strings[9].UpdateQuery);
+            Observe(Strings[14].UpdateQuery);
+            Observe(Strings[19].UpdateQuery);
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static void Observe(string query)
+        {
+        }
     }
 }

+ 4 - 4
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/EfDb.cs

@@ -12,13 +12,13 @@ using Microsoft.Extensions.Options;
 
 namespace Benchmarks.Data
 {
-    public class EfDb : IDb
+    public class EfDb
     {
-        private readonly IRandom _random;
+        private readonly ConcurrentRandom _random;
         private readonly ApplicationDbContext _dbContext;
         private readonly bool _useBatchUpdate;
 
-        public EfDb(IRandom random, ApplicationDbContext dbContext, IOptions<AppSettings> appSettings)
+        public EfDb(ConcurrentRandom random, ApplicationDbContext dbContext, IOptions<AppSettings> appSettings)
         {
             _random = random;
             _dbContext = dbContext;
@@ -84,7 +84,7 @@ namespace Benchmarks.Data
         private static readonly Func<ApplicationDbContext, AsyncEnumerable<Fortune>> _fortunesQuery
             = EF.CompileAsyncQuery((ApplicationDbContext context) => context.Fortune);
 
-        public async Task<IEnumerable<Fortune>> LoadFortunesRows()
+        public async Task<List<Fortune>> LoadFortunesRows()
         {
             var result = await _fortunesQuery(_dbContext).ToListAsync();
 

+ 0 - 19
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IDb.cs

@@ -1,19 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace Benchmarks.Data
-{
-    public interface IDb
-    {
-        Task<World> LoadSingleQueryRow();
-
-        Task<World[]> LoadMultipleQueriesRows(int count);
-
-        Task<World[]> LoadMultipleUpdatesRows(int count);
-
-        Task<IEnumerable<Fortune>> LoadFortunesRows();
-    }
-}

+ 0 - 10
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IRandom.cs

@@ -1,10 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-namespace Benchmarks.Data
-{
-    public interface IRandom
-    {
-        int Next(int minValue, int maxValue);
-    }
-}

+ 15 - 3
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Random.cs

@@ -2,19 +2,31 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 using System;
+using System.Runtime.CompilerServices;
 using System.Threading;
 
 namespace Benchmarks.Data
 {
-    public class DefaultRandom : IRandom
+    public class ConcurrentRandom
     {
         private static int nextSeed = 0;
+
         // Random isn't thread safe
-        private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref nextSeed)));
+        [ThreadStatic]
+        private static Random _random;
+
+        private static Random Random => _random ?? CreateRandom();
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static Random CreateRandom()
+        {
+            _random = new Random(Interlocked.Increment(ref nextSeed));
+            return _random;
+        }
 
         public int Next(int minValue, int maxValue)
         {
-            return _random.Value.Next(minValue, maxValue);
+            return Random.Next(minValue, maxValue);
         }
     }
 }

+ 38 - 40
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/RawDb.cs

@@ -5,27 +5,28 @@ using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Data.Common;
-using System.Text;
 using System.Threading.Tasks;
 using Benchmarks.Configuration;
 using Microsoft.Extensions.Options;
 
 namespace Benchmarks.Data
 {
-    public class RawDb : IDb
+    public class RawDb
     {
-        private readonly IRandom _random;
+        private static readonly Comparison<WorldRaw> WorldSortComparison = (a, b) => a.Id.CompareTo(b.Id);
+
+        private readonly ConcurrentRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
         private readonly string _connectionString;
 
-        public RawDb(IRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
+        public RawDb(ConcurrentRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
         {
             _random = random;
             _dbProviderFactory = dbProviderFactory;
             _connectionString = appSettings.Value.ConnectionString;
         }
 
-        public async Task<World> LoadSingleQueryRow()
+        public async Task<WorldRaw> LoadSingleQueryRow()
         {
             using (var db = _dbProviderFactory.CreateConnection())
             {
@@ -39,13 +40,13 @@ namespace Benchmarks.Data
             }
         }
 
-        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        async Task<WorldRaw> ReadSingleRow(DbConnection connection, DbCommand cmd)
         {
             using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
             {
                 await rdr.ReadAsync();
 
-                return new World
+                return new WorldRaw
                 {
                     Id = rdr.GetInt32(0),
                     RandomNumber = rdr.GetInt32(1)
@@ -66,58 +67,57 @@ namespace Benchmarks.Data
             return cmd;
         }
 
-        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        public async Task<WorldRaw[]> LoadMultipleQueriesRows(int count)
         {
-            var result = new World[count];
             using (var db = _dbProviderFactory.CreateConnection())
             {
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
-                using (var cmd = CreateReadCommand(db))
-                {
-                    for (int i = 0; i < count; i++)
-                    {
-                        result[i] = await ReadSingleRow(db, cmd);
-                        cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
-                    }
-                }
+                return await LoadMultipleRows(count, db);
             }
-
-            return result;
         }
 
-        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        private async Task<WorldRaw[]> LoadMultipleRows(int count, DbConnection db)
         {
-            var results = new World[count];
+            using (var cmd = CreateReadCommand(db))
+            {
+                cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
 
-            var updateCommand = new StringBuilder(count);
+                var result = new WorldRaw[count];
+                for (int i = 0; i < result.Length; i++)
+                {
+                    result[i] = await ReadSingleRow(db, cmd);
+                    cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                }
+                return result;
+            }
+        }
 
+        public async Task<WorldRaw[]> LoadMultipleUpdatesRows(int count)
+        {
             using (var db = _dbProviderFactory.CreateConnection())
             {
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
 
+                var results = await LoadMultipleRows(count, db);
+
+                // Postgres has problems with deadlocks when these aren't sorted
+                Array.Sort<WorldRaw>(results, WorldSortComparison);
+
                 using (var updateCmd = db.CreateCommand())
-                using (var queryCmd = CreateReadCommand(db))
                 {
-                    for (int i = 0; i < count; i++)
+                    for (int i = 0; i < results.Length; i++)
                     {
-                        results[i] = await ReadSingleRow(db, queryCmd);
-                        queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
-                    }
+                        var strings = BatchUpdateString.Strings[i];
 
-                    // Postgres has problems with deadlocks when these aren't sorted
-                    Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
-
-                    for(int i = 0; i < count; i++)
-                    {
                         var id = updateCmd.CreateParameter();
-                        id.ParameterName = BatchUpdateString.Strings[i].Id;
+                        id.ParameterName = strings.Id;
                         id.DbType = DbType.Int32;
                         updateCmd.Parameters.Add(id);
 
                         var random = updateCmd.CreateParameter();
-                        random.ParameterName = BatchUpdateString.Strings[i].Random;
+                        random.ParameterName = strings.Random;
                         random.DbType = DbType.Int32;
                         updateCmd.Parameters.Add(random);
 
@@ -125,19 +125,17 @@ namespace Benchmarks.Data
                         id.Value = results[i].Id;
                         random.Value = randomNumber;
                         results[i].RandomNumber = randomNumber;
-
-                        updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
                     }
 
-                    updateCmd.CommandText = updateCommand.ToString();
+                    updateCmd.CommandText = BatchUpdateString.Strings[count - 1].UpdateQuery;
                     await updateCmd.ExecuteNonQueryAsync();
+
+                    return results;
                 }
             }
-
-            return results;
         }
 
-        public async Task<IEnumerable<Fortune>> LoadFortunesRows()
+        public async Task<List<Fortune>> LoadFortunesRows()
         {
             var result = new List<Fortune>();
 

+ 59 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/StringBuilderCache.cs

@@ -0,0 +1,59 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Text;
+
+namespace Benchmarks.Data
+{
+    internal static class StringBuilderCache
+    {
+        private const int DefaultCapacity = 1386;
+        private const int MaxBuilderSize = DefaultCapacity * 3;
+
+        [ThreadStatic]
+        private static StringBuilder t_cachedInstance;
+
+        /// <summary>Get a StringBuilder for the specified capacity.</summary>
+        /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
+        public static StringBuilder Acquire(int capacity = DefaultCapacity)
+        {
+            if (capacity <= MaxBuilderSize)
+            {
+                StringBuilder sb = t_cachedInstance;
+                if (capacity < DefaultCapacity)
+                {
+                    capacity = DefaultCapacity;
+                }
+
+                if (sb != null)
+                {
+                    // Avoid stringbuilder block fragmentation by getting a new StringBuilder
+                    // when the requested size is larger than the current capacity
+                    if (capacity <= sb.Capacity)
+                    {
+                        t_cachedInstance = null;
+                        sb.Clear();
+                        return sb;
+                    }
+                }
+            }
+            return new StringBuilder(capacity);
+        }
+
+        public static void Release(StringBuilder sb)
+        {
+            if (sb.Capacity <= MaxBuilderSize)
+            {
+                t_cachedInstance = sb;
+            }
+        }
+
+        public static string GetStringAndRelease(StringBuilder sb)
+        {
+            string result = sb.ToString();
+            Release(sb);
+            return result;
+        }
+    }
+}

+ 9 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/World.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.ComponentModel.DataAnnotations.Schema;
+using System.Runtime.InteropServices;
 
 namespace Benchmarks.Data
 {
@@ -14,4 +15,12 @@ namespace Benchmarks.Data
         [Column("randomnumber")]
         public int RandomNumber { get; set; }
     }
+
+    [StructLayout(LayoutKind.Sequential, Size = 8)]
+    public struct WorldRaw
+    {
+        public int Id { get; set; }
+
+        public int RandomNumber { get; set; }
+    }
 }

+ 4 - 4
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesEfMiddleware.cs

@@ -33,11 +33,11 @@ namespace Benchmarks.Middleware
                 var rows = await db.LoadFortunesRows();
 
                 await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
-
-                return;
             }
-
-            await _next(httpContext);
+            else
+            {
+                await _next(httpContext);
+            }
         }
     }
 

+ 4 - 4
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesRawMiddleware.cs

@@ -33,11 +33,11 @@ namespace Benchmarks.Middleware
                 var rows = await db.LoadFortunesRows();
 
                 await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
-
-                return;
             }
-
-            await _next(httpContext);
+            else
+            {
+                await _next(httpContext);
+            }
         }
     }
 

+ 7 - 5
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MiddlewareHelpers.cs

@@ -30,15 +30,17 @@ namespace Benchmarks.Middleware
                     : 1;
         }
 
-        public static async Task RenderFortunesHtml(IEnumerable<Fortune> model, HttpContext httpContext, HtmlEncoder htmlEncoder)
+        public static Task RenderFortunesHtml(List<Fortune> model, HttpContext httpContext, HtmlEncoder htmlEncoder)
         {
             httpContext.Response.StatusCode = StatusCodes.Status200OK;
             httpContext.Response.ContentType = "text/html; charset=UTF-8";
 
-            var sb = new StringBuilder();
+            var sb = StringBuilderCache.Acquire();
             sb.Append("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
-            foreach (var item in model)
+            var count = model.Count;
+            for (var i = 0; i < count; i++)
             {
+                var item = model[i];
                 sb.Append("<tr><td>");
                 sb.Append(item.Id.ToString(CultureInfo.InvariantCulture));
                 sb.Append("</td><td>");
@@ -47,10 +49,10 @@ namespace Benchmarks.Middleware
             }
 
             sb.Append("</table></body></html>");
-            var response = sb.ToString();
+            var response = StringBuilderCache.GetStringAndRelease(sb);
             // fortunes includes multibyte characters so response.Length is incorrect
             httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(response);
-            await httpContext.Response.WriteAsync(response);
+            return httpContext.Response.WriteAsync(response);
         }
     }
 }

+ 6 - 1
frameworks/CSharp/aspnetcore-mono/Benchmarks/Startup.cs

@@ -49,7 +49,7 @@ namespace Benchmarks
             services.AddSingleton(Scenarios);
 
             // Common DB services
-            services.AddSingleton<IRandom, DefaultRandom>();
+            services.AddSingleton<ConcurrentRandom>();
 
             var appSettings = Configuration.Get<AppSettings>();
             Console.WriteLine($"Database: {appSettings.Database}");
@@ -175,6 +175,11 @@ namespace Benchmarks
                 app.UseMvc();
             }
 
+            if (Scenarios.Any("Update"))
+            {
+                BatchUpdateString.Initalize();
+            }
+
             if (Scenarios.StaticFiles)
             {
                 app.UseStaticFiles();

+ 1 - 1
frameworks/CSharp/aspnetcore-mono/Benchmarks/Views/Fortunes/Fortunes.cshtml

@@ -1,5 +1,5 @@
 @using Benchmarks.Data
-@model IEnumerable<Fortune>
+@model List<Fortune>
 <!DOCTYPE html>
 <html>
 <head><title>Fortunes</title></head>

+ 47 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Updates.cs

@@ -0,0 +1,47 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO.Pipelines;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using Utf8Json;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task Updates(PipeWriter pipeWriter, int count)
+        {
+            OutputUpdates(pipeWriter, await Db.LoadMultipleUpdatesRows(count));
+        }
+
+        private static void OutputUpdates(PipeWriter pipeWriter, World[] rows)
+        {
+            var writer = GetWriter(pipeWriter);
+
+            // HTTP 1.1 OK
+            writer.Write(_http11OK);
+
+            // Server headers
+            writer.Write(_headerServer);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            // Content-Type header
+            writer.Write(_headerContentTypeJson);
+
+            // Content-Length header
+            writer.Write(_headerContentLength);
+            var jsonPayload = JsonSerializer.SerializeUnsafe(rows);
+            writer.WriteNumeric((uint)jsonPayload.Count);
+
+            // End of headers
+            writer.Write(_eoh);
+
+            // Body
+            writer.Write(jsonPayload);
+            writer.Commit();
+        }
+    }
+}

+ 37 - 19
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.cs

@@ -36,15 +36,16 @@ namespace PlatformBenchmarks
         private readonly static AsciiString _fortunesTableEnd = "</table></body></html>";
         private readonly static AsciiString _contentLengthGap = new string(' ', 4);
 
-        public static IDb Db { get; set; }
+        public static RawDb Db { get; set; }
 
         public static class Paths
         {
-            public readonly static AsciiString Plaintext = "/plaintext";
+            public readonly static AsciiString SingleQuery = "/db";
             public readonly static AsciiString Json = "/json";
             public readonly static AsciiString Fortunes = "/fortunes";
-            public readonly static AsciiString SingleQuery = "/db";
-            public readonly static AsciiString MultipleQueries = "/queries/count=";
+            public readonly static AsciiString Plaintext = "/plaintext";
+            public readonly static AsciiString MultipleQueries = "/queries/queries=";
+            public readonly static AsciiString Updates = "/updates/queries=";
         }
 
         private RequestType _requestType;
@@ -74,44 +75,60 @@ namespace PlatformBenchmarks
                 }
                 else if (Paths.MultipleQueries.Length <= pathLength && path.StartsWith(Paths.MultipleQueries))
                 {
-                    if (!Utf8Parser.TryParse(path.Slice(Paths.MultipleQueries.Length), out int queries, out _) || queries < 1)
-                    {
-                        queries = 1;
-                    }
-                    else if (queries > 500)
-                    {
-                        queries = 500;
-                    }
-                    _queries = queries;
+                    _queries = ParseQueries(path);
                     requestType = RequestType.MultipleQueries;
                 }
+                else if (Paths.Updates.Length <= pathLength && path.StartsWith(Paths.Updates))
+                {
+                    _queries = ParseQueries(path);
+                    requestType = RequestType.Updates;
+                }
             }
 
             _requestType = requestType;
         }
 
+        private static int ParseQueries(Span<byte> path)
+        {
+            if (!Utf8Parser.TryParse(path.Slice(Paths.MultipleQueries.Length), out int queries, out _) || queries < 1)
+            {
+                queries = 1;
+            }
+            else if (queries > 500)
+            {
+                queries = 500;
+            }
+
+            return queries;
+        }
+
         public Task ProcessRequestAsync()
         {
-            if (_requestType == RequestType.PlainText)
+            var requestType = _requestType;
+            if (requestType == RequestType.PlainText)
             {
                 PlainText(Writer);
             }
-            else if (_requestType == RequestType.Json)
+            else if (requestType == RequestType.Json)
             {
                 Json(Writer);
             }
-            else if (_requestType == RequestType.Fortunes)
+            else if (requestType == RequestType.Fortunes)
             {
                 return Fortunes(Writer);
             }
-            else if (_requestType == RequestType.SingleQuery)
+            else if (requestType == RequestType.SingleQuery)
             {
                 return SingleQuery(Writer);
             }
-            else if (_requestType == RequestType.MultipleQueries)
+            else if (requestType == RequestType.MultipleQueries)
             {
                 return MultipleQueries(Writer, _queries);
             }
+            else if (requestType == RequestType.Updates)
+            {
+                return Updates(Writer, _queries);
+            }
             else
             {
                 Default(Writer);
@@ -148,7 +165,8 @@ namespace PlatformBenchmarks
             Json,
             Fortunes,
             SingleQuery,
-            MultipleQueries
+            MultipleQueries,
+            Updates
         }
     }
 }

+ 39 - 6
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/BatchUpdateString.cs

@@ -3,22 +3,55 @@
 
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 
 namespace PlatformBenchmarks
 {
     internal class BatchUpdateString
     {
-        public static IList<BatchUpdateString> Strings { get;} = 
-            Enumerable.Range(0, 500)
+        private const int MaxBatch = 500;
+        private static string[] _queries = new string[MaxBatch];
+
+        public static IList<BatchUpdateString> Strings { get; } =
+            Enumerable.Range(0, MaxBatch)
                       .Select(i => new BatchUpdateString
                       {
                           Id = $"Id_{i}",
                           Random = $"Random_{i}",
-                          UpdateQuery = $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"
+                          BatchSize = i
                       }).ToArray();
-                        
+
+        private int BatchSize { get; set; }
         public string Id { get; set; }
         public string Random { get; set; }
-        public string UpdateQuery { get; set; }
+        public string UpdateQuery => _queries[BatchSize] ?? CreateQuery(BatchSize);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private string CreateQuery(int batchSize)
+        {
+            var sb = StringBuilderCache.Acquire();
+            foreach (var q in Enumerable.Range(0, batchSize + 1)
+                .Select(i => $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"))
+            {
+                sb.Append(q);
+            }
+            var query = sb.ToString();
+            _queries[batchSize] = query;
+            return query;
+        }
+
+        public static void Initalize()
+        {
+            Observe(Strings[0].UpdateQuery);
+            Observe(Strings[4].UpdateQuery);
+            Observe(Strings[9].UpdateQuery);
+            Observe(Strings[14].UpdateQuery);
+            Observe(Strings[19].UpdateQuery);
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static void Observe(string query)
+        {
+        }
     }
-}
+}

+ 0 - 19
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/IDb.cs

@@ -1,19 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace PlatformBenchmarks
-{
-    public interface IDb
-    {
-        Task<World> LoadSingleQueryRow();
-
-        Task<World[]> LoadMultipleQueriesRows(int count);
-
-        Task<World[]> LoadMultipleUpdatesRows(int count);
-
-        Task<List<Fortune>> LoadFortunesRows();
-    }
-}

+ 0 - 10
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/IRandom.cs

@@ -1,10 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-namespace PlatformBenchmarks
-{
-    public interface IRandom
-    {
-        int Next(int minValue, int maxValue);
-    }
-}

+ 15 - 3
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Random.cs

@@ -2,19 +2,31 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 using System;
+using System.Runtime.CompilerServices;
 using System.Threading;
 
 namespace PlatformBenchmarks
 {
-    public class DefaultRandom : IRandom
+    public class ConcurrentRandom
     {
         private static int nextSeed = 0;
+
         // Random isn't thread safe
-        private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref nextSeed)));
+        [ThreadStatic]
+        private static Random _random;
+
+        private static Random Random => _random ?? CreateRandom();
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static Random CreateRandom()
+        {
+            _random = new Random(Interlocked.Increment(ref nextSeed));
+            return _random;
+        }
 
         public int Next(int minValue, int maxValue)
         {
-            return _random.Value.Next(minValue, maxValue);
+            return Random.Next(minValue, maxValue);
         }
     }
 }

+ 33 - 36
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/RawDb.cs

@@ -5,19 +5,19 @@ using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Data.Common;
-using System.Text;
 using System.Threading.Tasks;
-using Microsoft.Extensions.Options;
 
 namespace PlatformBenchmarks
 {
-    public class RawDb : IDb
+    public class RawDb
     {
-        private readonly IRandom _random;
+        private static readonly Comparison<World> WorldSortComparison = (a, b) => a.Id.CompareTo(b.Id);
+
+        private readonly ConcurrentRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
         private readonly string _connectionString;
 
-        public RawDb(IRandom random, DbProviderFactory dbProviderFactory, AppSettings appSettings)
+        public RawDb(ConcurrentRandom random, DbProviderFactory dbProviderFactory, AppSettings appSettings)
         {
             _random = random;
             _dbProviderFactory = dbProviderFactory;
@@ -67,57 +67,56 @@ namespace PlatformBenchmarks
 
         public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
-            var result = new World[count];
-
             using (var db = _dbProviderFactory.CreateConnection())
             {
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
-                using (var cmd = CreateReadCommand(db))
-                {
-                    for (int i = 0; i < count; i++)
-                    {
-                        result[i] = await ReadSingleRow(db, cmd);
-                        cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
-                    }
-                }
+                return await LoadMultipleRows(count, db);
             }
 
-            return result;
         }
 
-        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        private async Task<World[]> LoadMultipleRows(int count, DbConnection db)
         {
-            var results = new World[count];
+            using (var cmd = CreateReadCommand(db))
+            {
+                cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
 
-            var updateCommand = new StringBuilder(count);
+                var result = new World[count];
+                for (int i = 0; i < result.Length; i++)
+                {
+                    result[i] = await ReadSingleRow(db, cmd);
+                    cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                }
+                return result;
+            }
+        }
 
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
             using (var db = _dbProviderFactory.CreateConnection())
             {
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
 
+                var results = await LoadMultipleRows(count, db);
+
+                // Postgres has problems with deadlocks when these aren't sorted
+                Array.Sort<World>(results, WorldSortComparison);
+
                 using (var updateCmd = db.CreateCommand())
-                using (var queryCmd = CreateReadCommand(db))
                 {
-                    for (int i = 0; i < count; i++)
+                    for (int i = 0; i < results.Length; i++)
                     {
-                        results[i] = await ReadSingleRow(db, queryCmd);
-                        queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
-                    }
-
-                    // Postgres has problems with deadlocks when these aren't sorted
-                    Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+                        var strings = BatchUpdateString.Strings[i];
 
-                    for(int i = 0; i < count; i++)
-                    {
                         var id = updateCmd.CreateParameter();
-                        id.ParameterName = BatchUpdateString.Strings[i].Id;
+                        id.ParameterName = strings.Id;
                         id.DbType = DbType.Int32;
                         updateCmd.Parameters.Add(id);
 
                         var random = updateCmd.CreateParameter();
-                        random.ParameterName = BatchUpdateString.Strings[i].Random;
+                        random.ParameterName = strings.Random;
                         random.DbType = DbType.Int32;
                         updateCmd.Parameters.Add(random);
 
@@ -125,16 +124,14 @@ namespace PlatformBenchmarks
                         id.Value = results[i].Id;
                         random.Value = randomNumber;
                         results[i].RandomNumber = randomNumber;
-
-                        updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
                     }
 
-                    updateCmd.CommandText = updateCommand.ToString();
+                    updateCmd.CommandText = BatchUpdateString.Strings[count - 1].UpdateQuery;
                     await updateCmd.ExecuteNonQueryAsync();
+
+                    return results;
                 }
             }
-
-            return results;
         }
 
         public async Task<List<Fortune>> LoadFortunesRows()

+ 5 - 2
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/World.cs

@@ -1,12 +1,15 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Runtime.InteropServices;
+
 namespace PlatformBenchmarks
 {
-    public class World
+    [StructLayout(LayoutKind.Sequential, Size = 8)]
+    public struct World
     {
         public int Id { get; set; }
-        
+
         public int RandomNumber { get; set; }
     }
 }

+ 4 - 2
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Program.cs

@@ -26,6 +26,8 @@ namespace PlatformBenchmarks
             Console.WriteLine(BenchmarkApplication.Paths.SingleQuery);
             DateHeader.SyncDateTimer();
 
+            BatchUpdateString.Initalize();
+
             BuildWebHost(args).Run();
         }
 
@@ -42,11 +44,11 @@ namespace PlatformBenchmarks
 
             if (appSettings.Database == DatabaseServer.PostgreSql)
             {
-                BenchmarkApplication.Db = new RawDb(new DefaultRandom(), NpgsqlFactory.Instance, appSettings);
+                BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), NpgsqlFactory.Instance, appSettings);
             }
             else if (appSettings.Database == DatabaseServer.MySql)
             {
-                BenchmarkApplication.Db = new RawDb(new DefaultRandom(), MySqlClientFactory.Instance, appSettings);
+                BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), MySqlClientFactory.Instance, appSettings);
             }
 
             var host = new WebHostBuilder()

+ 59 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/StringBuilderCache.cs

@@ -0,0 +1,59 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Text;
+
+namespace PlatformBenchmarks
+{
+    internal static class StringBuilderCache
+    {
+        private const int DefaultCapacity = 1386;
+        private const int MaxBuilderSize = DefaultCapacity * 3;
+
+        [ThreadStatic]
+        private static StringBuilder t_cachedInstance;
+
+        /// <summary>Get a StringBuilder for the specified capacity.</summary>
+        /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
+        public static StringBuilder Acquire(int capacity = DefaultCapacity)
+        {
+            if (capacity <= MaxBuilderSize)
+            {
+                StringBuilder sb = t_cachedInstance;
+                if (capacity < DefaultCapacity)
+                {
+                    capacity = DefaultCapacity;
+                }
+
+                if (sb != null)
+                {
+                    // Avoid stringbuilder block fragmentation by getting a new StringBuilder
+                    // when the requested size is larger than the current capacity
+                    if (capacity <= sb.Capacity)
+                    {
+                        t_cachedInstance = null;
+                        sb.Clear();
+                        return sb;
+                    }
+                }
+            }
+            return new StringBuilder(capacity);
+        }
+
+        public static void Release(StringBuilder sb)
+        {
+            if (sb.Capacity <= MaxBuilderSize)
+            {
+                t_cachedInstance = sb;
+            }
+        }
+
+        public static string GetStringAndRelease(StringBuilder sb)
+        {
+            string result = sb.ToString();
+            Release(sb);
+            return result;
+        }
+    }
+}

+ 20 - 18
frameworks/CSharp/aspnetcore-mono/benchmark_config.json

@@ -23,7 +23,8 @@
     "pg": {
       "fortune_url": "/fortunes",
       "db_url": "/db",
-      "query_url": "/queries/count=",
+      "query_url": "/queries/queries=",
+      "update_url": "/updates/queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
@@ -43,7 +44,8 @@
     "my": {
       "fortune_url": "/fortunes",
       "db_url": "/db",
-      "query_url": "/queries/count=",
+      "query_url": "/queries/queries=",
+      "update_url": "/updates/queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
@@ -100,18 +102,18 @@
       "notes": "",
       "versus": "aspcore-mono-pg"
     },
-    "mw-ef-pg": {
-      "db_url": "/db/ef",
-      "query_url": "/queries/ef?queries=",
-      "update_url": "/updates/ef?queries=",
-      "fortune_url": "/fortunes/ef",
+    "mw-my": {
+      "db_url": "/db/raw",
+      "query_url": "/queries/raw?queries=",
+      "update_url": "/updates/raw?queries=",
+      "fortune_url": "/fortunes/raw",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
-      "database": "Postgres",
+      "database": "MySQL",
       "framework": "ASP.NET Core",
       "language": "C#",
-      "orm": "Full",
+      "orm": "Raw",
       "platform": ".NET",
       "flavor": "Mono",
       "webserver": "Kestrel",
@@ -119,20 +121,20 @@
       "database_os": "Linux",
       "display_name": "ASP.NET Core on Mono, Middleware",
       "notes": "",
-      "versus": "aspcore-mono-pg"
+      "versus": "aspcore-mono-my"
     },
-    "mw-my": {
-      "db_url": "/db/raw",
-      "query_url": "/queries/raw?queries=",
-      "update_url": "/updates/raw?queries=",
-      "fortune_url": "/fortunes/raw",
+    "mw-ef-pg": {
+      "db_url": "/db/ef",
+      "query_url": "/queries/ef?queries=",
+      "update_url": "/updates/ef?queries=",
+      "fortune_url": "/fortunes/ef",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
-      "database": "MySQL",
+      "database": "Postgres",
       "framework": "ASP.NET Core",
       "language": "C#",
-      "orm": "Raw",
+      "orm": "Full",
       "platform": ".NET",
       "flavor": "Mono",
       "webserver": "Kestrel",
@@ -140,7 +142,7 @@
       "database_os": "Linux",
       "display_name": "ASP.NET Core on Mono, Middleware",
       "notes": "",
-      "versus": "aspcore-mono-my"
+      "versus": "aspcore-mono-pg"
     },
     "mvc": {
       "plaintext_url": "/mvc/plaintext",

+ 5 - 5
frameworks/CSharp/nancy/benchmark_config.json

@@ -1,7 +1,7 @@
 {
   "framework": "nancy",
   "tests": [{
-    "default": {
+    "mono": {
       "plaintext_url":"/plaintext",
       "json_url": "/json",
       "db_url": "/db",
@@ -18,9 +18,9 @@
       "webserver": "Kestrel",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "nancy",
+      "display_name": "Nancy on ASP.NET Core on Mono",
       "notes": "",
-      "versus": ""
+      "versus": "aspcore-mono"
     },
     "netcore": {
       "plaintext_url":"/plaintext",
@@ -39,9 +39,9 @@
       "webserver": "Kestrel",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "nancy",
+      "display_name": "Nancy on ASP.NET Core on .NET Core",
       "notes": "",
-      "versus": "nancy"
+      "versus": "aspcore"
     }
   }]
 }

+ 0 - 0
frameworks/CSharp/nancy/nancy.dockerfile → frameworks/CSharp/nancy/nancy-mono.dockerfile