Skip to content

Commit fbe1d1b

Browse files
committed
add benchmarks for IsRelationshipPath
1 parent 6568c37 commit fbe1d1b

File tree

5 files changed

+75
-10
lines changed

5 files changed

+75
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```ini
2+
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
3+
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
4+
.NET Core SDK=2.1.4
5+
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
6+
DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
7+
```
8+
9+
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
10+
| ---------- | --------: | ---------: | ---------: | -----: | --------: |
11+
| UsingSplit | 421.08 ns | 19.3905 ns | 54.0529 ns | 0.4725 | 744 B |
12+
| Current | 52.23 ns | 0.8052 ns | 0.7532 ns | - | 0 B |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Attributes.Exporters;
3+
4+
namespace Benchmarks.JsonApiContext
5+
{
6+
[MarkdownExporter, MemoryDiagnoser]
7+
public class PathIsRelationship_Benchmarks
8+
{
9+
private const string PATH = "https://example.com/api/v1/namespace/articles/relationships/author/";
10+
11+
[Benchmark]
12+
public void Current()
13+
=> JsonApiDotNetCore.Services.JsonApiContext.PathIsRelationship(PATH);
14+
15+
[Benchmark]
16+
public void UsingSplit() => UsingSplitImpl(PATH);
17+
18+
private bool UsingSplitImpl(string path)
19+
{
20+
var split = path.Split('/');
21+
return split[split.Length - 2] == "relationships";
22+
}
23+
}
24+
}

benchmarks/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using BenchmarkDotNet.Running;
2+
using Benchmarks.JsonApiContext;
23
using Benchmarks.LinkBuilder;
34
using Benchmarks.Query;
45
using Benchmarks.RequestMiddleware;
@@ -12,7 +13,8 @@ static void Main(string[] args) {
1213
typeof(JsonApiSerializer_Benchmarks),
1314
typeof(QueryParser_Benchmarks),
1415
typeof(LinkBuilder_GetNamespaceFromPath_Benchmarks),
15-
typeof(ContainsMediaTypeParameters_Benchmarks)
16+
typeof(ContainsMediaTypeParameters_Benchmarks),
17+
typeof(PathIsRelationship_Benchmarks)
1618
});
1719
switcher.Run(args);
1820
}

benchmarks/RequestMiddleware/ContainsMediaTypeParameters_Benchmarks.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using BenchmarkDotNet.Attributes;
32
using BenchmarkDotNet.Attributes.Exporters;
43
using JsonApiDotNetCore.Internal;

src/JsonApiDotNetCore/Services/JsonApiContext.cs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using JsonApiDotNetCore.Builders;
54
using JsonApiDotNetCore.Configuration;
6-
using JsonApiDotNetCore.Extensions;
75
using JsonApiDotNetCore.Internal;
86
using JsonApiDotNetCore.Internal.Generics;
97
using JsonApiDotNetCore.Internal.Query;
@@ -65,23 +63,53 @@ public IJsonApiContext ApplyContext<T>(object controller)
6563
throw new JsonApiException(500, $"A resource has not been properly defined for type '{typeof(T)}'. Ensure it has been registered on the ContextGraph.");
6664

6765
var context = _httpContextAccessor.HttpContext;
68-
var requestPath = context.Request.Path.Value;
6966

7067
if (context.Request.Query.Count > 0)
7168
{
7269
QuerySet = _queryParser.Parse(context.Request.Query);
7370
IncludedRelationships = QuerySet.IncludedRelationships;
7471
}
7572

76-
var linkBuilder = new LinkBuilder(this);
77-
BasePath = linkBuilder.GetBasePath(context, _controllerContext.RequestEntity.EntityName);
73+
BasePath = new LinkBuilder(this).GetBasePath(context, _controllerContext.RequestEntity.EntityName);
7874
PageManager = GetPageManager();
79-
80-
var pathSpans = requestPath.SpanSplit('/');
81-
IsRelationshipPath = pathSpans[pathSpans.Count - 2].ToString() == "relationships";
75+
IsRelationshipPath = PathIsRelationship(context.Request.Path.Value);
8276

8377
return this;
8478
}
79+
80+
internal static bool PathIsRelationship(string requestPath)
81+
{
82+
// while(!Debugger.IsAttached) { Thread.Sleep(1000); }
83+
const string relationships = "relationships";
84+
const char pathSegmentDelimiter = '/';
85+
86+
var span = requestPath.AsSpan();
87+
88+
// we need to iterate over the string, from the end,
89+
// checking whether or not the 2nd to last path segment
90+
// is "relationships"
91+
// -2 is chosen in case the path ends with '/'
92+
for(var i = requestPath.Length - 2; i >= 0; i--)
93+
{
94+
// if there are not enough characters left in the path to
95+
// contain "relationships"
96+
if(i < relationships.Length)
97+
return false;
98+
99+
// we have found the first instance of '/'
100+
if(span[i] == pathSegmentDelimiter)
101+
{
102+
// in the case of a "relationships" route, the next
103+
// path segment will be "relationships"
104+
return (
105+
span.Slice(i - relationships.Length, relationships.Length)
106+
.SequenceEqual(relationships.AsSpan())
107+
);
108+
}
109+
}
110+
111+
return false;
112+
}
85113

86114
private PageManager GetPageManager()
87115
{

0 commit comments

Comments
 (0)