1

Fail-fast seems like a right way since it simplifies bug detection. But it's a harm for performance cause of multiple checking the same thing at several levels of the system.

Simple example. There is a function that input parameters must be not null. And there is function wrapping it, that also await the same parameters also not null. After some activities function-wrapper passes input parameters into first one. So that the same items checked two times: at the beginning of function wrapper and inside wrapped function.

So I would like to know how much widespread this style is. Should I write fail-fast code or check everything just once?

  • One fail fast paradigm is "kill the process/task when an error occurs" which only needs one check per error source. But that's probably not what you mean. Are you talking about exception handling? – user7043 Jan 29 '14 at 13:12
  • Fail-fast involves checking earlier rather than later, not early and late. Don't confuse it with e.g. client-side and server-side validation - that is different and semantically necessary rather than a design decision. – Kilian Foth Jan 29 '14 at 13:13
  • Please see the edits. – SerG Jan 29 '14 at 13:31
  • Did you profile the code for possible performance problems or is this just exercise in premature optimization? – Euphoric Sep 21 '16 at 6:41
10

You're missing a vital point - it's not an either or scenario.

You only need to check "untrusted" parameters. In general, this means the borders of your public interface. If you have a chain of public functions that call other public functions and so on, yes, you'll need to check the input multiple times. You also maybe need to revisit your design to properly abstract and encapsulate things that maybe don't need to be public.

Another spot where this occurs is working with components that can fail (like databases or network connections). If a network connection fails, you don't just pass null back up the call stack, you identify it early and throw an exception or otherwise abort. There's no need to check the return value all the way up the stack.

5

Early checking of preconditions will be as fast or faster than doing the same checks in the middle of your calculation. The italic part is where your reasoning goes astray. Fail-fast/early checking does not mean you check more things or check them more often, it means that you perform the same checks earlier in the calculation (preferably before you start doing any 'real work' in a function).

To take your example of two functions (Foo and Bar) where both require their parameters to be non-null and Foo invokes Bar passing some of its parameters on.
With early checking, both function check if their parameters meet the precondition of being non-null.
Without early checking, both functions still need to make sure that their parameters are non-null, but the check gets delayed until the first use of the parameter and, worst case, has to be repeated on each subsequent use (depending on the code structure).

If, without early checking, Bar doesn't verify that the parameters are non-null (trusting that the check has already been done in Foo), then the same trust can be used to leave out the check in the early checking case.

  • And what is more acceptable: to grow such a "trust-chain" or review the state every time? – SerG Jan 29 '14 at 14:02
  • 4
    @SerG: That differs from community to community and language to language. I prefer validation checks at module boundaries and trust within modules (with occasional asserts to verify that my trust is still well founded). – Bart van Ingen Schenau Jan 29 '14 at 14:16
2
  1. Use references and static analysis tools to avoid production code overload with trivial checking.

  2. Always handle real (as opposed to imaginary) unexpected conditions. The handling should crash development/debug builds but it should log message and handle the problem in production builds.

  3. Pack crash/log request into short macro. Say 'soft_err()' is more than enough: there is no point in elaborated messages: it would not happen often enough to justify the typing, file-name and line number will be enough to debug issues from the field and for development builds there will be core-dumps.

  4. Note: because handling of unexpected conditions would not happen often it is OK for it to be relatively expensive: say call back-end function to log or crash instead of relying on macros, make back-end functions installed at link time to adapt code to various environments etc.

The combined benefits far outweigh the run-time cost of checking.

1

@DieterLucking is absolutely right but I wanted to add on to what he said. That rule doesn't apply to just C++ but to just about any language as well including managed ones.

The idea of fail fast is that you do not go overboard and provide all kinds of checking and exception handling to "try" to keep running when there are unexpected error conditions because you are under the impression my product is 24/7 it has to run. Being able to run 24/7 is a great requirement and we can all strive for that, but the reality is that the product needs to run correctly 24/7. But software does have an occasional bug, some are recoverable but many aren't. In those cases, you might as well crash, because a server process that crashes, goes offline for 20 seconds and gets restarted is still a better alternative than a server process that keeps running for hours but handles incoming requests incorrectly for hours until a human restarts it.

So what does that mean for you as a developer? If there are unexpected circumstances, don't check for them (you will never anticipate them all even if you try) and simply let your code crash. When that happens, you will be able to capture a crash dump and determine the exact location of the crash and often the root cause.

And before someone reads this, gets all fired up and starts commenting how I advocate writing code that crashes and how that makes me a terrible developer, here's a little story.

An engineer comes to me for help. Says he spent a full week working on one customer problem and he is out of ideas. They can duplicate the problem by following an exact series of steps, which requires opening dialog A, clicking a bunch of buttons, then closing that dialog, and going to a completely different part of the application. If you do those steps exactly, your app crashes but he can't figure out why.

So together we take a look at the crash dump and see that processes heap has been corrupted, but there is no indication where or how that happened. After a whole bunch of digging we found code in dialog A that looked like this:

try {
    ... do some work
}
catch( ... ) {
    ... not even a log statement here ...
}

So the code in dialog A was going off the deep end and doing some really bad stuff. Instead of crashing early with a stack trace that shows the exact problem the developer decided that crashes are bad and app must stay running. So instead, the app would crash 15 min to 4 hours later in a completely random place when you were doing a completely harmless action. That developer didn't help anyone with his protected code.

  • ++ Amen. Let it crash. – RubberDuck Sep 20 '16 at 22:42
0

In C/C++ it is even common not to apply any check at all (if you have no debug code). Having a function like memcpy and making it checking the arguments implies a serious overall overhead. Hence preconditions are applied to the usage of the function (putting all responsibility to the programmer). The best 'fail fast' is a failure at compilation time. With C++ it could be a function taking a reference and no pointer (but it will not prevent a programmer to dereference a pointer and pass that).

  • Maybe it's one of the causes of opinion that C++ is very convenient tool for shoot in the foot. – SerG Jan 29 '14 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.