Performance Stub: getting all subtype items from a list

Performance Stubs

These blog posts are simply times I wanted to identify the fastest method for accomplishing a particular goal. As I write the code, I like to make some light notes of alternatives while driving forward with the first implementation that makes it from my brain to my fingers.

When I get the chance, I can go back and flesh out the two versions and drop them into some basic Stopwatch timing to determine which is better in terms of raw speed. Factoring those results with clarity of code, I have a method I will likely choose the next time I need the same feature.

Goal

Given a particular IEnumerable, find all the elements that are of a particular type (subclass, in this case).

Method 1: Select As, Where Not Null

The as operator with convert between two types with one nice tweak. If the cast cannot happen, it results in null.

In this version, we massage the list by hitting all items with an as cast and then filter out the ones that didn’t cast successfully.

public static IEnumerable<SubSomething> SelectAsWhereNotNull(IEnumerable<Something> source) {
    return source.Select(item => item as SubSomething).Where(item => item != null);
}

Method 2: Where Is, Cast

There is also the is operator. Instead of returning a cast result, it simply returns a boolean for whether the instance can be cast as a given type. From there, we use another Linq extension method: Cast<TResult>.

In this version, we filter the original list to just those that can make the cast successfully, then Cast those items to the final desired type.

public static IEnumerable<SubSomething> WhereIsCast(IEnumerable<Something> source) {
    return source.Where(item => item is SubSomething).Cast<SubSomething>();
}

Test

I built up a list (n=1,000,000) of parent type Something but make every other item a SubSomething (which is a subclass of Something). Then, I run that list through both methods 1000 iterations each.

Results

Clarity

Neither method is particularly difficult to decipher. The Select/Where method takes a few more characters, but not enough to hurt.

Speed

It takes quite a list for these tests to even register with average ticks. That said, there is little gain to be had here over small lists. Once list lengths are sufficiently large, method 1 took the lead.

For n=1,000,000 and 1000 iterations (run on 2.4GHz AMD Phenom 9750 with 8GB RAM)
Average Method
1.0 ticks {IEnumerable<Something>}.Select(item => item as SubSomething).Where(item => item != null)
1.7 ticks {IEnumerable<Something>}.Where(item => item is SubSomething).Cast<SubSomething>()

Performance Testing Framework

The original source for the test framework code evolved from a StackOverflow answer about converting byte arrays to hexadecimal strings. –That code is available in a bitbucket repo. I have adapted it here for these two methods.– That code is available in the framework repo.

Rather than post another repo for each test, I am going to abstract out a testing framework for these posts. That repo will show up soon and I will update this post accordingly.

Update 2013-01-22

I finally refined the testing framework code I was using. While I won’t pretend this code couldn’t use some improvements, feel free to hack away with it. The code is available on GitHub.

To add a new case to an existing test:

  1. Add the new static method (Func<byte[], string>) to /Tests/ConvertByteArrayToHexString/Test.cs.
  2. Add that method’s name to the TestCandidates return value in that same class.
  3. Make sure you are running the input version you want, sentence or text, by toggling the comments in GenerateTestInput in that same class.
  4. Hit F5 and wait for the output (an HTML dump is also generated in the /bin folder).

To add a new test fixture entirely, just write up something that implements IPerformanceTest in that project’s namespace.

Closing and Re-opening tabs in Visual Studio with Ctrl+W

Visual Studio 2017 Undo-Close Update: The Productivity Power Tools have spun off into a bunch of more-focused extensions. To get Undo Close in Visual Studio 2017, you will want the Power Commands extension now.

Visual Studio 2013 Undo-Close Update: Since the prior options for re-opening closed tabs fell apart with the release of Visual Studio 2013, you will need the newly released Productivity Power Tools 2013.

Update: now with the ability to re-open closed tabs with Ctrl+Shift+T. This also allows you to re-open tabs closed by a project file reload, which is fantastic!

Ever tried to close a tab in Visual Studio 2010/2012 with Ctrl+W. If so, you find yourself selecting the current word in your text editor (Edit.SelectCurrentWord). I don’t use that shortcut, though I could see it being handy over my usual Ctrl+Shift+Right-/Left-Arrow. I do, however, use Ctrl+W to close windows/tabs in just about every other program I use. In order to make that shortcut work for your Visual Studio editing, you just need to assign it to File.Close instead.

For the visual, here’s a snapshot similar to what you will want (note: I already made this change before snapping a pic, so yours may look slightly different).

Visual Studio Keyboard Options

  1. Go into the Tools->Options menu.
  2. Select Environment->Keyboard in that window.
  3. Type “File.Close” in the “Show commands containing” field and select it in the list when it shows.
  4. Choose “Text Editor” for the “Use new shortcut in” field.
  5. In “Press shortcut keys” field, press Ctrl+W.
  6. Click “Assign” to make it happen.
  7. Close the Options window with the “OK” button.

