[All the other Parts: History of Visual Studio]
[Visit the Microsoft Visual Studio 2010 and .NET Framework 4 Beta web site for the latest info]
Visual Studio 2008 Winds Down, Visual Studio 2010 Begins
Just as things were starting to wind down on VS2008, I got a new assignment. I was going to be Chief Architect of Visual Studio. Wow. So here this series takes a bit of a turn because “my” history is quite a bit different at this point – I actually stopped working on the current product at all, and much as I would have liked to write about what I was doing back then, you can imagine there wasn’t a whole lot of enthusiasm about starting blog discussions on what we should do in “Dev10” (the term “Hawaii” never stuck and was soon abandoned) while we were trying to launch VS2008.
From a technical perspective, the idea of VS2010 was to begin a remodeling process that would span several versions. Making key investments that would help long-standing problems and at the same time offer some immediately compelling features as well. I gave a lot of thought to what enabling steps we would have to take to make our experience be something great in say three versions. It was hard to see exactly what that future might be, so I tried not to think about specific features at all. I had seen all sorts of “flashware” concepts for things we might do and I tried to think about what key problems we would have to solve to implement things of that nature. Using all that, and what I already knew, I came up with a list of fairly low-level considerations for a “VS of the Future” that I thought we should work on, and why.
I think the actual document I created is pretty dry, it’s full of observations about the size of processor caches, the number of cores, the memory wall, the locality of our data structures, blah, blah, blah. It goes on like this for pages, but I think I can summarize it into a couple of major themes.
The first major theme is concurrency, and the need to increase our ability to use it successfully. You might think that with all of those threads Visual Studio spawns that there is a great deal of parallelism but, for the most part, it’s only superficial (there are exceptions), what happens more often than not is that those threads run something like relay race with only one thread having the baton at any given moment. One key reason for this is that due to the single-threaded history of our code-base, and indeed many Windows applications, a lot of the objects are tied to the STA that is the main thread.
To create opportunities for concurrency you have to start to disentangle the important data structures from the STA and give them better access models that are friendly to concurrency. A great example of this is the new editor in VS2010. It allows you to access editor buffers via immutable micro-snapshots. Previously, if you wanted to do a something like say background analysis on a live text buffer, you had to do crazy things like copying the entire editor buffer on every keystroke. Concepts familiar to database programmers, like isolation, need to be present in the key structures of any concurrent sytem.
The STA concerns are a bit like a plague, they can cause a massive “entanglement” of data processing and presentation. Transitions into or between STAs can result in unexpected message pumping, leading to bizarre re-entrancies which in turn result in astonishing bugs that are very hard to find and fix.
Additional entanglements occur between processes trying to create parent/child relationships between their windows where the single pump effectively fuses together certain STAs – a necessary step given typical application assumptions but a total bug nightmare and a parallelism disabler.
Ultimately the most important thing is to make it as easy as possible to move work off of the UI thread. I think it’s vitally important that there is a single thread responsible for the UI – it’s the sanest way to preserve the order of operations the user initiated – the causality from their perspective if you will – but as their requests become non-trivial you need the flexibility to do them asynchronously.
There are two major kinds of background activity that come up in an IDE; it is easiest to illustrate these two needs by example:
- Search for “foo”
- This can be done in a totally asynchronous fashion with different “threads” proceeding in parallel against different editor buffers or other searchable contexts. All the foos light up as they are discovered for instance.
- Foos that are not visible need not be processed, so this sort of parallelism has the option to be “lazy”
- Rename “foo” to “bar”
- We can still do this asynchronously but now there is an expectation that we will eventually do it all and of course we must do all the foos not just the visible ones
- Generally this requires progress, and a transaction
- It can fail, or be cancelled, these are normal things
Both of these represent two cases where we can use a “coarse” parallelism model (e.g. one file at a time, independently). Of course we also would like to do fine-grained parallelism in places and in fact searching could be amenable to this as well (e.g. search odd lines with one thread even lines with another). Fine-grained parallelism has the opportunity to keep data locality good because you can access the data in one stream rather than access disparate data streams all at once, at the expense of more complex scheduling. Locality is good for performance, more threads are good, sounds good but you need some fundamentals to make any of this happen:
- Limited memory usage in the face of many threads running
- Ability to safely access the data in slices that are durable and limited in span
- Isolation from changes that happen during the operations
- No STA requirements in the data access and a asynchronous way to deliver updates to the UI
New UI Model
Again, I went on at great length about this in my original memo but I’ll treat this topic even more briefly than concurrency. The long and the short of it is that your average developer’s machine has the equivalent of a few 1990s supercomputers for a GPU (we could haggle over how many) and it’s important to be able to get good mileage out of that unit. It’s hard to imagine achieving those kinds of successes at reasonable cost using the good old WM_PAINT + GDI model – a high quality retained mode graphics system is basically a must.
In addition to a making it easier to use the GPU, there is also a tremendous desire to do good separation between the UI and the data elements of your design. Not just because this is architecturally sensible and clean but, as I discussed in the previous section, because to do otherwise significantly hinders your ability to achieve a good degree of parallelism. The retained mode graphics model actively encourages badly needed separation and you all know I love the Pit of Success.
Ultimately that leads to the choice of WPF for presentation in Visual Studio because the long term choices are to either use that or roll our own. However WPF and Silverlight evolve in future frameworks, and you’d have to be crazy to think they won’t, we’re better off using WPF notions than relying on the past, or worse, on something of our own making.
What came of all this?
I’ve deliberately avoided going into feature discussions so far, just as I did in my original memo, but ultimately we had to choose particular product areas where we would take these notions and invest in moving them into a good direction. That’s the point of the “remodel” after all.
These are some of the major initiatives that got an investment; they had impact throughout the entire product.
A new framework, and in particular a new CLR, essentially mandated that we do this work. We had been able to do this more cheaply in previous releases because all the target frameworks shared a CLR (the CLR had been on v2.0 even though the framework moved on to 3.0 and 3.5). This base level of compatibility allowed a simpler “onion-skin” kind of model to deliver an effective solution in Orcas – that wasn’t going to work in Dev10. In Dev10 we had to be able to target CLR 2.0 and CLR 4.0 and we had to do it in such a way that any given solution might use the related frameworks in any combination. We certainly couldn’t assume that the mscorlib that the IDE had loaded was the one that the user wanted to target.
Then there’s the matter of tools. There are different compilers for different frameworks and we can’t assume that, for instance, the C# compiler for version 4.0 is the right tool for the job when building a project designed to run on version 3.5. And even if we were super careful with the compilers, what about all the other tools that create outputs, like say resources? Those assets might be in a format that is serialized with the expectation that a particular framework will load it. So no one tool could do the job.
And let’s not forget the native tools, they have all these problems too, though I’ve been using managed examples, you can search and replace the runtime names in my discussion with native tools and libraries and you get basically all the same phenomena.
So in VS2010, based on the project target, we have to select the right libraries and tools to create all your outputs and we have to be able to vary these on a project by project basis. That sounds good.
That’s just the start.
The build chain is in some ways the least of our problems, what about all those designers? What about Intellisense? As you change focus from from file to file, depending on the framework you’re targeting in that file we need to show you Intellisense for just the methods you can actually use in that context. Likewise designers should include only choices that are reasonable for the current target, so for instance, the items in the Winforms designer toolbox should not include controls that are available in the version of the framework the IDE happens to be using, but rather the ones that are available on the desired target, and of course those are not likely to be the same.
Add to this mix the desire to target platform subsets like the “client profile” of framework 4.0 and you’ve got quite challenge on your hands.
I remember a very compelling breakdown that the teams used to explain the levels of success to management (and me)
Good: Designers and Intellisense prevent you from making mistakes; you see only things that you can really use for your target.
Not Good: Designers didn’t warn you, but at least you got a compile time error that was helpful and can be corrected.
Bad: There was no compile time error but when you ran the code you could readily see that it had some target specific aspect that shouldn’t be there and you could correct the code to avoid the problem.
Ugly: As above, but there is no reasonable diagnostic information and/or no reasonable way to fix it.
Obviously we want all aspects to be “Good” and if that can’t be achieved then as few as possible cases in each successively lower category. I think you’ll find that as of Beta 2 we’ve been very successful.
Without going into a full discussion of the problems with the “old” editor it is worth mentioning these basic facts:
- The old system was blocking innovation because of limitations in how it does the drawing – adornments are painful to add
- It was “tricky” to use in non-trivial cases, often requiring extreme interventions such as wholesale copying of buffers to achieve stable state, the locking mechanism is prone to mistakes
- When combined with its undo-manager it sometimes (always?) highly wasteful in terms of memory
- It has assorted algorithms that do not scale well, generally those to do with region tracking – increasingly critical to editor consumers
Many customer performance complaints boil down to lack of scalability in the current editor structure. These issues in turn boil down to non-linear memory usage, and non-linear algorithms, where sub-linear choices are possible, and necessary, to create the proper experience. We get real scalability complaints from customers on an ongoing basis and these problems can’t be solved by “tuning.”
To address these problems we had already built a new editor platform, including visualization and text management services. The design had proven scalability and extensibility and had already shipped (in Blend). It was a very real technology. However, there was a great deal of code in Visual Studio that depended on old editor behaviors.
To simplify adoption of the new editor we created “shim” services that supported the most popular of the old interfaces allowing existing clients to continue to use those interfaces. Since most editor consumers actually use only a subset of the whole API we were able to substantially mitigate the cost of conversion across Visual Studio – shims weren’t a universal solution, but they helped.
Now I say that like it was some easy thing but wow it’s a ton of work even at the “reduced” cost. And the number of shims seemed to keep growing, and growing, and… it took a while to get this under control.
There were other problems. The new editor wasn’t identical to the old, despite being more functional overall it was missing some things – like column select – that would need to be added (it’s in Beta 2). Then there are the complicated editor interactions in cases like those presented by the Web Forms designer – the presence of markup intermixed with a language (like VB or C#) makes Intellisense very tricky. The language service needs to see the file without the markup to provide coloring and Intellisense but of course the user edits the composite file. Getting this to work on the new editor was quite a trick.
Having gone through all this I think I can say that the new editor has a very bright future; it has unprecedented extensibility – everything from text replacement, to object insertion, to HUD–like overlays. In this version we’ve barely begun to tap its capabilities.
It would be a mistake to think of the new shell as just a redo of the old in WPF, it’s quite a lot more than that. There is a whole new toolbar system, a whole new menu system, both based on WPF. The docking controls, easily taken for granted, those are all new. There are the bits that allow document windows to completely float for better multi-monitor support. There are all the decorative bits, the window tabs, the output window tabs. It’s starting to add up but these things are only one aspect of the Shell, its UI.
Behind the scenes, there to enable pieces like the new editor that I discussed above, and the project system that will be next, are the Shell Services. You might see screenshots of the new Extension Manager and the Visual Studio Gallery, they are in some ways the face of much of the underlying work: a new deployment model for extensions centered around the VSIX packaging format and the MEF extension model.
But those dialogs really are just the face, to make the systems work there are many invisible things, like the MEF catalog and services that manage extension state. In fact, if you were to visit the Shell Services link I provided above, you could go down that entire list and you would find very few of the major systems that were not affected in this rewrite. From the Start Page, to Intellisense, most services have had significant work. MEF itself lays out a roadmap for future levels of extensibility in the product, simultaneously more comprehensive, easier to use, and in more areas.
That last notion is perhaps the most important one, I’ve said this many times in various speeches but it bears repeating: It’s hard to look at Visual Studio, a product that is substantially ALL extensions, and say “it needs more extensibility” – but it does. The original Visual Studio shell provided excellent extensibility of the core services – VS itself is the proof of that – but what is lacking is general extensibility of the extensions themselves. These extra levels are what we sought to add in Editor and the Common Project System, and hopefully they will pervade the product over time.
“Common” Project System
I’ve alluded to this system several times so far in the document; it’s also an important step on a long-term plan.
Anyone who has created a project type for Visual Studio knows that it can be a serious pain to try to keep up with the Joneses. The flagship languages add new features (ClickOnce is the canonical example) and since there is no central place to put project features, they end up having to be re-implemented in the code for many different project types. The ability to create project “flavors” (described a little bit here) helps alleviate this somewhat but certainly does not provide complete relief.
So, at the start of the cycle we find ourselves in a situation that is something like this:
- We’d like to create project system that would provided the needed extensibility so that we could unify many project types
- We think it would be nuts to create this thing and then try to change all the projects to that plan in one release
- We have the C++ project system which needs to move to MSBuild anyway to be more like the others and gain MSBuild's incremental build and scale benefits
So the solution seems obvious – we’ll start building “CPS” and we’ll do C++ first, that way we have a test case, we don’t have to change the world all at once, and the C++ team will no longer have to be in the business of managing all its own project stuff. The reason that I quoted “Common” is that, at least for now, it’s only the C++ project system – though it has ambitions to be a lot more.
How hard can that be? It’s only an all new project system with virtually all of its attributes encoded cascaded XML files, general purpose editing UI, with pervasive MEF extension points, and enough scale to handle the largest C++ solutions we have. Why it’s triviality itself J
When I wrote about “Dolphin” I gave a litany of changes, it was an impressive list, but I think I can say with candor that I gave a fairly complete list of the major new pieces of work in that release. I was able to do that because the team was a lot smaller and the whole release could fit inside one person’s head. I’m not even going to pretend that I can write about everything that’s in VS2010 – So far I’ve stayed very close to my own team and the things that were most dear to me – this is only “my” history after all, the complete history would require input from thousands. So even though I can’t possibly be complete, there are a few of my favorite new things that I want to talk about.
The New C++ Code Model
The VS2010 C++ Code Model is essentially a complete redo of the old technology. Now I’m a funny guy to be writing about this because probably more than any other one person I’m responsible for creation of the old technology. All those .bsc files, that was my baby, we came up with the current system when we were working on Minimal Rebuild (previously discussed) and several of my friends had the joy of maintaining that code in the years after I left the C++ team (thanks, you know who you are, and sorry!). The .ncb code was the “no compile browser” and the original flavor of that was created by another fellow who I supervised, no name dropping, this was another case of “give the new guy this really hard project, I’m sure he’ll do fine”. Gee I find myself apologizing a lot in this history.
Anyway suffice to say they were fine in say 1993 but I think it’s possible to do a lot better in 2009 and nobody is happier to see my old code leave the building than me. The new system offers improvements almost across the board, especially in reliability and performance.
You know I couldn’t do another entry without talking about debugger technology. Historical Debugging is a feature that’s been incubating in the minds of debugger people probably for as long as they’ve been debugging.
I was the debugger lead in the early 90s and I used to explain the utility of debuggers and debugging tools in this way: Imagine a program with a bug, it has been running along, everything is fine, everything is going wonderful, the flow of execution arrives at a point we’ll call Albuquerque, where it turns right. Now as every Bugs Bunny fan knows, the correct thing to do at Albuquerque is to turn left. The program’s decision to go right has led it down an incorrect path and sometime later we will observe a problem.
Now if we’re very lucky “sometime later” will be very soon, like for instance it might be that we just de-referenced a null pointer and we’re going to take an exception about 2 nanoseconds after the mistake. That’s an easy bug to fix. On the other hand it could be that “turning right” was more subtle – maybe we corrupted a data structure in a minor way and it might be days before we can see an observable effect – that kind of bug is a nightmare.
Finding “Albuquerque” is what I call The Fundamental Problem of Debugging. The debugger provides you with tools (e.g. breakpoints) that allow you to stop execution while things were still good and slowly approach the point where things first went wrong. The debugger likewise provides you with tools to examine the state afterwards, hoping to find evidence of what went wrong in the recent past that will help you to see the origin. The callstack window is a great example of looking at the past to try to understand what might have already gone wrong.
To find the problem, you might start after the failure and try to look back, finding a previously unobserved symptom and moving closer to the original problem or you might start before the failure and try to move forward slowly, hopefully not overshooting the problem by too much. Or you might do a combination of these things. You might add assertions or diagnostic output to help you to discover sooner that things went wrong, and give you a view of the past. It’s all about finding Albuquerque.
Historical debugging directly addresses the Fundamental Problem by giving you the ability to look at a lot more of the past. And it’s far superior to the limited ideas we had back in the 90s for accomplishing the result. I know a couple of people who have had this idea in their head for over fifteen years – it’s great to see it out there for programmers to enjoy. I think you’ll love it, it’s like a drug! J
The Help system was another case where we saw very early on that it should get a remodel. I think the energy around that effort began with a very well written memo from a friend of mine. Frankly the sad truth is that it was pretty hard to find people who had anything nice to say about the Help system in late 2008. I think many users had given up and were just using their favorite online search to find what they needed, which, as a doubly tragic statistical truth, was probably not our search.
The Help team didn’t have an architect so I loaned myself out to them and together we made what I think are some pretty cool plans that have borne fruit. The new system is much lighter weight and is standards based. Taking a cue from MS Office the content files are basically XHTML in a .ZIP file with a custom extension MSHC. This is an archivist’s dream, but it also opens up the Help authoring space tremendously – with no documentation at all you can now just look at one of the archives and pretty much figure out how to get your content anywhere in the system.
For offline viewing, the customer help viewer is gone, replaced with a mini localhost web-server that delivers the help to the browser of your choice from the archives. We tried various indexing strategies for full-text and other searches but ultimately settled on a custom indexer that I wrote in a few days (well, to be fair they did a lot of work on it after I finished my parts, but I wrote the core). The indexer is actually very similar in operation to my old bscmake program, the C++ program database generator – those techniques fit Help content well, and it produces MSHI files.
Importantly, you don’t have to create our index format to publish Help for your topics; the MSHC file has all the information in the XHTML. We ship pre-built MSHI files to save installation time because of the size of our corpus, but you don’t have to. Additionally, the MSHI files can actually be merged as much or as little as desired for even more performance. But don’t merge MSHI’s from different locales because there is no usable universal sort order that looks decent to customers.
A 3rd party help viewer need not use MSHI at all, you could easily build your own index.
The content itself is delivered in a much lighter weight format, the big tree control is gone. In fact most of the styling for the pages is itself stored in the content as a topic and it can all be updated like any other content. You can have a lot of help on your system, or just a minimal set of your favorite books, your choice.
Overall I’m very happy with how this effort went. We replaced aging indexing technology with something new and much more precise. We removed the viewer application entirely. We drastically simplified the interface between the IDE and help (basically it’s a URL at this point). And it’s faster!
The Benefits of Dogfooding
It’s fairly well known that we try to dogfood all our own products. In this particular release I think there were some nice significant benefits from this practice. I’ll highlight just a few of the ones that I think are the most important here.
.NET Framework 4.0
It’s hard to imagine not dogfooding this in the release given its importance but there were several unexpected benefits for customers. First, the general character of the release was profoundly affected by Visual Studio’s early adoption, and our need to create designer assets for many different framework versions. The effects were felt in everything from binding policy to metadata format. As an architect I was very pleased by these results because in many cases we were able to find and prevent problems on the whiteboard – where corrections are a lot a cheaper.
A secondary benefit came about because Visual Studio 2010 had to implement so many existing COM interfaces in managed code – resulting in a large number of COM Callable Wrapper (CCW) objects. Likewise much new managed code needed to call existing interfaces that were not being converted, hence significant use of Runtime Callable Wrapper (RCW) objects. That’s all well and good, these technologies had existed for years, however, fairly innocuous seeming choices, like “when should RCW’s be cleaned up” were having profound consequences. The additional CLR cleanup code present in past releases introduced unexpected reentrancy that was not present in the strictly native implementation. Importantly Visual Studio gave us fairly easy and important test cases to work with and ultimately resulted in significant improvements in COM interoperability – these kinds of experiences make the Framework better for everyone.
Windows Presentation Foundation
The WPF team had many headaches to deal with thanks to having VS for a customer but I’d like to think WPF also got more than a few benefits. Some of the corrections we asked for were fairly minor – like the toolbar panel’s layout code which was fairly weak, and we have a lot of toolbars. But in other cases there were significant weakness.
I’d say that if there was a theme to our WPF problems it was this: that the complexity of Visual Studio’s use of hybrid traditional Win32 and WPF elements was a new challenge. But ultimately this resulted in a lot of goodness. It’s possible to do good focus transitions now between WPF elements and Win32 elements, something that would make a person’s head hurt. And, perhaps even more importantly, different combinations of hosting WPF in Win32 UI and Win32 UI in WPF were resulting in extra drawing delays. These paths were exercised by the many window combinations present in Visual Studio and the abundance of big display transitions (like switching to debug mode and back). Generally speaking the hosting services got a lot of exercise and were significantly improved as a consequence.
I’ll talk a little bit more about WPF below specifically as it relates to Beta 2.
Team Foundation Server
Of all the technologies that we beat on, I think TFS probably got the beating worse than any other system. For sheer volume of users, size and number of shelf-sets, number of branches, merges, automated builds, reports, to say nothing of bugs, issue tracking, and basically every other TFS feature our entire division put a level of load on our TFS systems that makes me think that anyone with any load that is even remotely normal is going to have no trouble at all. Sometimes it was painful for us, like dogfooding is (and is supposed to be) but it’s going to be that much better for you.
From Beta 1 to Beta 2
I’ve saved the best news for the last section of the last installment. Beta 1 of VS2010 represented some really great progress towards our goals but there were a lot of issues, I’m happy to report that we’ve been hard at work on the most serious ones and I think we’ve got great results for you. I’ve listed just a few of the improvements that I think are most important below:
- Beta 1 got many complaints due to “fuzzy text”, in response to this WPF adopted Direct Write to give a much better overall experience. Those changes accrue to everyone (and yes you’ll get lovely text in VS!)
- Performance in Remote Desktop situations is much improved
- Startup time is significantly reduced in virtually all cases
- Raw typing speed is vastly improved, I know that for instance, C# raw typing speed has never been faster
- The new editor includes column select, as well as dozens of other underlying improvements that help region management, outlining and so forth
- Debugger performance, with and without historical debugging, is much improved, stepping is quite snappy
- The Visual Basic project system can now emit an executable while it is compiling a different one, those should yield the best build times ever for VB solutions
- Assorted shell changes in important panels, in data caching, in MEF catalog construction, and in style application will help virtual all cases but notably transitions between modes
- The new offline Help system is ready to you to use
- Find in Files uses a parallel search algorithm, and last, but not least…
- The hated Add References dialog begins on a sensible tab and populates the full list of references asynchronously should you ask for them
There are so many improvements I can’t possibly list them all. I lost count of the number of performance issues we addressed, there were probably over 100 performance analysis reports counting only my efforts on the most critical scenarios and I was hardly alone in this!
Generally, looking at the full battery of our performance tests we find that VS2010 outperforms VS2008 in the majority of cases, and, if you weigh the scenarios by importance, VS2010 looks even better than that. Our remaining performance battles are around reducing overall memory consumption, and I’m sure that will continue to be a focus area between now and the final release as we fight for every byte.
Internally, folks are very surprised and pleased by how far we have come in this Beta. I hope you are equally pleased.
To everyone that got this far, thanks so much for reading My History of Visual Studio, and I look forward to your feedback on either the Beta or the series.
[See The Documentary on Channel 9!]
[All the other Parts: History of Visual Studio]
[Visit the Microsoft Visual Studio 2010 and .NET Framework 4 Beta web site for the latest info]