Skip to content

Query tweaks #1257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 11, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve change tracking performance
|          Method |     Mean |     Error |    StdDev | Ratio |   Gen0 | Allocated | Alloc Ratio |
|---------------- |---------:|----------:|----------:|------:|-------:|----------:|------------:|
|    TrackChanges | 3.741 us | 0.0122 us | 0.0114 us |  1.00 | 0.0229 |   6.41 KB |        1.00 |
| NewTrackChanges | 1.359 us | 0.0070 us | 0.0066 us |  0.36 | 0.0095 |   2.62 KB |        0.41 |

using BenchmarkDotNet.Attributes;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;
using Microsoft.Extensions.Logging.Abstractions;

namespace Benchmarks.ChangeTracking;

// ReSharper disable once ClassCanBeSealed.Global
[MarkdownExporter]
[MemoryDiagnoser]
public class ChangeTrackerBenchmarks
{
    private readonly JsonApiRequest _request;
    private readonly TargetedFields _targetedFields;

    public ChangeTrackerBenchmarks()
    {
        IJsonApiOptions options = new JsonApiOptions();
        IResourceGraph resourceGraph = new ResourceGraphBuilder(options, NullLoggerFactory.Instance).Add<ExampleResource, long>().Build();
        ResourceType resourceType = resourceGraph.GetResourceType<ExampleResource>();

        _request = new JsonApiRequest
        {
            PrimaryResourceType = resourceType,
            IsCollection = true
        };

        _targetedFields = new TargetedFields();

        foreach (AttrAttribute attribute in resourceType.Attributes)
        {
            _targetedFields.Attributes.Add(attribute);
        }
    }

    [Benchmark(Baseline = true)]
    public void TrackChanges()
    {
        var changeTracker = new ResourceChangeTracker<ExampleResource>(_request, _targetedFields);

        var resource = new ExampleResource
        {
            Id = 1,
            Attr1 = "some",
            Attr2 = "more",
            Attr3 = "other",
            Attr4 = false,
            Attr5 = 123,
            Attr6 = default,
            Attr7 = default,
            Attr8 = default,
            Attr9 = DayOfWeek.Sunday
        };

        changeTracker.SetInitiallyStoredAttributeValues(resource);

        resource = new ExampleResource
        {
            Id = 1,
            Attr1 = "new",
            Attr2 = "change",
            Attr3 = "this",
            Attr4 = true,
            Attr5 = 456,
            Attr6 = default,
            Attr7 = default,
            Attr8 = default,
            Attr9 = DayOfWeek.Saturday
        };

        changeTracker.SetFinallyStoredAttributeValues(resource);

        changeTracker.HasImplicitChanges();
    }

    [Benchmark]
    public void NewTrackChanges()
    {
        var changeTracker = new NewResourceChangeTracker<ExampleResource>(_request, _targetedFields);

        var resource = new ExampleResource
        {
            Id = 1,
            Attr1 = "some",
            Attr2 = "more",
            Attr3 = "other",
            Attr4 = false,
            Attr5 = 123,
            Attr6 = default,
            Attr7 = default,
            Attr8 = default,
            Attr9 = DayOfWeek.Sunday
        };

        changeTracker.SetInitiallyStoredAttributeValues(resource);

        resource = new ExampleResource
        {
            Id = 1,
            Attr1 = "new",
            Attr2 = "change",
            Attr3 = "this",
            Attr4 = true,
            Attr5 = 456,
            Attr6 = default,
            Attr7 = default,
            Attr8 = default,
            Attr9 = DayOfWeek.Saturday
        };

        changeTracker.SetFinallyStoredAttributeValues(resource);

        changeTracker.HasImplicitChanges();
    }

    private sealed class ExampleResource : Identifiable<long>
    {
        [Attr]
        public string? Attr1 { get; set; }

        [Attr]
        public string? Attr2 { get; set; }

        [Attr]
        public string? Attr3 { get; set; }

        [Attr]
        public bool Attr4 { get; set; }

        [Attr]
        public int Attr5 { get; set; }

        [Attr]
        public DateTime Attr6 { get; set; }

        [Attr]
        public DateTimeOffset Attr7 { get; set; }

        [Attr]
        public TimeSpan Attr8 { get; set; }

        [Attr]
        public DayOfWeek Attr9 { get; set; }
    }
}
  • Loading branch information
bkoelman committed Mar 10, 2023
commit 82f04b29c0f16aa6675dc1e4663553af281d60d4
26 changes: 12 additions & 14 deletions src/JsonApiDotNetCore/Resources/ResourceChangeTracker.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Text.Json;
using JetBrains.Annotations;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources.Annotations;
Expand All @@ -13,9 +12,9 @@ public sealed class ResourceChangeTracker<TResource> : IResourceChangeTracker<TR
private readonly IJsonApiRequest _request;
private readonly ITargetedFields _targetedFields;

private IDictionary<string, string>? _initiallyStoredAttributeValues;
private IDictionary<string, string>? _requestAttributeValues;
private IDictionary<string, string>? _finallyStoredAttributeValues;
private IDictionary<string, object?>? _initiallyStoredAttributeValues;
private IDictionary<string, object?>? _requestAttributeValues;
private IDictionary<string, object?>? _finallyStoredAttributeValues;

public ResourceChangeTracker(IJsonApiRequest request, ITargetedFields targetedFields)
{
Expand Down Expand Up @@ -50,15 +49,14 @@ public void SetFinallyStoredAttributeValues(TResource resource)
_finallyStoredAttributeValues = CreateAttributeDictionary(resource, _request.PrimaryResourceType!.Attributes);
}

private IDictionary<string, string> CreateAttributeDictionary(TResource resource, IEnumerable<AttrAttribute> attributes)
private IDictionary<string, object?> CreateAttributeDictionary(TResource resource, IEnumerable<AttrAttribute> attributes)
{
var result = new Dictionary<string, string>();
var result = new Dictionary<string, object?>();

foreach (AttrAttribute attribute in attributes)
{
object? value = attribute.GetValue(resource);
string json = JsonSerializer.Serialize(value);
result.Add(attribute.PublicName, json);
result.Add(attribute.PublicName, value);
}

return result;
Expand All @@ -71,21 +69,21 @@ public bool HasImplicitChanges()
{
foreach (string key in _initiallyStoredAttributeValues.Keys)
{
if (_requestAttributeValues.TryGetValue(key, out string? requestValue))
if (_requestAttributeValues.TryGetValue(key, out object? requestValue))
{
string actualValue = _finallyStoredAttributeValues[key];
object? actualValue = _finallyStoredAttributeValues[key];

if (requestValue != actualValue)
if (!Equals(requestValue, actualValue))
{
return true;
}
}
else
{
string initiallyStoredValue = _initiallyStoredAttributeValues[key];
string finallyStoredValue = _finallyStoredAttributeValues[key];
object? initiallyStoredValue = _initiallyStoredAttributeValues[key];
object? finallyStoredValue = _finallyStoredAttributeValues[key];

if (initiallyStoredValue != finallyStoredValue)
if (!Equals(initiallyStoredValue, finallyStoredValue))
{
return true;
}
Expand Down