Welcome to MSDN Blogs Sign in | Join | Help

Benchmarking, C++, and C# Micro-optimizations

Two posts (1 2) on C# loop optimization got me thinking recently.

Thinking about what I did when I first joined Microsoft.

Way back in the spring of 1995 or so (yes, we did have computers back then, but the Internet of the time really *was* just a series of tubes), I was on the C++ compiler test team, and had just picked up the responsibility for running benchmark tests on various C++ compilers. I would run compilation speed and execution speed tests in controlled environments, so that we could always know where we were.

We used a series of “standard” benchmarks – such as Spec – and a few of our own.

Because execution speed was one of the few ways (other than boxes with lots of checkmarks) that you could differentiate your compiler from the other guy’s, all the compiler companies invested  resources at being faster at the benchmarks.

The starting point was to look at the benchmark source, the resultant IL, and the final machine code, and see if you could see any opportunity for improvement. Were you missing any optimization opportunities?

Sometimes, that wasn’t enough, so some compiler writers (*not* the ones I worked with) sometimes got creative.

You could, for example, identify the presence of a specific expression tree that just “happened to show up” in the hot part of of a benchmark, and bypass your usual code generation with a bit of hand-tuned assembly that did things a lot faster.

Or, with a little more work, you could identify the entire benchmark, and substitute another bit of hand-tuned assembly.

Or, perhaps that hand-tuned assembly doesn’t really do *all* the work it needed to, but took a few shortcuts but still managed to return the correct answer.

For some interesting accounts, please text “compiler benchmark cheating” to your preferred search engine.

As part of that work, I got involved a bit in the writing and evaluation of benchmarks, and I thought I’d share a few rules around writing and interpreting micro-benchmarks. I’ll speak a bit about the two posts – which are about looping optimizations in C# – along the way. Just be sure to listen closely, as I will be speaking softly (though not in the Rooseveltian sense…)

Rule 0: Don’t

There has always been a widespread assumption that the speed of individual language constructs matter. It doesn’t.

Okay, it does, but only in limited cases, and frankly people devote more time to it than it deserves.

The more productive thing is to follow the agile guideline and write the simplest thing that works. And note that “works” is a bit of a weasely word here – if you write scientific computing software, you may have foreknowledge about what operations need to be fast and can safely choose something more complicated, but for most development that is assuredly not true.

Rule 1: Do something useful

Consider the following:

void DoLoop()
{
    for (int x = 0; x < XMAX; x++)
    {
        for (int y = 0; y < YMAX; y++)
        {
        }
    }

}

void TimeLoop()
{
    // start timer
    for (int count = 0; count < 1000; count++)
    {
        DoLoop();
    }
    // stop timer
}

if XMAX is 1000, YMAX is 1000, and the total execution time is 0.01 seconds, what is the time spent per iteration?

Answer: Unknown.

The average C++ optimizer is smarter that this. That nested loop has no effect on the result of the program, so the compiler is free to optimize it out (the .NET JIT may not have time to do this).

So, you modify the loop to be something like:

void DoLoop()
{
    int sum;

    for (int x = 0; x < XMAX; x++)
    {
        for (int y = 0; y < YMAX; y++)
        {
            sum += y;
        }
    }
}

The loop now has some work done inside of it, so the loop can’t be eliminated.

Rule 2: No, really. Do something useful

However, the numbers won’t change. The call to DoLoop() has no side effects, so the entire call can be safely eliminated.

To make sure your loop is really a loop, there needs to be a side effect. The best bet is to have a value returned from the method and write it out to the console. This has the added benefit of giving you a way of checking whether things are working correct.

Rule 3: Benchmark != Real world

There are lurking effects that invalidate your results. Your benchmark is likely tiny and places very different memory demands on the system than your real program does.

Rule 4: Profile, don’t benchmark

 

C# loop optimization

If you are writing code that needs the utmost in speed, there is an improvement to be had using for rather than foreach. There is also improvement to be had using arrays rather than lists, and unsafe code and pointers rather than array indexing.

Whether this is worthwhile in a specific case depends exactly on what the code is doing. I don’t see a lot of point in spending time measuring loops when you could spend time measuring the actual code.

