On the Lambda

Programming, Technology, and Systems Administration

On the Lambda

There are worse things than Exceptions

October 21st, 2014 · No Comments · .net, c#, development, stackoverflow

A piece of advise I’ve given on Stack Overflow more than once is to avoid the File.Exists() method, and others like it. Instead, I’ll tell people to just use a try/catch block, and put their time into writing a good exception handler. I won’t re-hash the reasoning here, as I’ve already covered it before. One of those links was even Gold badge -worthy.

One of the responses I often get to this strategy is that handling exceptions is slow. Why risk a slow exception handler if you can avoid it most of the time with a quick File.Exists() check? I think this argument misses the point first of all for correctness reasons. You still need the exception handler, and using File.Exists() to avoid it is a mistake. But more than that, I think that is just plain wrong about the performance issue, too. Here’s why.

Yes, handling exceptions is expensive from a performance standpoint; very expensive. Let’s get that out of the way: I’m not trying to say that exceptions should be your first choice in every situation. The list of things you can do in programming that are slower is very short. However, the list is not empty. Do you know what’s worse than exceptions? I/O. Disk and Network are far and away worse. Let me explain. Here’s a link and except that show just how much worse they can be:

https://gist.github.com/jboner/2841832

Latency Comparison Numbers
--------------------------
L1 cache reference                            0.5 ns
Branch mispredict                             5   ns
L2 cache reference                            7   ns             14x L1 cache
Mutex lock/unlock                            25   ns
Main memory reference                       100   ns             20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy              3,000   ns
Send 1K bytes over 1 Gbps network        10,000   ns    0.01 ms
Read 4K randomly from SSD*              150,000   ns    0.15 ms
Read 1 MB sequentially from memory      250,000   ns    0.25 ms
Round trip within same datacenter       500,000   ns    0.5  ms
Read 1 MB sequentially from SSD*      1,000,000   ns    1    ms  4X memory
Disk seek                            10,000,000   ns   10    ms  20x datacenter roundtrip
Read 1 MB sequentially from disk     20,000,000   ns   20    ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA     150,000,000   ns  150    ms

If thinking in nanoseconds isn’t your thing, here’s another reference that normalizes a single CPU cycle as 1 second and scales from there:

http://blog.codinghorror.com/the-infinite-space-between-words/

1 CPU cycle             0.3 ns      1 s
Level 1 cache access    0.9 ns      3 s
Level 2 cache access    2.8 ns      9 s
Level 3 cache access    12.9 ns     43 s
Main memory access      120 ns      6 min
Solid-state disk I/O    50-150 μs   2-6 days
Rotational disk I/O     1-10 ms     1-12 months
Internet: SF to NYC     40 ms       4 years
Internet: SF to UK      81 ms       8 years
Internet: SF to AUS     183 ms      19 years
OS virt. reboot         4 s         423 years
SCSI command time-out   30 s        3000 years
Hardware virt. reboot   40 s        4000 years
Physical system reboot  5 m         32 millenia

Taking even the best-case scenario for exceptions, you can access memory at least 480 times while waiting on the first response from a disk, and that’s assuming a very fast SSD. Many of us still need spinning hard-drives, where things get much, much worse.

For a comparison reference, Jon Skeet has blogged about exception handling, where he was able to handling them at a rate of between 42 and 188 per millisecond. While there were some issues with his benchmark, I think the point is spot on: relative to other options, exceptions may not be as bad as you think.

And that’s only the beginning of the story. When you use .Exists(), you incur this additional cost (and it is an addition: you have to do the same work again when you go to open the file) on every attempt. You pay this costs whether the file exists or not, because the disk still has to go look for it in it’s file tables. With the exception method, you only pay the extra costs like unwinding the call stack in the case of failure.

In other words, yes: exceptions are horribly costly. But compared to the disk check, it’s still faster — and not by just a small margin. Thankfully, this is unlikely to drive your app’s general performance… but I still want to put to bed the “exceptions are slow” argument for this specific task.

Tags:

No Comments so far ↓

There are no comments yet...Kick things off by filling out the form below.

Leave a Comment