Category Archives: General

I’m Sorry, I Haven’t a Cluestick

Unless you’ve listened to I’m Sorry, I Haven’t a Clue, the BBC Radio 4 “antidote to panel games” which was chaired by the recently departed (and much missed) Humphrey Lyttelton, this post may not make a lot of sense to you. That’s not intended to guarantee that it’ll make any sense to you even if you have listened to it, mind you.

ISIHAC was full of very silly improvisation games. I was listening to an episode last night, and thought a developer version could be equally silly. Games played might include the following: (links show the Wikipedia description of the original)

  • One language to the API of another: Not so much a joke as a strategy now at both Microsoft and Sun, but I’m sure it could be taken to extremes. Ever fancied using the ZX Spectrum BASIC functions from C#? How about SmallTalk from Java?
  • Singleton Crescent: Players name design patterns, seemingly at random to the unenlightened observer, until one of them declares “Singleton”. Multiple variations exist, such as Gamma’s Folly: “After a diagonal move ending on a creational pattern, a behavioural pattern is not permitted.”
  • Fluent Gorge: Players start with a single method call and add additional expressions. The player who inadvertently completes the statement is the loser. This game is already played at lunchtime in many ISVs, some of whom accidentally check in the tortuous code afterwards.
  • Pick-up algorithm: A player begins copying code out of a book, which is then removed after a few lines. They then have to continue coding as accurately as possible until the book is brought back. Points are awarded if they’re within a gnat’s semi-colon of the original. Bonus points used to be awarded if the resulting code had fewer bugs in than the printed original, but this practice has recently been discontinued as being a near-trivial way of achieving a high score.
  • Call my bluff: One player is given a single-line project goal, and then several sets of requirements purporting to support that goal. The player has to guess which set of requirements is the real one for the project. The twist is that none of the sets of requirements is even slightly realistic. Again, this game is in widespread use, played by Business Analysts on unsuspecting developers.

Right, that’s more than enough silliness. Something more serious next time, I promise.

C# in Depth – it’s really real

On Saturday, when I returned from Mountain View, there were two boxes waiting for me. Guess what was inside…

So yes, it really exists, it’s slightly slimmer than expected – and even Amazon have it for sale now instead of as a “pre-release”. Apparently they’ll have stock on May 3rd. Now that it’s on “normal” sale, it’s open for reviews. So, any of you who happen to have read the eBook and wish to make your feelings clear on Amazon or Barnes and Noble, please feel very free… (Really, I’d appreciate it. But please be honest!)

On a different note, it’s just under two years since I first talked to Manning about Groovy in Action. Since then we’ve had two more sons, I’ve changed jobs twice, and written/co-written two books. (Holly’s probably written about 10… I’ve lost track of just how many she’s got on the go at any one time.) Wow. It feels more like five years. Who knows what the next two years will bring?

Programming “in” a language vs programming “into” a language

I’m currently reading Steve McConnell’s Code Complete (for the first time – yes, I know that’s somewhat worrying) and there was one section was disturbed me a little. For those of you with a copy to hand, it’s in section 4.3, discussing the difference between programming in a language and programming into a language:

Programmers who program “in” a language limit their thoughts to constructs that the language directly supports. If the language tools are primitive, the programmer’s thoughts will also be primitive.

Programmers who program “into” a language first decide what thoughts they want to express, and then they determine how to express those thoughts using the tools provided by their specific language.

Now don’t get me wrong – I can see where he’s coming from, and the example he then provides (Visual Basic – keeping the forms simple and separating them from business logic) is fine, but he only seems to give one side of the coin. Here’s a different – and equally one-sided – way of expressing the same terms:

Programmers who program “in” a language understand that language’s conventions and idioms. They write code which integrates well with other libraries, and which can be easily understood and maintained by other developers who are familiar with the language. They benefit from tools which have been specifically designed to aid coding in the supported idioms.

Programmers who program “into” a language will use the same ideas regardless of their target language. If their style does not mesh well with the language, they will find themselves fighting against it every step of the way. It will be harder to find libraries supporting their way of working, and tools may well prove annoying. Other developers who come onto the project later and who have experience in the language but not the codebase will find it hard to navigate and may well accidentally break the code when changing it.