Now, you can happily close windows with Ctrl+W in Visual Studio.

Next step

Figure out how to get Ctrl+Shift+T to bring back the last closed window. If I find a way, I’ll post it.

Update (2012-02-16): Re-open previously closed tabs (with PowerCommands for Visual Studio 2010 [also works for Visual Studio 2012])

As Sergey Vlasov pointed out in a comment, the PowerCommands for Visual Studio 2010 adds the ability to “Undo Close” on previously closed tabs. Since it adds them as commands to Visual Studio, you can also override the default keyboard shortcut (Ctrl+Shift+Z) to a more web-browser-esque shortcut: Ctrl+Shift+T. If you already had this installed for Visual Studio 2010 when you started using Visual Studio 2012, just install the PowerCommands for Visual Studio 2010 again and it will offer to add it to Visual Studio 2012. You will still need to make the shortcut assignment in 2012.

Visual Studio Keyboard Options (Edit.UndoClose)

Just like with your favorite web browser, this will cycle through previously closed tabs, resurrecting them from their graves and restoring your cursor location to right where you left off when you closed them. Amazingly enough, this also works when Visual Studio closes your active windows when a project reloads.

Initial setup for a new ASP.NET MVC site in IIS7

Background

Over the years, I have spent far too many hours running the same set of commands against ASP.NET and ASP.NET MVC sites to bring them up to what I consider a starting point. Most of the time, I have to refresh myself about how to do at least one of them. This is a list of those commands in a central location for myself and anyone else to use. As with any good instructions, there is no guarantee you won’t completely destroy your server, site, or soul using these commands. I can only say they worked for me once.

  1. Remove unnecessary HTTP headers
    1. Server
    2. X-Powered-By
    3. X-AspNet-Version
    4. X-AspNetMvc-Version
  2. Adding dynamic content compression (e.g., GZIP)
  3. GZIP for JSON and JSONP content types (e.g., “application/json”)
  4. Complete appcmd command-line list

Remove unnecessary HTTP headers

By default, IIS and ASP.NET give you a few headers in each and every HTTP response sent out.

Server: Microsoft-IIS/7.5 (or 7.0, or whatever IIS version you have)
X-Powered-By: ASP.NET
X-AspNet-Version: 4.0.303319 (or 2.0.50727, or 1.1.4322, or whatever version you have)
X-AspNetMvc-Version: 3.0 (or 2.0, or 1.0, or whatever version you have)

People will rant and rave about removing these headers for security and whether that helps. I am firmly on the side of “not-why-I-care-at-all”. I always figure if I am a target, obscurity doesn’t buy me anything when someone with very little computing power can just throw packets exploiting all the major frameworks at me and see which stick (which happens even when you aren’t a real “target”). The reason I do care is that removing these headers strips out even more data that has to be sent between you and the end user. Even though the size difference is small, it applies to every single HTTP response handled by your site. For such simple one-time changes, I will gladly take the savings.

Server (the annoying one)

This HTTP header appears to be the most annoying to remove. In fact, short of an HTTP module, the common method for doing so is a new line in a [probably new] event handler in global.asax.cs (or global.asax.vb).

protected void Application_PreSendRequestHeaders() {
    HttpContext.Current.Response.Headers.Remove("Server");
}

It’s not as pretty as the rest of the removal stuff below, but it gets the job done. In fact, they could all be solved this way, but I tend to lean on IIS to do the heavy lifting when I can.

X-Powered-By (the more annoying web.config/IIS one)

Many places that offer suggestions on removing the X-Powered-By: ASP.NET header talk about using IIS. You can certainly do this if you have permission to do so on your hosting provider and like the IIS GUI. I prefer things that I can push out with source control, so I go the web.config route.

<system.webServer>
    <httpProtocol>
        <customHeaders>
            <remove name="X-Powered-By" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']"

The root source of this setting is actually found in your server’s applicationHost.config (%windir%\system32\inetsrv\config\applicationHost.config). If you have the ability to modify that file, you can remove it there to make it the default for all IIS sites.

<system.webServer>
    <httpProtocol>
        <customHeaders>
            <clear />
            <add name="X-Powered-By" value="ASP.NET" /> <!--Remove this line.-->
        </customHeaders>
        <redirectHeaders>
            <clear />
        </redirectHeaders>
    </httpProtocol>
</system.webServer>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']" /commit:apphost

X-AspNet-Version (the easy web.config one)

To make X-AspNet-Version: {your-aspnet-version} go away, you just need to tweak your site’s web.config file. In the <system.web> section, under <httpRuntime>, which is probably already there, just make sure the enableVersionHeader="false" attribute is added to it.

<system.web>
    <httpRuntime enableVersionHeader="false" />
</system.web>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.web/httpRuntime /enableVersionHeader:false

