diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Caching.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Caching.cs new file mode 100644 index 00000000000..0098e68d2dd --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Caching.cs @@ -0,0 +1,16 @@ +// 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; + +namespace PlatformBenchmarks +{ + public partial class BenchmarkApplication + { + private async Task Caching(PipeWriter pipeWriter, int count) + { + OutputMultipleQueries(pipeWriter, await Db.LoadCachedQueries(count)); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs new file mode 100644 index 00000000000..4c0b0ba5cb6 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs @@ -0,0 +1,53 @@ +// 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.IO.Pipelines; +using System.Text.Encodings.Web; +using System.Threading.Tasks; + +namespace PlatformBenchmarks +{ + public partial class BenchmarkApplication + { + private readonly static AsciiString _fortunesPreamble = + _http11OK + + _headerServer + _crlf + + _headerContentTypeHtml + _crlf + + _headerContentLength; + + private async Task Fortunes(PipeWriter pipeWriter) + { + OutputFortunes(pipeWriter, await Db.LoadFortunesRows()); + } + + private void OutputFortunes(PipeWriter pipeWriter, List model) + { + var writer = GetWriter(pipeWriter, sizeHint: 1600); // in reality it's 1361 + + writer.Write(_fortunesPreamble); + + var lengthWriter = writer; + writer.Write(_contentLengthGap); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + var bodyStart = writer.Buffered; + // Body + writer.Write(_fortunesTableStart); + foreach (var item in model) + { + writer.Write(_fortunesRowStart); + writer.WriteNumeric((uint)item.Id); + writer.Write(_fortunesColumn); + writer.WriteUtf8String(HtmlEncoder.Encode(item.Message)); + writer.Write(_fortunesRowEnd); + } + writer.Write(_fortunesTableEnd); + lengthWriter.WriteNumeric((uint)(writer.Buffered - bodyStart)); + + writer.Commit(); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs new file mode 100644 index 00000000000..e4f062dd944 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs @@ -0,0 +1,39 @@ +// 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.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; +using Utf8Json; + +namespace PlatformBenchmarks +{ + public partial class BenchmarkApplication + { + private async Task MultipleQueries(PipeWriter pipeWriter, int count) + { + OutputMultipleQueries(pipeWriter, await Db.LoadMultipleQueriesRows(count)); + } + + private static void OutputMultipleQueries(PipeWriter pipeWriter, World[] rows) + { + var writer = GetWriter(pipeWriter, sizeHint: 160 * rows.Length); // in reality it's 152 for one + + writer.Write(_dbPreamble); + + var lengthWriter = writer; + writer.Write(_contentLengthGap); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + writer.Commit(); + + var jsonPayload = JsonSerializer.SerializeUnsafe(rows); + pipeWriter.Write(jsonPayload); + + // Content-Length + lengthWriter.WriteNumeric((uint)jsonPayload.Count); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs new file mode 100644 index 00000000000..aabd5327aa0 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs @@ -0,0 +1,39 @@ +// 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.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; +using Utf8Json; + +namespace PlatformBenchmarks +{ + public partial class BenchmarkApplication + { + private async Task SingleQuery(PipeWriter pipeWriter) + { + OutputSingleQuery(pipeWriter, await Db.LoadSingleQueryRow()); + } + + private static void OutputSingleQuery(PipeWriter pipeWriter, World row) + { + var writer = GetWriter(pipeWriter, sizeHint: 180); // in reality it's 150 + + writer.Write(_dbPreamble); + + var lengthWriter = writer; + writer.Write(_contentLengthGap); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + writer.Commit(); + + var jsonPayload = JsonSerializer.SerializeUnsafe(row); + pipeWriter.Write(jsonPayload); + + // Content-Length + lengthWriter.WriteNumeric((uint)jsonPayload.Count); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Updates.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Updates.cs new file mode 100644 index 00000000000..f7527b7cce6 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Updates.cs @@ -0,0 +1,39 @@ +// 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.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; +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, sizeHint: 120 * rows.Length); // in reality it's 112 for one + + writer.Write(_dbPreamble); + + var lengthWriter = writer; + writer.Write(_contentLengthGap); + + // Date header + writer.Write(DateHeader.HeaderBytes); + + writer.Commit(); + + var jsonPayload = JsonSerializer.SerializeUnsafe(rows); + pipeWriter.Write(jsonPayload); + + // Content-Length + lengthWriter.WriteNumeric((uint)jsonPayload.Count); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs index f7fc914e8ab..34a82603c51 100644 --- a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs @@ -44,9 +44,7 @@ public partial class BenchmarkApplication private readonly static AsciiString _fortunesTableEnd = ""; private readonly static AsciiString _contentLengthGap = new string(' ', 4); -#if DATABASE public static RawDb Db { get; set; } -#endif [ThreadStatic] private static Utf8JsonWriter t_writer; diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/AppSettings.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/AppSettings.cs new file mode 100644 index 00000000000..259cf5a3444 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/AppSettings.cs @@ -0,0 +1,12 @@ +// 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 class AppSettings + { + public string ConnectionString { get; set; } + + public DatabaseServer Database { get; set; } = DatabaseServer.None; + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/DatabaseServer.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/DatabaseServer.cs new file mode 100644 index 00000000000..86f490e08a5 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Configuration/DatabaseServer.cs @@ -0,0 +1,13 @@ +// 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 enum DatabaseServer + { + None, + SqlServer, + PostgreSql, + MySql + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/BatchUpdateString.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/BatchUpdateString.cs new file mode 100644 index 00000000000..2cb48473903 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/BatchUpdateString.cs @@ -0,0 +1,44 @@ +// 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.Linq; + +namespace PlatformBenchmarks +{ + internal class BatchUpdateString + { + private const int MaxBatch = 500; + + public static DatabaseServer DatabaseServer; + + internal static readonly string[] Ids = Enumerable.Range(0, MaxBatch).Select(i => $"@Id_{i}").ToArray(); + internal static readonly string[] Randoms = Enumerable.Range(0, MaxBatch).Select(i => $"@Random_{i}").ToArray(); + + private static string[] _queries = new string[MaxBatch + 1]; + + public static string Query(int batchSize) + { + if (_queries[batchSize] != null) + { + return _queries[batchSize]; + } + + var lastIndex = batchSize - 1; + + var sb = StringBuilderCache.Acquire(); + + if (DatabaseServer == DatabaseServer.PostgreSql) + { + sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES "); + Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@Id_{i}, @Random_{i}), ")); + sb.Append($"(@Id_{lastIndex}, @Random_{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id"); + } + else + { + Enumerable.Range(0, batchSize).ToList().ForEach(i => sb.Append($"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};")); + } + + return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/CachedWorld.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/CachedWorld.cs new file mode 100644 index 00000000000..d32b5a53b66 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/CachedWorld.cs @@ -0,0 +1,17 @@ +// 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 CachedWorld + { + public int Id { get; set; } + + public int RandomNumber { get; set; } + + public static implicit operator World(CachedWorld world) => new World { Id = world.Id, RandomNumber = world.RandomNumber }; + public static implicit operator CachedWorld(World world) => new CachedWorld { Id = world.Id, RandomNumber = world.RandomNumber }; + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Fortune.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Fortune.cs new file mode 100644 index 00000000000..83287dcbdbe --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Fortune.cs @@ -0,0 +1,25 @@ +// 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; + +namespace PlatformBenchmarks +{ + public readonly struct Fortune : IComparable, IComparable + { + public Fortune(int id, string message) + { + Id = id; + Message = message; + } + + public int Id { get; } + + public string Message { get; } + + public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used"); + + // Performance critical, using culture insensitive comparison + public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message); + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Random.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Random.cs new file mode 100644 index 00000000000..9cb3ef4c39b --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/Random.cs @@ -0,0 +1,32 @@ +// 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.Runtime.CompilerServices; +using System.Threading; + +namespace PlatformBenchmarks +{ + public class ConcurrentRandom + { + private static int nextSeed = 0; + + // Random isn't thread safe + [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.Next(minValue, maxValue); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/RawDb.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/RawDb.cs new file mode 100644 index 00000000000..b1c4f448ede --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/RawDb.cs @@ -0,0 +1,262 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; +using Npgsql; + +namespace PlatformBenchmarks +{ + public class RawDb + { + private readonly ConcurrentRandom _random; + private readonly string _connectionString; + private readonly MemoryCache _cache = new MemoryCache( + new MemoryCacheOptions() + { + ExpirationScanFrequency = TimeSpan.FromMinutes(60) + }); + + public RawDb(ConcurrentRandom random, AppSettings appSettings) + { + _random = random; + _connectionString = appSettings.ConnectionString; + } + + public async Task LoadSingleQueryRow() + { + using (var db = new NpgsqlConnection(_connectionString)) + { + await db.OpenAsync(); + + var (cmd, _) = CreateReadCommand(db); + using (cmd) + { + return await ReadSingleRow(cmd); + } + } + } + + public async Task LoadMultipleQueriesRows(int count) + { + var result = new World[count]; + + using (var db = new NpgsqlConnection(_connectionString)) + { + await db.OpenAsync(); + + var (cmd, idParameter) = CreateReadCommand(db); + using (cmd) + { + for (int i = 0; i < result.Length; i++) + { + result[i] = await ReadSingleRow(cmd); + idParameter.TypedValue = _random.Next(1, 10001); + } + } + } + + return result; + } + + public Task LoadCachedQueries(int count) + { + var result = new World[count]; + var cacheKeys = _cacheKeys; + var cache = _cache; + var random = _random; + for (var i = 0; i < result.Length; i++) + { + var id = random.Next(1, 10001); + var key = cacheKeys[id]; + var data = cache.Get(key); + + if (data != null) + { + result[i] = data; + } + else + { + return LoadUncachedQueries(id, i, count, this, result); + } + } + + return Task.FromResult(result); + + static async Task LoadUncachedQueries(int id, int i, int count, RawDb rawdb, World[] result) + { + using (var db = new NpgsqlConnection(rawdb._connectionString)) + { + await db.OpenAsync(); + + var (cmd, idParameter) = rawdb.CreateReadCommand(db); + using (cmd) + { + Func> create = async (entry) => + { + return await rawdb.ReadSingleRow(cmd); + }; + + var cacheKeys = _cacheKeys; + var key = cacheKeys[id]; + + idParameter.TypedValue = id; + + for (; i < result.Length; i++) + { + var data = await rawdb._cache.GetOrCreateAsync(key, create); + result[i] = data; + + id = rawdb._random.Next(1, 10001); + idParameter.TypedValue = id; + key = cacheKeys[id]; + } + } + } + + return result; + } + } + + public async Task PopulateCache() + { + using (var db = new NpgsqlConnection(_connectionString)) + { + await db.OpenAsync(); + + var (cmd, idParameter) = CreateReadCommand(db); + using (cmd) + { + var cacheKeys = _cacheKeys; + var cache = _cache; + for (var i = 1; i < 10001; i++) + { + idParameter.TypedValue = i; + cache.Set(cacheKeys[i], await ReadSingleRow(cmd)); + } + } + } + + Console.WriteLine("Caching Populated"); + } + + public async Task LoadMultipleUpdatesRows(int count) + { + var results = new World[count]; + + using (var db = new NpgsqlConnection(_connectionString)) + { + await db.OpenAsync(); + + var (queryCmd, queryParameter) = CreateReadCommand(db); + using (queryCmd) + { + for (int i = 0; i < results.Length; i++) + { + results[i] = await ReadSingleRow(queryCmd); + queryParameter.TypedValue = _random.Next(1, 10001); + } + } + + using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), db)) + { + var ids = BatchUpdateString.Ids; + var randoms = BatchUpdateString.Randoms; + + for (int i = 0; i < results.Length; i++) + { + var randomNumber = _random.Next(1, 10001); + + updateCmd.Parameters.Add(new NpgsqlParameter(parameterName: ids[i], value: results[i].Id)); + updateCmd.Parameters.Add(new NpgsqlParameter(parameterName: randoms[i], value: randomNumber)); + + results[i].RandomNumber = randomNumber; + } + + await updateCmd.ExecuteNonQueryAsync(); + } + } + + return results; + } + + public async Task> LoadFortunesRows() + { + var result = new List(); + + using (var db = new NpgsqlConnection(_connectionString)) + { + await db.OpenAsync(); + + using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", db)) + using (var rdr = await cmd.ExecuteReaderAsync()) + { + while (await rdr.ReadAsync()) + { + result.Add(new Fortune + ( + id:rdr.GetInt32(0), + message: rdr.GetString(1) + )); + } + } + } + + result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." )); + result.Sort(); + + return result; + } + + private (NpgsqlCommand readCmd, NpgsqlParameter idParameter) CreateReadCommand(NpgsqlConnection connection) + { + var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection); + var parameter = new NpgsqlParameter(parameterName: "@Id", value: _random.Next(1, 10001)); + + cmd.Parameters.Add(parameter); + + return (cmd, parameter); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private async Task ReadSingleRow(NpgsqlCommand cmd) + { + using (var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow)) + { + await rdr.ReadAsync(); + + return new World + { + Id = rdr.GetInt32(0), + RandomNumber = rdr.GetInt32(1) + }; + } + } + + private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray(); + + public sealed class CacheKey : IEquatable + { + private readonly int _value; + + public CacheKey(int value) + => _value = value; + + public bool Equals(CacheKey key) + => key._value == _value; + + public override bool Equals(object obj) + => ReferenceEquals(obj, this); + + public override int GetHashCode() + => _value; + + public override string ToString() + => _value.ToString(); + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/World.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/World.cs new file mode 100644 index 00000000000..8ac83c75f64 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Data/World.cs @@ -0,0 +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 +{ + [StructLayout(LayoutKind.Sequential, Size = 8)] + public struct World + { + public int Id { get; set; } + + public int RandomNumber { get; set; } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj index 5aee6dc3dd0..0a10b525aae 100644 --- a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj @@ -18,7 +18,12 @@ + + + + $(DefineConstants);DATABASE + @@ -27,5 +32,6 @@ + diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs index 2c244c27105..52629d382d7 100644 --- a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs @@ -36,9 +36,9 @@ public static async Task Main(string[] args) DateHeader.SyncDateTimer(); var host = BuildWebHost(args); -#if DATABASE var config = (IConfiguration)host.Services.GetService(typeof(IConfiguration)); BatchUpdateString.DatabaseServer = config.Get().Database; +#if DATABASE await BenchmarkApplication.Db.PopulateCache(); #endif await host.RunAsync(); @@ -53,8 +53,8 @@ public static IWebHost BuildWebHost(string[] args) .AddCommandLine(args) .Build(); -#if DATABASE var appSettings = config.Get(); +#if DATABASE Console.WriteLine($"Database: {appSettings.Database}"); Console.WriteLine($"ConnectionString: {appSettings.ConnectionString}"); diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/StringBuilderCache.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/StringBuilderCache.cs new file mode 100644 index 00000000000..e067f818ff9 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/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; + + /// Get a StringBuilder for the specified capacity. + /// If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied. + 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; + } + } +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utf8Json.Generated.cs b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utf8Json.Generated.cs index 8f6b2666dcc..4c67f0acf82 100644 --- a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utf8Json.Generated.cs +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utf8Json.Generated.cs @@ -1,4 +1,4 @@ -// Generated by Utf8Json.UniversalCodeGenerator. Do not hand edit. +// Generated by Utf8Json.UniversalCodeGenerator. Do not hand edit. #pragma warning disable 618 #pragma warning disable 612 @@ -45,9 +45,11 @@ internal static class GeneratedResolverGetFormatterHelper static GeneratedResolverGetFormatterHelper() { - lookup = new global::System.Collections.Generic.Dictionary(1) + lookup = new global::System.Collections.Generic.Dictionary(9) { - {typeof(global::PlatformBenchmarks.JsonMessage), 0 }, + {typeof(global::PlatformBenchmarks.World[]), 0 }, + {typeof(global::PlatformBenchmarks.World), 1 }, + {typeof(global::PlatformBenchmarks.JsonMessage), 2 }, }; } @@ -58,7 +60,9 @@ internal static object GetFormatter(Type t) switch (key) { - case 0: return new Utf8Json.Formatters.PlatformBenchmarks.JsonMessageFormatter(); + case 0: return new global::Utf8Json.Formatters.ArrayFormatter(); + case 1: return new Utf8Json.Formatters.PlatformBenchmarks.WorldFormatter(); + case 2: return new Utf8Json.Formatters.PlatformBenchmarks.JsonMessageFormatter(); default: return null; } } @@ -82,6 +86,91 @@ namespace Utf8Json.Formatters.PlatformBenchmarks using Utf8Json; + public sealed class WorldFormatter : global::Utf8Json.IJsonFormatter + { + readonly global::Utf8Json.Internal.AutomataDictionary ____keyMapping; + readonly byte[][] ____stringByteKeys; + + public WorldFormatter() + { + this.____keyMapping = new global::Utf8Json.Internal.AutomataDictionary() + { + { JsonWriter.GetEncodedPropertyNameWithoutQuotation("Id"), 0}, + { JsonWriter.GetEncodedPropertyNameWithoutQuotation("RandomNumber"), 1}, + }; + + this.____stringByteKeys = new byte[][] + { + JsonWriter.GetEncodedPropertyNameWithBeginObject("Id"), + JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator("RandomNumber"), + + }; + } + + public void Serialize(ref JsonWriter writer, global::PlatformBenchmarks.World value, global::Utf8Json.IJsonFormatterResolver formatterResolver) + { + + + writer.WriteRaw(this.____stringByteKeys[0]); + writer.WriteInt32(value.Id); + writer.WriteRaw(this.____stringByteKeys[1]); + writer.WriteInt32(value.RandomNumber); + + writer.WriteEndObject(); + } + + public global::PlatformBenchmarks.World Deserialize(ref JsonReader reader, global::Utf8Json.IJsonFormatterResolver formatterResolver) + { + if (reader.ReadIsNull()) + { + throw new InvalidOperationException("typecode is null, struct not supported"); + } + + + var __Id__ = default(int); + var __Id__b__ = false; + var __RandomNumber__ = default(int); + var __RandomNumber__b__ = false; + + var ____count = 0; + reader.ReadIsBeginObjectWithVerify(); + while (!reader.ReadIsEndObjectWithSkipValueSeparator(ref ____count)) + { + var stringKey = reader.ReadPropertyNameSegmentRaw(); + int key; + if (!____keyMapping.TryGetValueSafe(stringKey, out key)) + { + reader.ReadNextBlock(); + goto NEXT_LOOP; + } + + switch (key) + { + case 0: + __Id__ = reader.ReadInt32(); + __Id__b__ = true; + break; + case 1: + __RandomNumber__ = reader.ReadInt32(); + __RandomNumber__b__ = true; + break; + default: + reader.ReadNextBlock(); + break; + } + + NEXT_LOOP: + continue; + } + + var ____result = new global::PlatformBenchmarks.World(); + if(__Id__b__) ____result.Id = __Id__; + if(__RandomNumber__b__) ____result.RandomNumber = __RandomNumber__; + + return ____result; + } + } + public sealed class JsonMessageFormatter : global::Utf8Json.IJsonFormatter { readonly global::Utf8Json.Internal.AutomataDictionary ____keyMapping; diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.json b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.json new file mode 100644 index 00000000000..b4a36c9d8c4 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.json @@ -0,0 +1,4 @@ +{ + "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Delay Us=500;Write Coalescing Buffer Threshold Bytes=1000", + "Database": "postgresql" +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.updates.json b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.updates.json new file mode 100644 index 00000000000..b4a36c9d8c4 --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.postgresql.updates.json @@ -0,0 +1,4 @@ +{ + "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Delay Us=500;Write Coalescing Buffer Threshold Bytes=1000", + "Database": "postgresql" +} diff --git a/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/rd.xml b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/rd.xml new file mode 100644 index 00000000000..0281010332d --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/rd.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg-up.dockerfile b/frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg-up.dockerfile new file mode 100644 index 00000000000..08a54cb164e --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg-up.dockerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +RUN apt-get update +RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 +WORKDIR /app +COPY PlatformBenchmarks . +RUN dotnet publish -c Release -o out -r linux-x64 /p:IsDatabase=true + +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime +ENV ASPNETCORE_URLS http://+:8080 +WORKDIR /app +COPY --from=build /app/out ./ +COPY PlatformBenchmarks/appsettings.postgresql.updates.json ./appsettings.json + +EXPOSE 8080 + +ENTRYPOINT ["./PlatformBenchmarks"] diff --git a/frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg.dockerfile b/frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg.dockerfile new file mode 100644 index 00000000000..c7a34e74bea --- /dev/null +++ b/frameworks/CSharp/aspnetcore-corert/aspcore-corert-ado-pg.dockerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +RUN apt-get update +RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 +WORKDIR /app +COPY PlatformBenchmarks . +RUN dotnet publish -c Release -o out -r linux-x64 /p:IsDatabase=true + +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime +ENV ASPNETCORE_URLS http://+:8080 +WORKDIR /app +COPY --from=build /app/out ./ +COPY PlatformBenchmarks/appsettings.postgresql.json ./appsettings.json + +EXPOSE 8080 + +ENTRYPOINT ["./PlatformBenchmarks"] diff --git a/frameworks/CSharp/aspnetcore-corert/benchmark_config.json b/frameworks/CSharp/aspnetcore-corert/benchmark_config.json index a11a6220f07..742eab77aa6 100644 --- a/frameworks/CSharp/aspnetcore-corert/benchmark_config.json +++ b/frameworks/CSharp/aspnetcore-corert/benchmark_config.json @@ -19,6 +19,45 @@ "display_name": "ASP.NET Core [Platform, CoreRT]", "notes": "", "versus": "aspcore" + }, + "ado-pg": { + "fortune_url": "/fortunes", + "db_url": "/db", + "query_url": "/queries/", + "cached_query_url": "/cached-worlds/", + "port": 8080, + "approach": "Stripped", + "classification": "Platform", + "database": "Postgres", + "framework": "ASP.NET Core", + "language": "C#", + "orm": "Raw", + "platform": ".NET", + "flavor": "CoreRT", + "webserver": "Kestrel", + "os": "Linux", + "database_os": "Linux", + "display_name": "ASP.NET Core [Platform, CoreRT, Pg]", + "notes": "", + "versus": "aspcore-ado-pg" + }, + "ado-pg-up": { + "update_url": "/updates/", + "port": 8080, + "approach": "Stripped", + "classification": "Platform", + "database": "Postgres", + "framework": "ASP.NET Core", + "language": "C#", + "orm": "Raw", + "platform": ".NET", + "flavor": "CoreRT", + "webserver": "Kestrel", + "os": "Linux", + "database_os": "Linux", + "display_name": "ASP.NET Core [Platform, CoreRT, Pg]", + "notes": "", + "versus": "aspcore-ado-pg-up" } }] } diff --git a/frameworks/CSharp/aspnetcore-corert/config.toml b/frameworks/CSharp/aspnetcore-corert/config.toml index 106b0058e85..9678e79a6d5 100644 --- a/frameworks/CSharp/aspnetcore-corert/config.toml +++ b/frameworks/CSharp/aspnetcore-corert/config.toml @@ -13,3 +13,30 @@ orm = "Raw" platform = ".NET" webserver = "Kestrel" versus = "aspcore" + +[ado-pg] +urls.db = "/db" +urls.query = "/queries/" +urls.fortune = "/fortunes" +urls.cached_query = "/cached-worlds/" +approach = "Stripped" +classification = "Platform" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = ".NET" +webserver = "Kestrel" +versus = "aspcore-ado-pg" + +[ado-pg-up] +urls.update = "/updates/" +approach = "Stripped" +classification = "Platform" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = ".NET" +webserver = "Kestrel" +versus = "aspcore-ado-pg-up"