问题
I have been researching Asp.Net Security and I found some surprising code:
Strange Code?
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("<html><body>");
await context.Response.WriteAsync("An remote error has occured: " + context.Request.Query["ErrorMessage"] + "<br>");
await context.Response.WriteAsync("<a href=\"/\">Home</a>");
await context.Response.WriteAsync("</body></html>");
What surprised me is the multiple calls to WriteAsync with short strings.
What I would have done
I would have used a template with String.Format or a StringBuilder to concatenate the strings and then write that in a single call:
var template = @"
<html><body>
An remote error has occured:{0}<br>
<a href=\"/\">Home</a>
</body></html>
";
var html = string.format(template, context.Request.Query["ErrorMessage"]);
await context.Response.WriteAsync(html);
The differences I observe
- My code is much easier to modify.
- I've got some extra white-space.
- My code uses a larger hard-coded string instead of a bunch of small hard-coded strings.
- I use String.Format which may have a performance hit compared to concatenation.
If string concatenation should be avoided, this part should be broken up:
"An remote error has occured: " + context.Request.Query["ErrorMessage"] + "<br>"
Questions
For the purposes of discussion. Let's assume that this is in the context of a web server with an average of ~10,000 simultaneous active users: So performance is important.
- Why is this done like this?
- How does it affect performance?
- When should await Response.WriteAsync be called instead of Response.Write?
- How often should Response.WriteAsync be called?
- As often as possible with tiny amounts of data
- Only when a large amount of text is ready
回答1:
I created an Azure website (running on Basic 1 - 1 Small Instance) to benchmark this. Then I used the free service at https://loader.io to run each test at 100 users/second over 1 minute.
I ran each test 3 times in different orders. The times for each test run were within 200ms of each other.
Results:
The results were clear: StringBuilder won significantly. The cost of each async call far out weighs the cost of any form of string concatenation (even String.Format performed better than the multiple async calls).
- 1992ms - StringBuilder.Append
- 3071ms - StringBuilder.AppendFormat
- 4257ms - WriteAsync with String.Format
- 9265ms - WriteAsync
Here is the code for each test:
// Do not write this code - It is ugly and performs terribly
private async Task TestWriteAsync(HttpContext context)
{
var r = context.Response;
var id = "id";
var size = "12";
var text = "text";
await r.WriteAsync("<div style='display:none'>");
for (int i = 0; i < 10000; i++)
{
await r.WriteAsync("<li id='");
await r.WriteAsync(id);
await r.WriteAsync("' style='font-size:");
await r.WriteAsync(size);
await r.WriteAsync("'>");
await r.WriteAsync(text);
await r.WriteAsync("</li>");
}
await r.WriteAsync("</div>");
}
// This is much better, but still not great
private async Task TestWriteAsyncFormat(HttpContext context)
{
var r = context.Response;
var id = "id";
var size = "12";
var text = "text";
var template = "<li id='{0}' style='font-size:{1}'>{2}</li>";
await r.WriteAsync("<div style='display:none'>");
for (int i = 0; i < 10000; i++)
{
await r.WriteAsync(string.Format(template, id, size, text));
}
await r.WriteAsync("</div>");
}
// The Performance Winner, but ugly code
private async Task TestStringBuilder(HttpContext context)
{
var sb = new StringBuilder();
var id = "id";
var size = "12";
var text = "text";
sb.Append("<div style='display:none'>");
for (int i = 0; i < 10000; i++)
{
sb.Append("<li id='");
sb.Append(id);
sb.Append("' style='font-size:");
sb.Append(size);
sb.Append("'>");
sb.Append(text);
sb.Append("</li>");
}
sb.Append("</div>");
await context.Response.WriteAsync(sb.ToString());
}
// Decent performance and Clean Code
private async Task TestStringBuilderFormat(HttpContext context)
{
var sb = new StringBuilder();
var id = "id";
var size = "12";
var text = "text";
var template = "<li id='{0}' style='font-size:{1}'>{2}</li>";
sb.Append("<div style='display:none'>");
for (int i = 0; i < 10000; i++)
{
sb.AppendFormat(template, id, size, text);
}
sb.Append("</div>");
await context.Response.WriteAsync(sb.ToString());
}
So although the old "Response.Write" is faster than StringBuilder with synchronous requests, "await Response.WriteAsync" is much slower (because of the async overhead).
Test Screenshots:
回答2:
I found that Link that might answer some of your questions about Response.Write : http://www.dotnetperls.com/response-write
It seems that a lot of shorts strings is faster . I hope it works the same as Response.WriteAsync.
来源:https://stackoverflow.com/questions/33940903/multiple-response-writeasync-calls