多个Response.writeAsync调用

时间:2015-11-26 14:17:27

标签: c# asp.net performance asynchronous async-await

我一直在研究Asp.Net Security,我发现了一些令人惊讶的代码:

奇怪的代码?

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。

我会做什么

我会使用带有String.Format或StringBuilder的模板来连接字符串,然后在一次调用中编写它:

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,与串联相比,可能会有性能损失。
  • 如果应该避免字符串连接,则应该分解这部分:

    "An remote error has occured: " + context.Request.Query["ErrorMessage"] + "<br>"
    

问题

出于讨论的目的。让我们假设这是在Web服务器的上下文中,平均有大约10,000个活跃用户:所以性能很重要。

  • 为什么这样做?
  • 它如何影响表现?
  • 何时应该等待Response.WriteAsync而不是Response.Write?
  • 应该多久调用一次Response.WriteAsync?
    • 尽可能多地使用少量数据
    • 仅在准备好大量文字时

2 个答案:

答案 0 :(得分:4)

我创建了一个Azure网站(在Basic 1 - 1 Small Instance上运行)来对此进行基准测试。然后我使用https://loader.io的免费服务以每分钟100个用户的速度运行每个测试。

我按不同的顺序运行了3次测试。每次测试的时间都在200毫秒之内。

结果:

结果很清楚: StringBuilder赢了很多。每个异步调用的成本远远超过任何形式的字符串连接的成本(甚至String.Format比多个异步调用表现更好)。

  • 1992ms - StringBuilder.Append
  • 3071ms - StringBuilder.AppendFormat
  • 4257ms - 与String.Format一起使用WriteAsync
  • 9265ms - 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());
    }

所以虽然旧的&#34; Response.Write&#34;比同步请求的StringBuilder更快,&#34;等待Response.WriteAsync&#34;慢得多(因为异步开销)。

测试截图:

enter image description here

enter image description here

enter image description here

enter image description here

答案 1 :(得分:0)

我发现链接可能会回答您关于Response.Write的一些问题: http://www.dotnetperls.com/response-write

似乎很多短裤字符串更快。 我希望它与Response.WriteAsync一样。