|
@@ -2,6 +2,7 @@
|
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
|
|
using System;
|
|
|
+using System.Collections.Concurrent;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
using System.Runtime.CompilerServices;
|
|
@@ -13,6 +14,9 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
public class RawDb
|
|
|
{
|
|
|
+ private static readonly ConcurrentQueue<SqlReadCommand> _sqlReadCommands = new ConcurrentQueue<SqlReadCommand>();
|
|
|
+ private static readonly ConcurrentQueue<SqlFortuneCommand> _sqlFortuneCommands = new ConcurrentQueue<SqlFortuneCommand>();
|
|
|
+ private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
|
|
|
private readonly ConcurrentRandom _random;
|
|
|
private readonly string _connectionString;
|
|
|
private readonly MemoryCache _cache = new MemoryCache(
|
|
@@ -33,9 +37,10 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
await db.OpenAsync();
|
|
|
|
|
|
- var (cmd, _) = CreateReadCommand(db);
|
|
|
- using (cmd)
|
|
|
+ using (var cmd = CreateReadCommand())
|
|
|
{
|
|
|
+ cmd.Connection = db;
|
|
|
+ cmd.Parameter.TypedValue = _random.Next(0, 10000) + 1;
|
|
|
return await ReadSingleRow(cmd);
|
|
|
}
|
|
|
}
|
|
@@ -49,13 +54,14 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
await db.OpenAsync();
|
|
|
|
|
|
- var (cmd, idParameter) = CreateReadCommand(db);
|
|
|
- using (cmd)
|
|
|
+ using (var cmd = CreateReadCommand())
|
|
|
{
|
|
|
+ cmd.Connection = db;
|
|
|
+ var param = cmd.Parameter;
|
|
|
for (int i = 0; i < result.Length; i++)
|
|
|
{
|
|
|
+ param.TypedValue = _random.Next(0, 10000) + 1;
|
|
|
result[i] = await ReadSingleRow(cmd);
|
|
|
- idParameter.TypedValue = _random.Next(1, 10001);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -93,9 +99,9 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
await db.OpenAsync();
|
|
|
|
|
|
- var (cmd, idParameter) = rawdb.CreateReadCommand(db);
|
|
|
- using (cmd)
|
|
|
+ using (var cmd = CreateReadCommand())
|
|
|
{
|
|
|
+ cmd.Connection = db;
|
|
|
Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
|
|
|
{
|
|
|
return await rawdb.ReadSingleRow(cmd);
|
|
@@ -104,7 +110,8 @@ namespace PlatformBenchmarks
|
|
|
var cacheKeys = _cacheKeys;
|
|
|
var key = cacheKeys[id];
|
|
|
|
|
|
- idParameter.TypedValue = id;
|
|
|
+ var param = cmd.Parameter;
|
|
|
+ param.TypedValue = id;
|
|
|
|
|
|
for (; i < result.Length; i++)
|
|
|
{
|
|
@@ -112,7 +119,7 @@ namespace PlatformBenchmarks
|
|
|
result[i] = data;
|
|
|
|
|
|
id = rawdb._random.Next(1, 10001);
|
|
|
- idParameter.TypedValue = id;
|
|
|
+ param.TypedValue = id;
|
|
|
key = cacheKeys[id];
|
|
|
}
|
|
|
}
|
|
@@ -128,14 +135,14 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
await db.OpenAsync();
|
|
|
|
|
|
- var (cmd, idParameter) = CreateReadCommand(db);
|
|
|
- using (cmd)
|
|
|
+ using (var cmd = CreateReadCommand())
|
|
|
{
|
|
|
+ cmd.Connection = db;
|
|
|
var cacheKeys = _cacheKeys;
|
|
|
var cache = _cache;
|
|
|
for (var i = 1; i < 10001; i++)
|
|
|
{
|
|
|
- idParameter.TypedValue = i;
|
|
|
+ cmd.Parameter.TypedValue = i;
|
|
|
cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(cmd));
|
|
|
}
|
|
|
}
|
|
@@ -152,13 +159,14 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
await db.OpenAsync();
|
|
|
|
|
|
- var (queryCmd, queryParameter) = CreateReadCommand(db);
|
|
|
- using (queryCmd)
|
|
|
+ using (var cmd = CreateReadCommand())
|
|
|
{
|
|
|
+ cmd.Connection = db;
|
|
|
+ var queryParameter = cmd.Parameter;
|
|
|
for (int i = 0; i < results.Length; i++)
|
|
|
{
|
|
|
- results[i] = await ReadSingleRow(queryCmd);
|
|
|
- queryParameter.TypedValue = _random.Next(1, 10001);
|
|
|
+ queryParameter.TypedValue = _random.Next(0, 10000) + 1;
|
|
|
+ results[i] = await ReadSingleRow(cmd);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -192,7 +200,9 @@ namespace PlatformBenchmarks
|
|
|
{
|
|
|
await db.OpenAsync();
|
|
|
|
|
|
- using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", db))
|
|
|
+ using var cmd = CreateFortuneCommand();
|
|
|
+ cmd.Connection = db;
|
|
|
+
|
|
|
using (var rdr = await cmd.ExecuteReaderAsync())
|
|
|
{
|
|
|
while (await rdr.ReadAsync())
|
|
@@ -212,14 +222,24 @@ namespace PlatformBenchmarks
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
|
|
|
+ private static SqlReadCommand CreateReadCommand()
|
|
|
{
|
|
|
- var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
|
|
|
- var parameter = new NpgsqlParameter<int>(parameterName: "@Id", value: _random.Next(1, 10001));
|
|
|
+ if (!_sqlReadCommands.TryDequeue(out var cmd))
|
|
|
+ {
|
|
|
+ cmd = new SqlReadCommand();
|
|
|
+ }
|
|
|
|
|
|
- cmd.Parameters.Add(parameter);
|
|
|
+ return cmd;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static SqlFortuneCommand CreateFortuneCommand()
|
|
|
+ {
|
|
|
+ if (!_sqlFortuneCommands.TryDequeue(out var cmd))
|
|
|
+ {
|
|
|
+ cmd = new SqlFortuneCommand();
|
|
|
+ }
|
|
|
|
|
|
- return (cmd, parameter);
|
|
|
+ return cmd;
|
|
|
}
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
@@ -237,7 +257,55 @@ namespace PlatformBenchmarks
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
|
|
|
+ internal class SqlFortuneCommand : IDisposable
|
|
|
+ {
|
|
|
+ private readonly NpgsqlCommand _cmd;
|
|
|
+
|
|
|
+ public NpgsqlConnection Connection
|
|
|
+ {
|
|
|
+ set { _cmd.Connection = value; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public SqlFortuneCommand()
|
|
|
+ {
|
|
|
+ _cmd = new NpgsqlCommand("SELECT id, message FROM fortune");
|
|
|
+ }
|
|
|
+
|
|
|
+ public Task<NpgsqlDataReader> ExecuteReaderAsync() => _cmd.ExecuteReaderAsync();
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ _cmd.Connection = null;
|
|
|
+ _sqlFortuneCommands.Enqueue(this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class SqlReadCommand : IDisposable
|
|
|
+ {
|
|
|
+ private readonly NpgsqlCommand _cmd;
|
|
|
+ private readonly NpgsqlParameter<int> _parameter;
|
|
|
+
|
|
|
+ public NpgsqlParameter<int> Parameter => _parameter;
|
|
|
+ public NpgsqlConnection Connection
|
|
|
+ {
|
|
|
+ set { _cmd.Connection = value; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public SqlReadCommand()
|
|
|
+ {
|
|
|
+ _cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id");
|
|
|
+ _parameter = new NpgsqlParameter<int>(parameterName: "@Id", value: 0);
|
|
|
+ _cmd.Parameters.Add(_parameter);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static implicit operator NpgsqlCommand(SqlReadCommand c) => c._cmd;
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ _cmd.Connection = null;
|
|
|
+ _sqlReadCommands.Enqueue(this);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
public sealed class CacheKey : IEquatable<CacheKey>
|
|
|
{
|