多个Response.writeAsync调用 [英] Multiple Response.writeAsync Calls
问题描述
我一直在研究Asp.Net Security,发现了一些令人惊讶的代码:
I have been researching Asp.Net Security and I found some surprising 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>");
令我惊讶的是,短字符串多次调用WriteAsync.
What surprised me is the multiple calls to WriteAsync with short strings.
我本可以使用具有String.Format或StringBuilder的模板来连接字符串,然后在一次调用中将其写入:
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);
我观察到的差异
- 我的代码更容易修改.
- 我还有一些空白.
- 我的代码使用较大的硬编码字符串,而不是一堆小的硬编码字符串.
- 我使用String.Format,与串联相比,它可能对性能有影响.
-
如果应避免字符串串联,则应将该部分分解:
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>"
- 为什么这样做?
- 它如何影响性能?
- 什么时候应该等待而不是Response.Write来调用Response.WriteAsync?
- 应多久调用一次Response.WriteAsync?
- 尽可能少地处理少量数据
- 仅当大量文本准备就绪时
- 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
推荐答案
我创建了一个Azure网站(在Basic 1-1 Small Instance上运行)对此进行基准测试.然后,我使用 https://loader.io 上的免费服务在1分钟内以100个用户/秒的速度运行了每个测试.
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.
我以不同的顺序运行了每个测试3次.每次测试的时间间隔都在200毫秒之内.
I ran each test 3 times in different orders. The times for each test run were within 200ms of each other.
结果很明显: StringBuilder取得了重大胜利.每个异步调用的开销都远远超过了任何形式的字符串连接的开销(甚至String.Format的性能也比多个异步调用要好).
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-具有String.Format的WriteAsync
- 9265毫秒-WriteAsync
这是每个测试的代码:
// 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()); }
因此,尽管旧的"Response.Write"要比具有同步请求的StringBuilder快,但"await Response.WriteAsync"要慢得多(由于异步开销).
So although the old "Response.Write" is faster than StringBuilder with synchronous requests, "await Response.WriteAsync" is much slower (because of the async overhead).
这篇关于多个Response.writeAsync调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
出于讨论目的.假设这是在平均同时有10,000个活动用户的Web服务器的上下文中进行的,因此性能非常重要.
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.