There is a happy medium to be achieved, clearly. You certainly shouldn’t restrict your thinking to techniques which are entirely idiomatic, but if you find yourself wanting to code in a radically different style to that encouraged by the language, consider changing language if possible!

If I were attacking the same problem in C# 1 and C# 3, I could easily end up with radically different solutions. Some data extraction using LINQ in a fairly functional way in C# 3 would probably be better solved in C# 1 by losing some of the functional goodness than by trying to back-port LINQ and then use it without the benefit of lambda expressions or even anonymous methods.

Accents and Conventions

That’s just between different versions of the same language. Between different actual languages, it can get much worse. If you’ve ever seen Java code written in a C++ style or vice versa, you’ll know what I mean. I’ve previously referred to this in terms of speaking a language with an accent – you can speak C# with a Java accent just as you can speak French with an English accent. Neither is pleasant.

At the lowest level, this is likely to be about conventions – and I’m pretty sure that when Steve writes “Invent your own coding conventions, standards, class libraries, and other augmentations” he doesn’t actually mean us to do it in a gratuitous fashion. It can be worth deviating from the “platform favoured” conventions sometimes, particularly if those differences are invisible to clients, but it should always be done with careful consideration. In a Java project I worked on a few years ago, we took the .NET naming conventions for interfaces (an I prefix) and constants (CamelCasing instead of SHOUTY_CAPS). Both of these made the codebase feel slightly odd, particularly where Java constants were used near our constants – but I personally found the benefits to be worth the costs. Importantly, the whole team discussed it before making any decisions.

Design Patterns

At a slightly higher level, many design patterns are just supported much, much better by some languages than others. The iterator pattern is a classic example. Compare the support for it from Java 6 and C# 2. On the “client” side, both languages have specific syntax: the enhanced for loop in Java and the foreach loop in C#. However, there is one important difference: if the iterator returned by GetEnumerator implements IDisposable (which the generic form demands, in fact) C# will call Dispose at the end of the loop, no matter how that occurs (reaching the end of the sequence, breaking early, an exception being thrown, etc). Java has no equivalent of this. Imagine that you want to write a class to iterate over the lines in a file. In Java, there’s just no safe way of representing it: you can make your iterator implement Closeable but then callers can’t (safely) use the enhanced for loop. You can make your code close the file handle when it reaches the end, but there’s no guarantee that will happen.

Then consider the “server” side of the iterator – the code actually providing the data. Java is like C# 1 – there’s no specific support for implementing an iterator. In C# 2 and above, iterator blocks (i.e. methods with yield statements) make life much, much easier. Writing iterators by hand can be a real pain. Reading a file line by line isn’t too bad, leaving aside the resource lifetime issue – but the complexity can balloon very quickly. Off by one errors are really easy to introduce.

So, if I were tackling a project which required reading text files line by line in various places, what would I do? In Java, I would take the reasonably small hit of a while loop in each place I needed it. In C# I’d write a LineReader class (if I didn’t already have one!) and use a more readable foreach loop. The contortions involved in introducing that idea into Java just wouldn’t be worth the effort.

At a much higher level, we get into whole programming styles and paradigms. If your natural inclination is to write imperative code, you’re likely to create a mess (or get very frustrated) in a functional language. If the problem really does call for a functional language, find someone else to help you think in a more functional way. If the problem suits imperative programming just as well as it does functional programming, see if you can change the environment to something more familiar.

Conclusion

I’m not suggesting that Steve’s point isn’t valid – but he’s done his readers a disservice by only presenting one side of the matter. Fortunately, the rest of the book (so far) is excellent and humbling – to such a degree that this minor quibble stuck out like a sore thumb. In a book which had more problems, I would probably barely have even noticed this one.

There’s another possibility, of course – I could be competely wrong; maybe I’ve been approaching problems from a restrictive viewpoint all this time. How about you?

Critical dead-weight