Posted by ericgu | 0 Comments
Filed under: ,

Holiday light project 2008...

I've been searching for a new project to do for this season's holiday lights. I typically have four or five ideas floating around my head, and this year is no different.

Lots of choices, so I've had to come up with a "rule" about new projects. The rule is that the project has to be in the neighborhood of effort-neutral. It already takes too long to put up (and worse, take down) the displays we already have, and I don't want to add anything that makes that worse. Oh, and they can't take too much power, because I'm already on a power budget.

Unless, it's, like, especially cool.

I had an idea that met all my criteria. It was small - small enough to be battery powered, if I did my power calculations properly, and was going to be pretty cool.

It was, unfortunately, going to be a fair pain-in-the-butt to build - the fabrication was a bit complex, and the plan was to build a number of identical pieces. Oh, and it required me to choose the perfect LEDs from the 15 thousand that Mouser carries.

So, I hadn't made much progress.

Then, one day I was waiting for some paint to be tinted at my local home store, and I came across these.

They're holiday lights. Jumbo-sized holiday lights.  The bulb part is made of colored plastic, and measures about 7" high. At the bottom there is a large fake lamp socket. Inside of all of it is a genuine C7 bulb of the appropriate color.

I bought 3 sets, 15 in all.

To be different, I wanted to build these as self-contained devices, with a separate microcontroller in each of the light bases. The microcontrollers I'm using cost about $1 each, so there isn't too much cost there, but the big challenge is a power supply. Generally, I build a linear power supply, which is simple and performs well, but you need an expensive and bulky transformer.

There is a way around that, with the reasonably named "transformerless power supply". Realistically, a better name would be the "high-voltage shock-o-matic", because it involves hooking things directly to the AC line, can only supply a small amount of current, is inefficient, and is hard to troubleshoot. Oh, and if one component fails you get 150 volts instead of the 5 volts you were expecting.

I decided to build one of these, so I ordered up some parts, wired it up, plugged it in, and immediately lost the magic smoke from one of the resistors. Turns out I miscalculated, and I needed a much-more-expensive power resister.

Thinking about it some more, I decided that since I still needed power to each bulb - and therefore a wire to each bulb - it was simpler to just build a simple system with one microcontroller.

Posted by ericgu | 1 Comments
Filed under:

Brilliant...

I've been doing some electronics recently, and perhaps I'm therefore more likely to treat this kindly, but I have to say that I think it's brilliant.
Posted by ericgu | 2 Comments

What's going on now...

If you want to keep track of what's going on right now, you need this site...
Posted by ericgu | 1 Comments

Scary...

Last Saturday, we were invited to a Halloween party at a friend of a friend. I only decided to go Friday night, so I'd put essentially zero effort into thinking about a costume.

The wife was going as a vampire (we had a long discussion on what the feminine form of "vampire" was. I tended towards "vampress", mostly because of how silly it sounded), and I thought of doing something that fit together with that thematically. A lame costume that fits together thematically with another one is much better than a lame one that sits by itself.

After a while, something suggested itself, and things came together pretty well. You can see the results here:

(Like pilot in the 1950s who had eyepatches to preserve eyesight in their dominant eye in case of a nuclear explosion, I suggest covering one eye before clicking on the following link).

Pictures

I'm hoping that it's obvious who I am.

The party itself was pretty good. The hosts hired a magician who walked around and did close magic to entertain the crowd. He was talented, though frankly given the sobriety of the majority of the guests it could have been me doing the tricks.

While we were there a few of the ladies took it on themselves to vandalize me.

Posted by ericgu | 3 Comments

wireless LCD photo frame...

I want to buy a couple of wireless LCD photo frames for my relatives. It needs to have a decent display, speak wireless, and hook up to a smugmug gallery.

Any recommendations?

Posted by ericgu | 5 Comments

Versioning in HealthVault

Download the sample code from MSDN Code Gallery.

“Versioning” is an interesting feature of the HealthVault platform that allows us to evolve our data types (aka “thing types”) without forcing applications to be rewritten. Consider the following scenario:

An application is written using a specific version of the Encounter type. To retrieve instances of this type, you write the following:

HealthRecordSearcher searcher = PersonInfo.SelectedRecord.CreateSearcher();

HealthRecordFilter filter = new HealthRecordFilter(Encounter.TypeId);
searcher.Filters.Add(filter);

HealthRecordItemCollection items = searcher.GetMatchingItems()[0];

foreach (Encounter encounter in items)
{

}

That returns all instances of the type with the Encounter.TypeId type id. When the XML data comes back into the .NET SDK, it creates instances of the Encounter wrapper type from them, and that’s what’s in the items array.

Time passes, cheese and wine age, clothes go in and out of style. The HealthVault data type team decides to revise the Encounter type, and the change is a breaking one in which the new schema is incompatible with the existing schema. We want to deploy that new version out so that people can use it, but because it’s a breaking change, it will (surprise surprise) break existing applications if we release it.

Looking at our options, we come up with 3:

  1. Replace the existing type, break applications, and force everybody to update their applications.
  2. Leave the existing type in the system and release a new type (which I’ll call EncounterV2). New applications must deal with two types, and existing applications don’t see the instances of the new type.
  3. Update all existing instances in the database to the new type.

#3 looks like a nice option, were it not for the fact that some instances are digitally signed and we have no way to re-sign the updated items.

#1 is an obvious non-starter.

Which leaves us with #2. We ask ouselves, “selves, is there a way to make the existence of two versions of a type easier for applications to deal with?”

And the answer to that question is “yes, and let’s call it versioning”…

Versioning

Very simply, the platform knows that the old and new versions of a type are related to each other, and how to do the best possible conversion between the versions (more on “best possible” in a minute…). It uses this information to let applications pretend that there aren’t multiple versions of a type.

Down Versioning

The first scenario is existing applications that were written using the original version of the Encounter type (which we’ll call EncounterV1 for clarity), and what happens when the come across a health record that also has EncounterV2 instances in it. Here’s a graphical indication of what is going on:

 

image

This application is doing queries with the EncounterV1 type id. When a query is executed, the platform knows that the encounter type has multiple versions, and converts the query for the V1 type to a query for all encounter types (both EncounterV1 and EncounterV2).

The platform finds all the instances of those two types, and looks at each one. If it’s an EncounterV1 instance, it just ships it off to the application.

But, if it’s an EncounterV2 instance, the platform knows (by looking at the application configuration) that this application doesn’t know what to do with an EncounterV2. It therefore takes the EncounterV2 instance, runs a transform on the EncounterV2 xml to convert it into EncounterV1 xml, and ships that across to the application. The data is “down versioned” to the earlier version.

The application is therefore insulated from the updated version – it sees the instances using the type that the application was coded against.

Down version conversions are typically lossy – there are often fields in the V2 version that are missing in the V1 version.  The platform therefore prevents updating instances that are down versioned, and will throw an exception if you try. You can look at the IsDownVersioned property on an instance to check whether updating is possible to avoid the exception.

Up Versioning

The second scenario is an application written to use the new EncounterV2 type:

image

This time, the V1 data is transformed to the V2 version. An application can check the aptly-named IsUpVersioned property to tell whether an instance is up versioned.

Higher versions typically contain a superset of the data in the old version, and the platform therefore allows the instance to be “upgraded” (ie updated to the new version).

However, doing so will prevent an application using the V1 version from being able to update that instance, which may break some scenarios. The application should therefore ask the user for confirmation before upgrading any instances.

This would be a good time to download the sample applications and run the EncounterV1 and EncounterV2 projects. Because this behavior is controlled by the application configuration, each application has its own certificate, which will need to be imported before the application can be run.

Add some instances from both V1 and V2, and see how they are displayed in each application. Note that EncounterV1 displays both the instances it created and the down-versioned instances of the EncounterV2 type, and that EncounterV2 displays the instances it created and up-versioned instances of the EncounterV1 type.

Version-aware applications….

In the previous scenarios, the application was configured to only use a single version of a type.

In some cases, an application may want to deal with both versions of a data type simultaneously, and this is known as a “version-aware” application.

