5

Is it expensive?

I am developing an HtmlHelper that renders directly to Response.Output in order to save unnecesary string creation and I need to choose between:

<% Validator.RenderClient(Response.Output); %>

and

<% Validator.RenderClient(); %>

and get the textWriter from HttpContext.Current.Response

Santiago Corredoira
  • 47,267
  • 10
  • 52
  • 56

3 Answers3

3

@Dawkins

100 runs is too few, you need to run about 10000 times several times and repeat it and then take the average of that to get a reliable result. The margin of error is to big in your example, but it's the right way to go.

Here's what i did:

var results1 = new List<long>();
var results2 = new List<long>();

for (int j = 0; j < 100; j++)
{
    var sp = new System.Diagnostics.Stopwatch();

    // With HttpContext.Current: 
    sp.Start();
    for (int i = 0; i < 10000; i++)
    {
        HttpContext.Current.Response.Output.Write(i);
    }
    sp.Stop();

    results1.Add(sp.ElapsedTicks);

    // Without: 
    TextWriter output2 = HttpContext.Current.Response.Output;
    sp.Reset();

    sp.Start();
    for (int i = 0; i < 10000; i++)
    {
        output2.Write(i);
    }
    sp.Stop();

    HttpContext.Current.Response.Clear();

    results2.Add(sp.ElapsedTicks);
}

results1.Sort();
results2.Sort();

HttpContext.Current.Response.Write(string.Format("HttpContext.Current={0:0.000}ms, Local variable={1:0.000}ms, R={2:0.0%}<br/>", results1[results1.Count / 2] / (double)TimeSpan.TicksPerMillisecond, results2[results2.Count / 2] / (double)TimeSpan.TicksPerMillisecond, (double)results1[results1.Count / 2] / (double)results2[results2.Count / 2]));

Your result show that there's a 18% performance difference, which shows that it's more expensive but it off by 8%.

I re-ran the numbers several times and came up with a 10% difference with a error margin of less then 1%.

It stablaizes around:

HttpContext.Current=0,536ms, Local variable=0,486ms, R=110,2% 

Anyway, HttpContext.Current to pose a significant performance problem you'll need to call it way more than 10000 per request (the cost is largely made up by the Response.Write calls). And that's likely not going to happen.

John Leidegren
  • 59,920
  • 20
  • 131
  • 152
2

from reflector

public static HttpContext get_Current()
{
    return (ContextBase.Current as HttpContext);
}

calls ContextBase which calls

public static object HostContext
{
    get
    {
        object hostContext = 
          Thread.CurrentThread.GetIllogicalCallContext().HostContext;
        if (hostContext == null)
        {
            hostContext = GetLogicalCallContext().HostContext;
        }
        return hostContext;
    }

...

so there is a bit of threading 'stuff' going on; the specific I don't really know.

oglester
  • 6,605
  • 8
  • 43
  • 63
  • 4
    For some reason I find it interesting that they seem to prefer what the illogical call context returns over what the logical call context would return (whatever those things are). – Michael Burr Nov 14 '08 at 18:40
2

It is not intensive at all. I don't know why I didn't try this the first time:

        System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();

        // With HttpContext.Current:
        sp.Start();
        for (int i = 0; i < 100; i++)
        {
            HttpContext.Current.Response.Output.Write(i.ToString());
        }
        sp.Stop();
        long result1 = sp.ElapsedTicks;

        // Without:
        TextWriter output2 = HttpContext.Current.Response.Output;
        sp.Reset();
        sp.Start();
        for (int i = 0; i < 100; i++)
        {
            output2.Write(i.ToString());
        }
        sp.Stop();
        long result2 = sp.ElapsedTicks; 

And my computer results are around:

result1= 395 ticks result2= 332 ticks

So it is pretty fast!

Santiago Corredoira
  • 47,267
  • 10
  • 52
  • 56
  • I have issues with our way of conducting performance tests, see my answer below. Your right in your answer, but the results are not accurate (statistically viable). – John Leidegren Jan 08 '10 at 13:38