I’m guessing this could be done at the applicationHost.config level, though I have never tried it (no real excuse, just too scared to mess with such a vital config entry too much).

X-AspNetMvc-Version (the global.asax code one)

Obviously, if you are tweaking a non-MVC ASP.NET site, you can ignore this one.

To make X-AspNetMvc-Version: {your-aspnetmvc-version} go away, you need to add some code and redeploy your site. In your global.asax.cs (or global.asax.vb) file, you will need to add a single line to the application start event.

protected void Application_Start() {
    MvcHandler.DisableMvcResponseHeader = true;
    // ...
}

Adding dynamic content compression (e.g., GZIP)

Reducing the amount of data you send over the wire is pretty good idea, especially if you are targeting low-bandwidth users like mobile browsers. While GZIP has some performance impact on the server and client, it is negligible on most systems. To add this system to IIS, add in the “Dynamic Content Compression” role service under the “Web Server (IIS)” role in “Server Manager”. If I am doing it visually, I typically just follow along with this iis.net article on the process.

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/globalModules /+[name='DynamicCompressionModule',image='%windir%\System32\inetsrv\compdyn.dll'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/modules /+[name='DynamicCompressionModule',lockItem='true'] /commit:apphost

If this has already been installed, this will error out with a message about adding a duplication “DynamicCompressionModule” entry.

GZIP for JSON and JSONP content types (e.g., “application/json”)

[Update 2012-02-02] NOTE: If you have a load-balancer, you may need to figure out how to get it to allow these content types through as GZIP. In one situation where I added this setting to a series of servers behind F5’s BIG-IP load balancer, it still wouldn’t get GZIP out to the requester. BIG-IP was either stripping the request of Accept-Encoding: gzip or getting GZIP from IIS and decompressing it before letting it return to the original requester (I didn’t confirm what was happening, but I would assume the former since it is less CPU-intensive).

While getting GZIP compression is great for HTML, JavaScript, CSS, and most others with “Dynamic Content Compression” is great, it drops the ball on content types it doesn’t have explicit orders to manipulate. One such content type that should get compression is dynamically-generated JSON and JSONP.

While working on the [Sierra Trading Post API](http://dev.sierratradingpost.com/] I noticed that IIS with dynamic compression enabled doesn’t do anything to JSON content types, “application/json” (JSON) and “application/javascript” (JSONP). Compression is decided by the content types listed in applicationHost.config (%windir%\system32\inetsrv\config\applicationHost.config). By default, you get most of the types served by the average ASP.NET site: text/* (covering text/html). If you need to add more to this list, add them to the config file. It does not matter if they come before or after the <add mimeType="*/*" enabled="false" /> wildcard entry that is there by default.

<system.webServer>
    <httpCompression ... >
        ...
        <dynamicTypes>
            ...
            <add mimeType="application/json; charset=utf-8" enabled="true" />
            <add mimeType="application/javascript; charset=utf-8" enabled="true" />
        </dynamicTypes>
    </httpCompression>
</system.webServer>

Command line happiness:

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/javascript; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost

For some reason, these are not recognized without the charset=utf-8 part. As well, if you don’t notice a change right away, that’s normal; you must recycle the app pool first (TechNet has a nice article with info about app pool recycling and why it doesn’t happen here).

 %windir%\System32\inetsrv\appcmd recycle apppool /apppool.name:"{your-app-pool-name}"

Again, some people would suggest an HTTP module for handling this, but it seems like IIS would be better equiped to handle this since it is already doing so for the rest of my outbound content.

Complete appcmd command-line happiness

If you are following along at home, most of this stuff can be poked into server config files, either applicationHost.config or your site’s web.config (all but the Server: Microsoft-IIS/7.5 HTTP header). If you want the fire-and-forget solution, here are all the commands smashed together for your batch file use.

%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']"
%windir%\System32\inetsrv\appcmd.exe set config "{your-site-name}" /section:system.web/httpRuntime /enableVersionHeader:false
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/globalModules /+[name='DynamicCompressionModule',image='%windir%\System32\inetsrv\compdyn.dll'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/modules /+[name='DynamicCompressionModule',lockItem='true'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/javascript; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd recycle apppool /apppool.name:"{your-app-pool-name}"

If you want to do all this to applicationHost.config alone, you just swap out the first two lines from hitting “{your-site-name}” to pointing to the apphost config. Here they are all smashed together for that variant. NOTE: I have not confirmed that setting enableVersionHeader in applicationHost.config will work.

%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config /section:system.web/httpRuntime /enableVersionHeader:false /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/globalModules /+[name='DynamicCompressionModule',image='%windir%\System32\inetsrv\compdyn.dll'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/modules /+[name='DynamicCompressionModule',lockItem='true'] /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/javascript; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost
%windir%\System32\inetsrv\appcmd recycle apppool /apppool.name:"{your-app-pool-name}"