We expect such applications to be relatively uncommon, but there are some cases where versioning doesn’t do everything you want.

One such case is our upcoming redesign to the aerobic session type. The AerobicSession type contains both summary information and sample information (such as the current heart rate collected every 5 seconds). In the redesign, this information will be split between the new Exercise and ExerciseSamples type. AerobicSession and Exercise will be versioned, but there will be no way to see the samples on AerobicSession through Exercise, nor will you be able to see ExerciseSamples through AerobicSession (there are a number of technical reasons why this is very difficult – let me know if you want more details). Therefore, applications that care about sample data will need to be version-aware.

Writing a version-aware application adds one level of complexity to querying for data. Revisiting our original code, this time for an application that is configured to access both EncounterV1 and EncounterV2:

HealthRecordSearcher searcher = PersonInfo.SelectedRecord.CreateSearcher();

HealthRecordFilter filter = new HealthRecordFilter(Encounter.TypeId);
searcher.Filters.Add(filter);

HealthRecordItemCollection items = searcher.GetMatchingItems()[0];

foreach (Encounter encounter in items)
{

}

When we execute this, items contains a series of Encounter items. The version of those items is constrained to the versions that the application is configured (in the Application Configuration Center) to access. If the version in the health record is a version that the application is configured to access, no conversion is performed, *even if* the version is not the version specified by the filter.

That means that the items collection may contain instances of different versions of the type – in this case, either Encounter or EncounterOld instances. Any code that assume that instances are only of the Encounter type won’t work correctly in this situation. You may have run into this if you are using the HelloWorld, as it has access to all versions of all types.

Instead, the code needs to look at the instances and determine their types:

foreach (HealthRecordItem item in items)

     Encounter encounter = item as Encounter; 
     if (encounter != null) 
     {
     } 

     EncounterOld encounterOld = item as EncounterOld;
     if (encounterOld != null)
     { 
     }
}

This is a good reason to create your own application configuration rather than just writing code against HelloWorld.

Controlling versioning in version-aware apps

It is possible for version-aware apps to request that versioning conversions be performed, by specifying the desired version(s) as part of the filter. If the following is added to the filter definition:

filter.View.TypeVersionFormat.Add(EncounterOld.TypeId);

the platform convert any instances that are not of the specified type id (or type ids) to that type id.

EncounterBoth is a version-aware application in the sample code that handles the Encounter and EncounterOld types. It will also let you set the TypeVersionFormat.

Asking the platform about versioned types…

If you ask nicely, the platform will be happy to tell you about the relationship between types. If you get fetch the HealthRecordItemTypeDefinition for a type, you can check the Versions property to see if there are other versions of the type.

The VersionedTypeList project in the sample code shows how to do this.

Naming Patterns

There are a couple of different naming patterns in use. The types that were modified before we had versioning use the “Old” suffix (MedicationOld and EncounterOld) to denote the original types. For future types, we will be adopting version suffix on the previous type (so, if we versioned HeartRate, the new type would be named HeartRate, and the old one HeartRateV1). We are also planning to rename MedicationOld and EncounterOld to follow this convention.

We will also be modifying the tools (application configuration center and the type schema browser) to show the version information. Until we get around to that, the best way is to ask the platform as outlined in the previous section.

Posted by ericgu | 2 Comments

100 skills every one should know...