We’re all familiar with the idea of a technology achieving critical mass: having enough users (subscribers, customers, whatever the appropriate metric might be) to keep it alive and useful. This morning I was considering the idea of critical dead-weight: having enough users etc to keep the technology alive long past its natural lifetime.

Examples of technologies we might like to kill

  • SMTP: I suspect that completely preventing spam and other abuses (while maintaining a lot of the benefits we currently enjoy) would be difficult even with a modern protocol design, but the considerations on a messaging system created today would be completely different to those used to conceive SMTP.
  • NNTP: I still prefer using a dedicated newsreader for newsgroups instead of the kind of web forum which seems fairly pervasive these days. The simple support for offline reading and deferred posting, the natural threading (including in the face of missing articles) and the nature of purpose-built applications all appeal to me. However, like SMTP there are various concerns which just weren’t considered in the original design.
  • HTML: In some ways HTML itself isn’t the biggest problem I see here (although making the markup language itself stricter to start with might have helped) – it’s the fact that browsers have always supported broken HTML. There are numbers which are often produced during discussions of browser (and particularly renderer) implementations to say just what proportion of browser code is dedicated to displaying invalid HTML in a reasonably pleasant way. I don’t recall the exact figures, and I suspect many are pulled out of thin air, but it’s a problem nonetheless. Folks who know more about the world of content itself are in a better position to comment on the core usefulness of HTML.
  • HTTP: Okay, this one is slightly tenuous. There are definitely bits of HTTP which could have been more sensibly defined (I seem to recall that the character encoding used when handling encoded bits of URL such as %2F etc is poorly specified, for example) but there are bigger issues at stake. The main thing is to consider whether the “single request, single response” model is really the most appropriate one for the modern web. It makes life more scalable in many ways, but even so it has various downsides. 
  • IPv4: This is one area where we already have a successor: IPv6. However, we’ve seen that the transition to IPv6 is happening at a snail’s pace, and there is already much criticism of this new standard, even before most of us have got there. I don’t profess to understand the details of the debate, but I can see why there is concern about the speed of change.
  • Old APIs (in Windows, Java etc): I personally feel that many vocal critics of Windows don’t take the time to appreciate how hard it is to maintain backwards compatibility to the level that Microsoft manages. This is not to say they do a perfect job, but I understand it’s a pretty nightmarish task to design a new OS when you’re so constrained by history. (I’ve read rumours that Windows 7 will tackle backward compatibility in a very different way, meaning that to run fully natively vendors will have to recompile. I guess this is similar to how Apple managed OS X, but I don’t know any details or even whether the rumours are accurate.) Similarly Java has hundreds or thousands of deprecated methods now – and .NET has plenty, too. At least there is a road towards planned obsolescence on both platforms, but it takes a long time to reach fruition. (How many of the deprecated Java APIs have actually been removed?)
  • Crufty bits of programming languages: Language designers aren’t perfect. It would be crazy to expect them to be able to look back 5, 10, 15 years later and say “Yes, I wouldn’t change anything in the original design.” I’ve written before about my own view of C# language design mistakes, and there are plenty in Java as well (more, in fact). Some of these can be deprecated by IDEs – for instance, Eclipse can warn you if you try to use a static member through a variable, as if it were an instance variable. However, it’s still not as nice as having a clean language to work with. Again, backward compatibility is a pain…

Where is the dead-weight?

There are a two slightly different kinds of dead-weight here. The first is a communications issue: if two people currently use a certain protocol to communicate (e.g. SMTP) then in most cases both parties need to change to a particular new option before all or sometimes any of its advantages can be seen.

The other issue can be broadly termed backward compatibility. I see this as slightly different to the communications issue, even though that can cover some of the same bases (where one protocol is backwardly compatible with another, to some extent). The core problem here is “We’ve got a lot of stuff for the old version” where stuff can be code, content, even hardware. The cost of losing all of that existing stuff is usually greater (at least in the short to medium term) than the benefits of whatever new model is being proposed.

What can be done?

This is where I start running out of answers fast. Obviously having a transition plan is important – IPv6 is an example where it at least appears that the designers have thought about how to interoperate with IPv4 networks. However, it’s another example of the potential cost of doing so – just how much are you willing to compromise an ideal design for the sake of a simplified transition? Another example would be generics: Java generics were designed to allow the existing collection classes to have generics retrofitted without backward compatibility issues, and without requiring a transition to new actual classes. The .NET approach was very different – ArrayList and List<T> certainly aren’t interchangable, for example – but this allows for (in my view) a more powerful design of generics in .NET.

There are some problems which are completely beyond my sight at the moment. I can’t imagine SMTP being replaced in the next 5 years, for instance – which means its use is likely to grow rather than shrink (although probably not across the board,  demographically speaking). Surely that means in 5 years time it’ll be even further away from replacement. However, I find it very hard to imagine that humankind will still be using SMTP in 200 years. It would be pretty sad for us if that were to be the case, certainly. I find myself considering the change to be inevitable and inconceivable, at the same time.

Some technologies are naturally replaceable – or can gradually begin to gather dust without that harming anyone. But should we pay more attention to the end of a technology’s life right from the beginning? How can we design away from technological lock-in? In particular, can we do so while still satisfying the business analysts who tend to like the idea of locking users in? Open formats and protocols etc are clearly part of the consideration, but I don’t think they provide the whole picture.

Transition is often painful for users, and it’s almost always painful to implement too. It’s a natural part of life, however – isn’t it time we got better at it?

Google, here I come!

This may be a somewhat unexpected announcement for many of you, but I’m delighted to announce that as of April 7th I will be an employee at Google. (If you really needed to follow that link to know who Google are, I have no idea what you’re doing reading my blog in the first place.)

This may seem an unusual move for someone who has been concentrating on C# for a while – but I view it as a once-in-a-lifetime opportunity to work with some of the smartest engineers around on hugely exciting projects used by billions of people. Strangely enough, at the moment I don’t really know how to build an application which supports billions of users. I’m looking forward to finding out.

This is likely to mean an end or at least a temporary hiatus in my professional use of C# – but that doesn’t mean my interest in it will die out. I’m still looking forward to seeing what’s in C# 4 :) I’m likely to be using Java for my day-to-day development, which is at least familiar ground, and as Josh Bloch works at Google I’ll be in good company! (Do you think he’d trade a copy of the new edition of Effective Java for a copy of C# in Depth?)

I’ll be working in the London office, but will spend the first two weeks in Mountain View. I don’t yet know what I’ll be working on, but many of the projects in London are in the mobile space, so that seems a reasonable possibility. Whatever project I end up on (and it’s likely to change reasonably frequently) it’s hard to imagine that life will be dull.

It seems fitting to thank my wife Holly at this point for supporting me in this – my daily commute will be significantly longer when I’m at Google, which means she’ll be doing even more of the childcare, not to mention coping on her own while I’m in sunny California. She’s been a complete rock and never once complained about the extra burden I’ll be putting on her.

So, I’m currently a mixture of terrified and extremely excited – and I can’t wait to fly out on Sunday…

Language design, when is a language “done”, and why does it matter?

As per previous posts, I’ve been thinking a fair amount about how much it’s reasonable to keep progressing a language. Not only have thoughts about C# 4 provoked this, but also a few other sources:

The video is very well worth watching in its entirety – even though I wouldn’t pretend to understand everything in it. (It’s worth watching rather than just listening to, by the way – Gilad’s body language is very telling.) Here are just a few of the things which particularly caught my attention:

  • Mads, 14:45 on the “Babelification” which could occur if everyone plugs in their own type system into a pluggable language. This is similar to my concern about LISP-like macros in C#, I think.
  • Gilad, 23:40: “We’re the kind of people who love to learn new things. Most people hate to learn new things.” I don’t agree with that – but I’d say that people hate to feel they’re on a treadmill where they spent all their time learning, with no chance to actually use what they’ve learned.
  • Gilad, 28:35: “People never know when to stop […] What happens is they do too much.”
  • Mads, 50:50: “The perfect language is the one that helps you do your task well, and that varies from task to task.”

So, what does this have to do with C#? Well, I was wondering how different people would respond when asked if C# was “done”. Eric’s certainly remarked that it’s nowhere near done – whereas prior to C# 3, I think I’d have called C# 2 “done”. C# 3 has opened my eyes a little about what might be possible – how radical changes can be made while still keeping a coherent language.

I’ve been worrying publicly about the burden of learning which is being placed on developers. I’m starting to change my thoughts now (yes, even since yesterday). I’ve started to wonder where the burden is coming from, and why it matters if C# changes even more radically in the future.

Who would make you learn or use C# 4?

Suppose the C# team went nuts, and decided that C# 4 would include:

  • x86 inline assembly
  • Optional reverse Polish notation, which could be mixed and matched with the existing syntax
  • Checked exceptions
  • Regular expressions as a language feature, but using a new and slightly different regex dialect
  • User-defined operators (so you could define the “treble clef” operator, should you wish to)
  • Making semi-colons optional, but whitespace significant. (Heck, we could remove braces at the same time – optionally.)
  • A scripting mode, where Console.WriteLine(“Hello”) would count as a complete program

I’m assuming that most readers wouldn’t want to use or even learn such a language. Would you do it anyway though? Bear it in mind.

Now suppose the C# team worked out ways of including significant pieces of obscure but powerful computer science into C# 4 instead. Lots to learn, but with great rewards. It’s backwardly compatible, but idiomatic C# 4 looks totally different to C# 3.

Here’s the odd thing: I’d be more comfortable with the first scenario than the second. Why? Because the first lets me get on with developing software, guilt-free. There’d be no pressure to learn a lunatic version of C# 4, whereas if it’s reasonably compelling I’ll have to find the time. It’s unlikely (in most companies anyway) that I’ll be given the time by my employers – there might be a training course if I’m lucky, but we all know that’s not really how you learn to use a language productively. You learn it by playing and experimenting in conjunction with the more theoretical training or reading. I like to learn new things, but I’m already several technologies behind.

What’s in a name?

Now consider exactly the same scenario, but where instead of “C# 4” the language is named “Gronk#”. In both cases it’s still backwardly compatible with C# 3.

Logically, the name of the language should make no difference whatsoever. But it does. As a C# developer, I feel an obligation (both personal and from my employer) to keep up with C#. If you’re a C# developer who isn’t at least looking at C# 3 at the moment, you’re likely to find yourself behind the field. Compare that with F#. I’m interested to learn F# properly, and I really will get round to it some time – but I feel no commercial pressure to do so. I’m sure that learning a functional language would benefit many developers – as much (or even more) for the gains in perspective when writing C# 3 as for the likelihood of using the functional language directly in a commercial setting. But hey, it’s not C# so there’s no assumption that it’s on my radar. Indeed, I suspect that if I polled my colleagues, many wouldn’t have even heard of F#. They’re good engineers, but they have a home life which doesn’t involve obsessing over computer languages (yeah, I find it hard to believe too), and at work we’re busy building products.

We could potentially have more “freedom” if every language release came with a completely different name. It would happen to be able to build the old code, but that could seem almost incidental. (It would also potentially give more room for breaking changes, but that’s a very different matter.) There’d be another potential outcome – branching.

Consider the changes I’ve proposed for C# 4. They are mere tweaks. They keep the language headed in the same direction, but with a few minor bumps removed. Let’s call this Jon#.

Now consider a language which (say) Erik Meijer might build as the successor to C# 3. I’m sure there are plenty of features from Haskell which C# doesn’t have yet. Let’s suppose Erik decides to bundle them all into Erik#. (For what it’s worth, I don’t for one moment believe that Erik would actually treat C# insensitively. I have a great respect for him, even if I don’t always understand everything he says.)

Jon# and Erik# can be independent. There’s no need for Erik# to contain the changes of Jon# if they don’t fit in with the bigger picture. Conservative developers can learn Jon# and make their lives a bit easier for little investment. Radical free thinkers can learn Erik# in the hope that it can give them really big rewards in the long run. Everyone’s happy. Innovation and pragmatism both win.

Well, sort of.

We’ve then got two language specs, two compilers, two IDE experiences, etc. That hurts. Branching gives some freedom at the cost of maintenance – as much here as in source control.

Where do we go from here?

This has been a meandering post, which is partly due to the circumstances in which I’ve written it, and partly due to the inconclusive nature of my thoughts on the matter. I guess some of the main points are:

  • Names matter – not just in terms of getting attention, but in the burden of expected learning as well.
  • Contrary to impressions I may have given before, I really don’t want to be a curmudgeonly stifler of language innovation. I just worry about unintended effects which are more to do with day to day human reality than technical achievement.
  • There are always options and associated costs – branching being one option which gives freedom at a high price

I really don’t have a good conclusion here – but I hope plenty of people will spare me their thoughts on this slightly non-technical matter as readily as they have about specific C# 4 features.

Macros, and languages within languages

Ian Griffiths mailed me about macros, and explained how LISP macros were very different to C/C++ macros, working at a language level instead of at a text level. I won’t pretend to understand all about what would be possible and what wouldn’t, but Ian gave a good example: query expressions in C# 3. Instead of being part of the language itself, they could apparently have been written as macros, if C# supported them. Then if you wanted to have similar support for different forms of expression, you could just write your own macro library.

Assuming that’s what people are actually requesting, I can certainly see the attraction – but I’d still prefer it if C# didn’t go down that route. I’ll go back to C++ for the guts of the reason why, but it’s not really about macros at this point. It’s about building your own language. Once, someone told me that C++ wasn’t a language – it was a meta-language; no-one used “bare” C++, they worked out their own language made from the building blocks of normal C++, and then used that.

That may or may not be true – or more likely, it’s true in some places but not others – but it scares me as an idea. I’m not going to claim I know every nuance of C#, but it’s pretty rare that you’d throw a line at me without it being reasonably clear what’s going to happen and why, at the language level. Extension methods might mean a bit more information is required as to where a particular method comes from, but it doesn’t take a lot of digging to see what’s going on.

Now imagine that C# 3 didn’t include query expressions, but that someone had come up with them as a macro library. It’s not an insignificant amount of effort to learn what’s going on there, and how it all maps to normal method calls, potentially with expression trees as arguments instead of delegates. Until you understand what’s going on at a reasonably deep level, you can’t really make any firm decisions as to what code including a query expression will do. (Heck, that’s one of the premises of the book: you should really know this stuff, or at least be aware of it.)

That’s fine when there’s a single macro library used globally, but now imagine every company has their own – or worse still, has a bunch of them grabbed from Code Project, possibly including a load of bugs. Most of us aren’t accomplished language designers, and I suspect there’d be an awful lot of macro libraries out there which weren’t quite thought through enough – but were still useful enough to be attractive. They’d become magnets for code warts.

It’s hard enough when you change company to work out what 3rd party libraries are in use, how they’re being used, what the coding conventions are etc. It would be much worse if I had to learn another flavour of C# itself each time. I’m already worried that developers are picking up C# 3 without having a firm enough grasp of C# 2 – and that’s when there’s just a progression within a single language.

I know this all sounds patronising and/or elitest and/or “nanny state” applied to programming languages – but it’s how I feel nonetheless. I just don’t think we (as a development community) are mature enough to handle that sort of power without it turning into a blood bath. This sort of thing sounds fabulous for hobby and research development, and would probably be great in the hands of the best few companies in the world – but I don’t think it’s a good idea for the mainstream.

Okay – time to hear why I’m wrong :)

Bridging gaps, and finding my role

Warning: this post won’t teach you anything technical. It’s about how I see myself. That may be of interest to you, or it may not. If not, feel free to skip it knowing you’re not missing anything else.

One of the great problems of the world today is undoubtedly this problem of not being able to talk to scientists, because we don’t understand science. They can’t talk to us because they don’t understand anything else, poor dears. (Michael Flanders)

For a while, I’ve made myself slightly miserable (only slightly – I’m generally a very happy guy) by seeing just how impoverished my own understanding of computing is compared with my “heroes” in the industry: Eric Lippert, Joe Duffy, and Wes Dyer to name but three. I always learn a lot from their blogs, but often I don’t manage to take it all in. I understand enough about Wes’s post about monads to realise that I’ve probably implemented a monad with my (incredibly primitive) Future classes in “push LINQ” – but my grasp of them is tenuous at best. I understand enough about threading to be able to reason about concurrency in my day-to-day life, but I’m never going to have Joe’s depth of knowledge of either the Windows-specific stuff or the underlying principles and theories. I can hold an interesting (to me) conversation with Eric over email, but I suspect that if we were talking in real life I’d have to constantly ask him to slow down.

This used to bother me. I used to almost feel that it was unfair that others were so much smarter than me. Yes, I know how arrogant that sounds even in terms of ambition, and I’m not going to flinch away from the term. However, I’m a bit more comfortable with my place in life now. You see, just because they’re so much smarter than me doesn’t mean I’m useless. I want to be a bridge.

People have occasionally used the word “expert” about me, entirely inappropriately. I’m not an expert in threading, or floating point, or text encoding. I know a bit more about those topics than most developers, and sometimes that’s all that’s required to be labeled as an expert these days. After the last ten months, I could probably agree with the label when applied to C# as a language, although certainly not .NET as a framework, or even the act of developing in C#. I happen to have read the spec more closely than most people, and retained that information reasonably well, that’s all.

The trouble is, real experts can be hard to understand sometimes for the “average developer” (again, I know how that sounds; I’m not putting myself above this “average developer” in all or even many respects, just on the topics I happen to write about). Don’t get me wrong: it’s not because they lack communication skills (although that’s sometimes the case). It’s that a lot of what they talk about is at a level of depth which requires a lot of background knowledge for one to properly understand it. This is where I believe I can play a useful role – I like explaining the most useful bits of what I’ve understood from the experts, but in a way which hopefully any interested developer can understand.

C# in Depth is a good manifestation of that. If you want the unadulterated “truth” about C# (at least in theory) you look at the spec. But that’s frankly a pain to read, and there’s very little to distinguish the bits which are really important from the corner cases which you’re unlikely to ever encounter. I hope my book covers provides more depth than the level of knowledge most C# developers already have (and more depth than most other books provide), but without going so deep as to be impenetrably difficult to understand.

Having identified this role, I’m really quite happy to try to fulfil it. I just hope I can keep doing so. It’s a lot of fun.

Types of Parallelism

When I was at TechEd, Joe Duffy mentioned task parallelism and data parallelism a number of times. It was easy enough to follow what he meant, but I had to keep consciously thinking about the terms instead of instinctively knowing what they meant. This post is intended to ram the point home to me as much as anyone else – there’s nothing like writing things up to make them stick in your head.

I thought I had a modicum of success with my “real life example” in the post about “push” LINQ, so I’ll use another one here. The difference is that I won’t be presenting any actual code in this post, just concepts.

Example situation: the soup factory

Suppose you run a soup factory, producing tins of tomato soup. Now, to simplify things massively, we’re going to assume there are three tasks:

  1. Create empty tins. Each empty tin takes 5 minutes to make, and has 400ml volume.
  2. Cook the soup in a big saucepan. It takes one person 1 hour (of constant stirring etc) to cook the soup, and the saucepan holds 20 litres.
  3. Fill the tins with soup. It takes 2 minutes to fill a tin and seal it.

Obviously the numbers here are entirely arbitrary. They just give us something to play with. Now let’s look at different ways we can run the soup factory.

No parallelism: a single worker

We’ll start off with the simplest situation – a single worker, who will just perform one task at a time. Let’s look at how he might spend his time, starting at 8am:

Time Activity
8:00-8:05 Make tin 1
8:05-8:10 Make tin 2
 
12:05-12:10 Make tin 50
12:10-13:10 Cook soup
13:10-13:12 Fill tin 1
13:12-13:14 Fill tin 2
 
14:48-14:50 Fill tin 50

Now, that’s just one way of doing it. He could have cooked the soup first, then made tin 1, filled tin 1, made tin 2, filled tin 2 etc. The only dependency is that we have both an empty tin and some soup before we fill the tin. Let’s give our worker a colleague and see how things can improve.

Just data parallelism: two workers doing the same thing

When we add a second worker, we can do organize things any number of ways, as we’ll see in a minute. However, we’ll stick to a simple way at the moment. Each of the workers will deal with 25 tins, and one will relax while the other is cooking.

Time Activity
  Worker 1 Worker 2
8:00-8:05 Make tin 1 Make tin 2
8:05-8:10 Make tin 3 Make tin 4
   
10:00-10:05 Make tin 49 Make tin 50
10:05-11:05 Cook soup Relax
11:05-11:07 Fill tin 1 Fill tin 2
11:07-11:09 Fill tin 3 Fill tin 4
   
11:53-11:55 Fill tin 49 Fill tin 50

This shows data parallelism. Both the “making tins” and “filling tins” tasks involve 50 tins which are each independent. We don’t need to wait for one tin to be full before starting another – with two people working, we can go twice as quickly. Note that this doesn’t hold for the cooking – two people cooking wouldn’t be able to get the work done any quicker.

With 50 people working like this, we’d have a total time of 1 hour and 7 minutes – five minutes of everyone making tins, one hour of soup cooking, and then two minutes of everyone filling tins. Now let’s try something different with our two workers. Adding extra workers at that point wouldn’t help.

Task parallelism: one person cooking while the other makes tins

We’ve seen that the cooking is the bottleneck – in our previous example, we were wasting time while the soup was cooking. We don’t want workers relaxing on the job! Let’s try a different kind of parallelism now – task parallelism, where instead of breaking a single task into multiple independent bits, we perform two wholly different tasks at the same time.

We’ll have one worker make tins while the other cooks soup. They can both fill tins afterwards.

Time Activity
  Worker 1 Worker 2
8:00-8:05 Make tin 1 Start cooking soup
8:05-8:10 Make tin 2 Still cooking…
   
9:00-9:05 Make tin 13 Finished cooking – relax
12:05-12:10 Make tin 50 Still relaxing
12:10-12:12 Fill tin 1 Fill tin 2
12:12-12:14 Fill tin 3 Fill tin 4
   
12:58-13:00 Fill tin 49 Fill tin 50

This is actually worse than the previous time – but could be improved by the second worker helping to make tins after the soup has finished cooking. This time, if we went up to 51 workers we’d have the fastest possible time for a single batch – 1 hour and 2 minutes.

Dependencies and pipelines

I think that’s enough detailed examples – it should be enough to demonstrate the difference between the two types of parallelism. However, I’d like to consider one more aspect: dependencies. When using task parallelism, you need to be aware of what dependencies are involved. As noted earlier, there’s no dependency between making tins and cooking the soup, but filling the tins depends on there being an empty tin available, and being soup available. That’s what makes the minimum possible time for a single batch 1 hour and 2 minutes rather than just the “longest single task” of 1 hour for cooking the soup.

So, does that mean that n batches of soup will take n * (1 hour and 2 minutes) to create? No – because we can form a pipeline for the work. Suppose we can start cooking another load of soup while the first one is being used to fill tins. We can get our average time for a batch down to an hour, using 7 people: 1 person always cooking, 4 people always making tins, 1 person always filling tins and 1 person making a couple of tins and then filling for the rest of the hour. The time taken for the first batch is relatively high, but thereafter we can keep producing a new batch of 50 full tins every hour.

Few processes in computing are as straightforward as this “assembly line” – but it’s a useful model to remind us of the two types of parallelism possible.

Boxing day

Here in the UK (and possibly elsewhere – I’m too lazy to check Wikipedia) December 26th is called Boxing Day. I want to know why only this one aspect of .NET is given its own public holiday. Expanding the opportunities to language features as well as those of the runtime, I’d like to see:

  • Null coalescing day (“Take 3 nulls into the shower? Not me. I just coalesce and go!”)
  • Type inference day (not popular with HR)
  • Garbage collection day (as a proper holiday, not just when the bins are collected)
  • Just-In-Time day (arguably every day for procrastinators such as myself)
  • Lambda expression day (get those lambdas out of the closet)

I could go on, but it’s a pretty thin joke and dinner is ready.