-
-
Notifications
You must be signed in to change notification settings - Fork 158
Feature/#258 #271
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
Feature/#258 #271
Conversation
… string as Span<char> given a char delimeter. Language version updated to 7.2 due to need for ref Struct to utilize Span<T> as a field.
…estMiddleware, JsonApiContext, and QueryParser to utilize new SpanSplitter library to avoid unnecessary intermediate string creation.
…'s logic. ContentNegotion tests pass.
This feature requires language version 7.2 for use of ref Struct to allow for usage of Span as a field. Appveyor build currently failing due to this. |
Thanks for doing this! I'll carve out some time soon to work on a review. Looks like the Travis build is blocked by travis-ci/travis-ci#8990 I'll do some research and see if there is a workaround, but I'm not terribly concerned as long as Travis is working on a fix. If we have to I can probably move the linux build over to CircleCI or something. I'm also interested in writing some benchmarks to quantify the impact. If you wanted to try it out, you can take a look at some of the other benchmarks we have: https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/benchmarks ... Otherwise, I'll probably just add on to your PR when I have some time. |
Sure, I can do some benchmarks. Ill work on that next. Thanks. |
…ming to be more accurate
…pdates usages and unit tests to use extension method.
} | ||
|
||
return nSpace; | ||
return sb.ToString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interestingly, as written this performs almost identically to the Split
version. I've found one optimization that cuts this in half by removing the StringBuilder
. I also think we can eliminate most of the allocations within SpanSplitter
since I don't think it needs to be stateful. We really just need to read over a string and find the place where /{entityName}
exists (followed by /
or EOL
), then we Slice
everything before that. So, we shouldn't need to allocate anything until the final .Slice(/*...*/).ToString()
. I'll push my changes sometime this weekend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the results I get when running benchmarks (will submit code soon):
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
.NET Core SDK=2.1.4
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
Job-LPGSMZ : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
LaunchCount=3 TargetCount=20 WarmupCount=10
Method | Mean | Error | StdDev | Median | Gen 0 | Allocated |
---|---|---|---|---|---|---|
UsingSplit (original) | 1,257.5 ns | 36.05 ns | 76.83 ns | 1,229.1 ns | 0.9251 | 1456 B |
UsingSpanWithStringBuilder (current PR) | 1,770.0 ns | 68.65 ns | 144.81 ns | 1,767.0 ns | 0.9460 | 1488 B |
UsingSpanWithoutStringBuilder | 871.3 ns | 17.22 ns | 36.69 ns | 858.9 ns | 0.4368 | 688 B |
UsingSpanWithoutSpanSplitter | 317.2 ns | 25.54 ns | 56.06 ns | 300.5 ns | 0.0863 | 136 B |
d67f22d
to
85bb9f4
Compare
const char delimeter = ';'; | ||
var subSpans = mediaType.SpanSplit(delimeter); | ||
if (subSpans.Count == 0) return false; | ||
return subSpans.Count == 2 && subSpans[0].ToString() == Constants.ContentType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, it appears that the simple overhead of the SpanSplitter
type definition and its members has enough cost that we don't actually improve the performance. In general, it appears we can get much better performance by not creating a new abstraction. While I feel like your approach was elegant, it unfortunately did not have the desired result.
In the coming commit, I was able to get this down to 0 allocations.
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
.NET Core SDK=2.1.4
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
Method | Mean | Error | StdDev | Gen 0 | Allocated |
---|---|---|---|---|---|
UsingSplit | 157.11 ns | 2.9374 ns | 2.6039 ns | 0.2134 | 336 B |
UsingSpan (Current PR) | 364.75 ns | 7.1680 ns | 6.3542 ns | 0.2389 | 376 B |
Proposal (6568c37) | 37.91 ns | 0.7256 ns | 0.6787 ns | - | 0 B |
IsRelationshipPath = path[path.Length - 2] == "relationships"; | ||
|
||
var pathSpans = requestPath.SpanSplit('/'); | ||
IsRelationshipPath = pathSpans[pathSpans.Count - 2].ToString() == "relationships"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
.NET Core SDK=2.1.4
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
Method | Mean | Error | StdDev | Gen 0 | Allocated |
---|---|---|---|---|---|
Original | 421.08 ns | 19.3905 ns | 54.0529 ns | 0.4725 | 744 B |
Current PR | 697.65 ns | 11.9282 ns | 11.1576 ns | 0.5999 | 944 B |
Proposal (fbe1d1b) | 52.23 ns | 0.8052 ns | 0.7532 ns | - | 0 B |
Thanks @crfloyd for getting this started. Sorry we couldn't take all your changes but your efforts are greatly appreciated! Let me know if you have any questions/concerns over the changes. |
No problem. Happy to help. |
Closes #258
FEATURE