Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Allocations in FormReader #553

Closed
Closed
@rynowak

Description

@rynowak

There's a lot of overhead right now in FormReader

Data is from 3000 requests to https://github.com/aspnet/Performance/tree/dev/testapp/BigModelBinding (x64). The client is doing a form post of about 100 form fields - we kinda consider this the upper bound for the amount of data (and shape of data) that you'd want to put through MVC model binding.

image

Based on this profile, my napkin math shows about 77mb of allocations from our underlying representation (string + System.Collections.Generic.Dictionary<String, StringValues> + etc) and about 122mb coming from various overheads.

I'm excluding from the 122mb stuff that's in theory covered by @benaadams current work (byte/char buffers, etc).


Breaking this down further

Task<string> - 47mb

image
Cost of being async

Task<Nullable<KeyValuePair<string, string>>> - 28mb

image
Cost of being async

Dictionary<string, List<String>>, List<string>, string[] - 25mb 12mb 9mb

image
Scratch data used in KeyValueAccumulator to build the final Dictionary<string, StringValues>

Note that 5mb of string[] allocations are coming from a hashset in MVC, so I excluded it from this total

Some thoughts

We should consider a design for FormReader where we pass the accumulator around or store values in fields/properties instead of returning them via Task<T>. Using async plus Task<T> at such a chatty level is what causes all of this overhead. Using Task on the other hand can avoid this issue.

If we're in a state where we know the entire body is buffered in memory, we might want to just optimize that path to do a synchronous read. That's probably a simpler fix, and it will result in running much more efficient code without any async overhead.

We should also consider changing KeyValueAccumulator to just operate on Dictionary<string, StringValues directly. Having multiple values for the same fields is the less-common case. If we wanted to be smart about it, we could basically build List<T>'s resizing behavior into a StringValues.Add method (returns a StringValues since they are immutable), and then all cases would be pretty optimal.

Right now we're making the worst case the common case by allocating a List<string> and string[] for the common case of a single value per key.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions