Avoid memory allocation and improve performance, part 1#522
Avoid memory allocation and improve performance, part 1#522DaniilSokolyuk wants to merge 2 commits intoreactjs:masterfrom
Conversation
|
Thanks for starting this conversation. It looks like there are a few breaking changes (like making component disposable), which could be a problem. This PR is also quite large, it would be better to split up optimization’s into smaller PRs so we can discuss each change :) Otherwise as is it’s going to be a bit before we can work through this one. |
|
@dustinsoftware i think disposable component is the smallest problem :) |
1cc32fb to
3b5e5c9
Compare
|
This is probably the largest pull request we've ever received :) Thanks for working on it. There's a lot of changes that change |
bbffdde to
60d96bb
Compare
|
@Daniel15 Thanks you, splitted, after this PR I will create another PR |
|
@Daniel15, @dustinsoftware what about this PR? I have already split this request and prepared the |
|
When you’re ready, please open some smaller pull requests so that we can
review and merge those changes individually :)
…On Wed, Mar 28, 2018 at 12:08, Daniil Sokolyuk ***@***.***> wrote:
@Daniel15 <https://github.com/Daniel15>, @dustinsoftware
<https://github.com/dustinsoftware> what about this PR? I have already
split this request and prepared the
second part
<https://github.com/DaniilSokolyuk/React.NET/tree/avoid-memory-allocations-part2>
We are already using these changes and significantly reduced the hits in
LOH and FullGC
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#522 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA5hFmwUqVYqIUKtOtV4BGc4NSQPnk2dks5ti9-OgaJpZM4S5xkt>
.
|
|
@dustinsoftware i am already split this PR, there are only 227 new lines here, and about 60% this lines is replace string.format to TextWriter.Write |
60d96bb to
bdda3e6
Compare
dustinsoftware
left a comment
There was a problem hiding this comment.
Thanks for submitting this PR! There are some good changes in here but we'll need to break this up into separate PR's even further before we can continue (if you don't have time to do that, let us know, and we can pick out some of these changes and test them individually when we have time).
From what I could tell, there are five separate improvements here:
- Use new component ID generation instead of Guid.NewGuid
- Using
StringBuilder/TextWriterinstead ofstring.Formatto generate HTML - Cache component name validation
- Optionally skip component exists checks
- Update JSON.NET version
With each of these improvements it would be great to see how much these changes affected the benchmarks instead of testing all of them at once. It could be that some of these changes made a significant difference (such as using StringBuilder) and others not so much (such as changing how IDs are generated).
Let me know if you'd like some more clarification or help moving this PR forward!
| { | ||
| tag.Attributes.Add("nonce", Environment.Configuration.ScriptNonceProvider()); | ||
| writer.Write(" nonce=\""); | ||
| writer.Write(Environment.Configuration.ScriptNonceProvider()); |
There was a problem hiding this comment.
I see that we're manually constructing HTML tags in existing code.. as long as we're not passing user-generated data to these methods, this should be OK (although it would be nice to sanitize the data going into these attributes just in case)
There was a problem hiding this comment.
yea, original tag builder has a similar code https://github.com/aspnet/Mvc/blob/feed0fea2c5280264ac000947b3fee542d7d9e6a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Rendering/TagBuilder.cs#L336
src/React.Core/React.Core.csproj
Outdated
| <PackageReference Include="JSPool" Version="3.0.1" /> | ||
| <PackageReference Include="MsieJavaScriptEngine" Version="2.2.2" /> | ||
| <PackageReference Include="Newtonsoft.Json" Version="9.0.1" /> | ||
| <PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> |
There was a problem hiding this comment.
Is bumping Json.NET required for this PR?
There was a problem hiding this comment.
No, split PR fail, bumping JSON.NET need in part 2 for use ArrayPool, fixed
| /// <param name="exceptionHandler">A custom exception handler that will be called if a component throws during a render. Args: (Exception ex, string componentName, string containerId)</param> | ||
| /// <returns>HTML</returns> | ||
| public virtual string RenderHtml(bool renderContainerOnly = false, bool renderServerOnly = false, Action<Exception, string, string> exceptionHandler = null) | ||
| public string RenderHtml(bool renderContainerOnly = false, bool renderServerOnly = false, Action<Exception, string, string> exceptionHandler = null) |
There was a problem hiding this comment.
Removing virtual is a breaking change, can this be avoided?
There was a problem hiding this comment.
we can return virtual but it does not make sense and can cause errors
because all methods and extensions uses overload with textwriter
src/React.Core/ReactComponent.cs
Outdated
| { | ||
| var isValid = componentName.Split('.').All(segment => _identifierRegex.IsMatch(segment)); | ||
| if (!isValid) | ||
| //TODO: cache |
There was a problem hiding this comment.
Let's remove any TODOs before merging this in
| ? string.Format("ReactDOMServer.renderToStaticMarkup({0})", GetComponentInitialiser()) | ||
| : string.Format("ReactDOMServer.renderToString({0})", GetComponentInitialiser()); | ||
| html = _environment.Execute<string>(reactRenderCommand); | ||
| stringWriter.Write(renderServerOnly ? "ReactDOMServer.renderToStaticMarkup(" : "ReactDOMServer.renderToString("); |
There was a problem hiding this comment.
This doesn't look thread safe - multiple threads could call Write at the same time to this shared instance. Keeping StringWriter scoped to this method instead of using a static field would solve this.
There was a problem hiding this comment.
_sharedStringWriter is marked as ThreadStatic and this is unique for each thread. (corefx use similar strategy, https://github.com/dotnet/corefx/blob/master/src/Common/src/System/IO/StringBuilderCache.cs) , I am reuse evertything for reduce allocations.
There was a problem hiding this comment.
Oh ok, did not know about ‘ThreadStatic’ :)
|
@dustinsoftware, yes you right, but previous ID generating is ugly, we are allocate GUID, after allocate byte array and 1-3 string... my implementation used 1 string, |
| var html = string.Empty; | ||
| if (!renderContainerOnly) | ||
| { | ||
| var stringWriter = _sharedStringWriter; |
There was a problem hiding this comment.
This variable declaration is unnecessary - you can just use _sharedStringWriter here and below:
if (_sharedStringWriter == null)
_sharedStringWriter = new StringWriter(new StringBuilder(512));There was a problem hiding this comment.
Also, the Write method on StringWriter just calls the wrapped StringBuilder, so it would be better to just use StringBuilder.
There was a problem hiding this comment.
- This variable is not unnecessary, because access to ThreadStatic variable is slow http://tips.x-tensive.com/2008/10/cost-of-threadstatic-attribute.html
- StringBuilder is not implement TextWriter, we cant use StringBuilder
| { | ||
| _sharedStringWriter = | ||
| stringWriter = | ||
| new StringWriter(new StringBuilder(512)); |
There was a problem hiding this comment.
This number seems unnecessarily large and might allocate more space than necessary for simple components, let's leave it unset unless there is a noticeable benefit to specifying a large value
There was a problem hiding this comment.
In part 2 i will remove all the StringWriter, StringBuilders and threadStatic and replace them with my own buffer based on ArrayPool, https://github.com/DaniilSokolyuk/React.NET/blob/c271afeb369875194a31c18bbbe6b76fac947d51/src/React.Core/ReactComponent.cs#L150
There may be consuming code that depends on the current ID format, so this isn't a good reason to change it. We unfortunately don't know how consumers are using this project and want to avoid surprises between version updates. |
|
Thanks for reviewing this, @dustinsoftware! |
|
@dustinsoftware , we can announce this change or publish major version |
|
That’s not the point I was trying to make... I am hesitant to merge in changes for minor improvements that are technically breaking changes. The discussion on this PR is getting too long, please open a new one for each individual change (such as ones the bulleted list above) and we can discuss them one at a time :) |
Hi @Daniel15, for more than a year we have been using this library, and recently noticed problems with allocations and object hits in Large Object Heap if the component is large or a large props is serialized
Goals
Branch with banchmarks here https://github.com/DaniilSokolyuk/React.NET/tree/bench
Note the difference between the nano seconds and microseconds
Before, Small objects
After, Small objects
Before, Large objects
After, Large objects