scanham/FlatplanRenderer

Perf issues in RC1

Opened this issue · 4 comments

The primary thing that's going wrong here is that too much memory is being allocated. It's not anything you've done wrong in your app to cause this, but you have shown us a gap in our perf testing.

The problematic code is cases like this:

<a model="@Model" title="@Model.Headline">@Model.Headline</a>

When there's dynamic content inside of an attribute value like title, we need to evaluate/ToString() and then HTML encode the content, and then shove it into a buffer.

What Razor is doing wrong is encoding it character-by-character. Combined with our buffering system, this is very inefficient - each character gets ToString()-ed again and then stuck back in the buffer. So instead of one string allocation that can't avoid and a single entry in a list, we end up with N string allocations and N entries in a list. Not good.

Again, this isn't something you've done wrong, it's out bug and a gap in our perf testing. Where we're testing cases like this, the amount of dynamic content is small.

Here's an allocation profile for 300 requests (after doing a warmup) on RC1:
image

Ouch! That's 2.72mb of allocations per request. At 200 rps, that's 544mb/s of memory the GC has to clean up.

Detailed view of the same for string:
image

Detailed view of the same of object[] (backing store of our buffer):
image

We unfortunately haven't fixed this issue yet for RC2. I'll update with numbers once I have em.

Wow! Thanks for the details 👍

Nice to know there is a reason for the numbers I'm seeing. If you need any other samples let me know!

Here's an update - with a few changes:
1). Don't encode char-by-char until we're writing to the final buffer
2). Removed a few unnecessary ViewBuffer[] instances per request
3). Tune sizes of ViewBuffer[] to avoid wastage
4). Avoid buffers/stringbuilders and reuse where possible

image

The profile will look a little different by nature because we've made a host of tweaks since RC1.

I can get about 350 rps with this now on my box


We'll continue to work on improving this until we release, I've got to clean up what I've been working on to check it in. I've already identified a few additional things from these data.

Sweet - thats def an improvement! That is a big drop in Object[] allocations. There are a lot of TagHelper related items there - does Razor use those for rendering just about everything or is that mostly from the few TagHelpers used in the example?

The taghelper related items are used every time a ProcessAsync method is called for a taghelper. We're actively working on driving these down: aspnet/Razor#692