Popular Mechanics has a list of "100 skills every one should know". How many have you have?

 (I'll give my count later...)

Posted by ericgu | 8 Comments

Triathlon report

For your "enjoyment", a report on the Triathlon I did last Sunday...
Posted by ericgu | 2 Comments
Filed under: ,

TV Calibration

A few years, ago, I bought one of the last high-end rear-projection TVs based on CRTs - a Pioneer Elite 620HD. I did some basic calibration with the Avia disc and did some other minor adjustments, but never got around to getting a real calibration done.

Calibration is the process of getting the TV to be as close as possible to NTSC settings - the same settings that were used when the program was created. That means getting colors and gray levels as close as possible to what they should be.

But, if it's a high-end TV, why isn't it set from the factory to meet NTSC settings? Well, the simple fact is that TV manufacturers play games to make their TVs stand out in showroom settings. That generally means pictures that are far brighter than they should be (with corresponding poor black levels), colors are off, and the picture is over-sharpened.

Some newer TVs let you choose a setting that's close to NTSC, but in most cases, calibration can make a big difference. If you have a LDC or Plasma set, start with a disc like Avia and see what you get out of it (Avia also has calibration for surround sound, which may also be useful).

In my case, my set needed cleaning, focus adjustments (because it's a rear-projection set), convergence (because it has 3 CRTs in it, one for red, blue, and green), and geometry (because it uses CRTs). You can find local techs in my area who can do calibration, but because my set is fairly rare these days, I wanted an expert, and hooked with David Abrams from Avical, working out of LA, when he was on a trip in the Seattle area.

David is a really nice guy and did a wonderful job. He spent 90 minutes on the geometry of the set (making sure straight lines are straight in all 3 colors), and about 60 minutes on the convergence (aligning all three colors). I only have about 5% of the patience he has when he's doing those kinds of things.

So, after cleaning the set, setting the focus, setting the geometry and convergence, he was on to setting the gray levels and colors. With his test pattern generator (running through the TV's component inputs, which is all I use...) and his $18K color analyzer, that part went pretty quickly. He then worked through all my sources (Tivo HD, DVD, XBox 360) and verified that everything was set right. Finally, we looked at some source and he did some final tuning.

The results were pretty impressive.

The downside is that the differences between HD feeds is now really obvious - some look great, but others really fall short.

Recommended.

Posted by ericgu | 4 Comments

A box of toys...

I am a box of toys and notions. Among other things, I contain a hard box full of legos and a gross of superballs. On my outside I have a series of labels that tell me where I have lived.

As a proud corrugated-American, I'd like to share my story.

113/1136

My location for the last 24 hours. My owner has moved to this office to be closer to the rest of the HealthVault partner team, which he has joined due to a recent organizational optimization. I like this location because lots of people stop by, but I'm worried it will be loud because of the standby diesel generator right outside.

113/2172

I moved back to main campus to this office. It faces east and has a decent view of a parking lot. My owner works on the HealthVault partner team.

Northup/2036

I love this office, which is big and has a nice view to the south. I do worry about degradation of my structure because the sunlight makes it hot and sometimes the A/C fails. My owner works on the HealthVault partner team, and enjoys the "small team" atmosphere.

119/2262

This office is on west campus. It's okay, but the building isn't great. My owner works on the Windows Live pictures and video team.

50/3602

I moved from one end of the hall to the other, and my corners are getting banged up. My owner is on a newly-reorganized team and isn't sure what he works on right now.

50/3430 

I moved two offices down to this one - I can look out through the window and watch my owner when he sits outside at lunch, though he does complain about the cafeteria now and then. My owner is on the Windows Movie Maker team working on the DVD Maker user interface.

50/3501

Welcome to the Movie Maker team, and to a new area of campus. Building 50 stands alone by itself and is a bit inconvenient to get to, but it seems nice enough, and there are lots of boxes next door that I can spend time with.

41/1722

A new office in the same building, this time looking at a wall of plants. My owner is a PM on the C# team, and is doing language design.

41/2788

Despair. My owner is happy with his assignment as the test lead on the C# compiler, but this office faces south, gets very hot, and has a lovely view of the top of a cafeteria.

42/1816

A pretty good office in a really nice building. I face north towards a set of vegetation. My owner co-owned office assignments on this move and got to choose his one before other people, so he got a nice one. He's a test lead for the C++ compiler front end, though there are rumors that there's something new coming along.

4/2238

My first window office, with a beautiful view of a forest. My owner is very happy to get such a nice window office, but he finds it hard not to get lost in the 1-4 building complex. He's a test lead for the C++ compiler.

25/????

I was never in building 25, but my owner often tells me stories about it late at night. He says he had two different offices there, both of them pretty good.

Posted by ericgu | 3 Comments

Fun with HealthVault transforms

It is sometimes useful to be able to display data in an application without having to understand the details of that data.

To make this easier, HealthVault provides a couple of features: transforms, and the HealthRecordItemDataGrid class.

Discovering transforms

To find out what transforms are defined for a specific type, we just ask the platform:

    HealthRecordItemTypeDefinition definition =
             ItemTypeManager.GetHealthRecordItemTypeDefinition(Height.TypeId, ApplicationConnection);

In this case, we get back the definition of the Height thing type. In the returned HealthRecordItemTypeDefinition (which I’ll just call “type definition” for simplicity), you can find the schema for the type (in the XmlSchemaDefinition property), and two properties related to transforms.

SupportedTransformNames lists the names of the transforms. In this case, the list is “form”, “mtt”, and “stt” (more on what these do later).

TransformSource contains the XSL transforms themselves, keyed with the name of the transform.

Applying transforms

If you want to apply the transform, there are two ways to do it.

The first way is to do it on the client side:

    string transformedXml = definition.TransformItem(“mtt”, heightInstance);

That’s pretty straightforward, though you do need to fetch the type definition for the type first. You could also apply the transform using the .NET XML classes, if you wanted to do more work.

The second option is to ask the platform to do the transform for you. You do this by specifying the transform name as part of the filter definition:

    HealthRecordFilter filter = new HealthRecordFilter(Height.TypeId);
    filter.View = new HealthRecordView();
    filter.View.TransformsToApply.Add("mtt");
    filter.View.Sections = HealthRecordItemSections.Core;

and then the item will already have the transformed text inside of it:

    XmlDocument mttDocument = heightInstance.TransformedXmlData["mtt"];

Available transforms

Each type defines a set of transforms that let you look at a data instance in a more general way. The are named “mtt”, “stt”, and “form”.

“mtt” transform

The mtt (which stands for “multi type transform”) generates the most condensed view of data – it returns values for properties that are present on all data types, and a single summary string.

For example, asking for the mtt transform of a Height instance returns the following:

<row wc-id="8608696a-94b3-41a8-b9a6-219ecbbc87d1" 
     wc-version="1e715849-43d7-4a72-9c65-8163649c0f84" 
     wc-note="" 
     wc-tags="" 
     wc-date="2008-01-22 11:12:42" 
     wc-type="Height Measurement" 
     wc-typeid="40750a6a-89b2-455c-bd8d-b420a4cb500b" 
     wc-source="" 
     wc-brands="" 
     wc-issigned="False" 
     wc-flags="" 
     wc-ispersonal="false" 
     wc-relatedthings="" 
     summary="1.9405521428867 m" />

The “wc-“ attributes are the common data items across all instances. The most interesting piece of data is the summary attribute, which gives you the (surprise!) summary string for the instance.

“stt” transform

The stt (“single type transform”) is similar to the mtt, but instead of a single summary attribute there are a series of attributes that correspond to the properties on the data type. It will generally contain a attribute for every important property, but if the property is a less important detail and/or the type is very complex, this may not be true.

For our Height instance, we get this for the mtt transform

<row
  wc-id="8608696a-94b3-41a8-b9a6-219ecbbc87d1"
  wc-version="1e715849-43d7-4a72-9c65-8163649c0f84"
  wc-note=""
  wc-tags=""
  wc-date="2008-01-22 11:12:42"
  wc-type="Height Measurement"
  wc-typeid="40750a6a-89b2-455c-bd8d-b420a4cb500b"
  wc-source=""
  wc-brands=""
  wc-issigned="False"
  wc-flags=""
  wc-ispersonal="false"
  wc-relatedthings=""
  when="2008-01-22 11:12:42"
  display="1.9405521428867 m"
  height-in-m="1.9405521428867" />

How do we know what attributes are here and what to do with them?

That information is stored in the ColumnDefinitions property of the type definition. Each of these (an ItemTypeDataColumn instance) corresponds to one of the attributes on the row created by the STT transform.

The following code can be used to pull out the values:

    XmlNode rowNode = item.TransformedXmlData["stt"].SelectSingleNode("data-xml/row");

    foreach (ItemTypeDataColumn columnDefinition in definition.ColumnDefinitions)
    {
        XmlAttribute columnValue = rowNode.Attributes[columnDefinition.ColumnName];
    }

This is the mechanism that the HealthVault shell uses to display detailed information about an instance. There is additional information in the ItemTypeDataColumn that it uses:

The Caption property stores a textual name for the column.

The ColumnTypeName property stores the type of the column.

The ColumnWidth contains a suggested width to use to display this information.

The VisibleByDefault property defines whether the column is visible in the shell view by default (the wc-<x> ones typically are not, with the exception of wc-date).

HealthRecordItemDataGrid

If you don’t want to decode all the column information yourself, you can use the HealthRecordItemDataGrid in your project.

Put the following after the page directive in your .aspx file:

    <%@ Register TagPrefix="HV" Namespace="Microsoft.Health.Web"  Assembly="Microsoft.Health.Web" %>

and then put an instance of the grid in the appropriate place:

    <HV:HealthRecordItemDataGrid ID="c_itemDataGrid" runat="server" />

You then create a filter that defines the data to show in the grid in the page load handler:

    c_itemDataGrid.FilterOverride = new HealthRecordFilter();
    c_itemDataGrid.FilterOverride.TypeIds.Add(AerobicSession.TypeId);

and the grid will be rendered using the STT transform view. If you want it to use the MTT transform view, you can set the TableView property on the grid to MultipleTypeTable, and it will show a summary view. You will also see this view if the filter returns more than one thing type.

The form transform

The final transform is the form transform. This transform exists on most, though not all types (we’re working to add form transforms where they’re absent). It provides an HTML view of the type.

For our Height instance, we get the following from the form transform:

<div class="xslThingTitle" id="genThingTitle">Height</div>
<div class="xslThingValue">1.9405521428867 m</div>
<table class="xslThingTable">
  <tr>
    <td class="xslTitleColumn">Date</td>
    <td class="xslValueColumn">2008-01-22 11:12:42</td>
  </tr>
</table>

which, when rendered, looks something like this:

Height
1.9405521428867 m
Date 2008-01-22 11:12:42

Other transforms

Some thing types will return a list that contain other transforms with names like “wpd-F5E5C661-26F5-46C7-9C6C-7C4E99797E53” or “hvcc-display”. These transforms are used by HealthVault Connection Center, for things like transforming WPD data into the proper xml format for a HealthVault instance.

Posted by ericgu | 2 Comments

Fantastic contraption

I apologize ahead of time

Fantastic Contraption...

Posted by ericgu | 3 Comments
Filed under:

In praise of boredom...

Raymond wrote an interesting post about the erosion of the car trip experience.

Along with the desired to shield our kids from any discomfort, I think there's a big desire to shield them from boredom.

Boredom is part of being an adult, and I think learning to deal with it is an important part of growing up.

Back when I was a kid, every year or so we took a long trip from Seattle to Boise to visit my grandparents. Though we usually made the trip over in the night (to avoid the heat (no AC, of course)), there were lots of hours of watching the "scenery" roll by (as an adult, I find the area around along the Columbia river to be striking, but as it kid it's a whole lot a nothin'), and then similar hours while we were there.

That meant we have to learn how to amuse ourselves and not annoy each other too much.

But if you have video every time your in the car, you don't learn how to deal with being bored, and (as a parent) you miss some great opportunities for conversation, not to mention the chance to inflict your musical tastes on your offspring.

 

 

Posted by ericgu | 5 Comments

Dr. Horrible...

Last night I fixed the quicktime player on my somewhat aging home machine by turning of DirectX acceleration, and the family sat down to watch Dr. Horrible's Sing-Along Blog, which I had purchased through iTunes for the princely sum of $3.99.

Dr. Horrible, if you aren't "in the know", was created by Joss Whedon, of Firefly fame. Firefly was a sci-fi western, and Dr. Horrible is a internet comic book superhero musical. It stars Felicia Day as the love interest, Nathan Fillion (aka Mal) as Captain Hammer, and Neil Patrick Harris (who will always be "Doogie" to me...) as the title character.

Both the writing and the music are top-notch, as are the performances by the main characters. I'm hoping there will be more than the initial 3 episodes.

Recommended.

Posted by ericgu | 0 Comments
More Posts Next page »
 
Page view tracker