Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??
water,

Excellent points, all of them, however I do disagree with a few of them.

And if you can (risk, speed, etc), leave them in and turned on in production code (with an appropriate handler, of course). (After all, these are assertions, and thus should NEVER happen, right?)

This I do not disagree with, but instead strongly agree with. After all, if one of your pre-conditons is that you get passed a connected database handle, or a writeable filehandle, would you not want to check that at runtime as well? Assertions and Contracts are not just for debugging, they are ways in which you can make your code more reliable and robust.

The program run time will very likely be dominated by IO, usually disk, db, network, web fetches, etc, and thus the speed difference between  assert (defined $foo, 'defined foo') and  defined $foo || die 'defined foo'; should be irrelevent in real code

This is true, assertions will almost never be your bottleneck, but personally I have assertions just about everywhere, which includes methods and functions which need to get called inside tight loops, as well as at time critical portions of code.

For instance I have a part of a reporting app which spends a lot of time doing a DB query, once I get the results back (up to 30,000+ rows) I need to loop through them all and calculate various values. My query has already taken a long time, so anything more I do, I want it to be fast so that the user doesn't think somethings wrong. I use these fast assertions inside the calcualtion routines and loops to make sure my values are always valid before I start. Doing this has the added benefit of keeping my calculation code simple and fast since it never gets an edge case so it doesnt need to handle them.

I guess my point is that, yes, IO/Network/DB stuff can take a while and certainly be the likelest bottleneck, but sometimes this means your post-processing code needs to be that much faster.

Machine time is cheap and Programmer time is expensive -- what is clearest? Which is easiest for others to read?

Agreed, but IMHO, my way is clearer :)

You say TOmato, I say TomAto. It's all a matter of style.

Making assert a sub makes it Very Easy to change the assert behavior of the whole code base (logging, reponding to errors, correct shutdown in case of Really Bad Errors, etc), whereas the in-line || doesn't give you those options

Not so, a modification of the example above will show that it can be done:

sub insertQuarter {     my ($coin) = @_;     (defined($coin) && $coin == 25) || errorHandler(You must insert 25 cents");     initializeGame();     startGame(); }
Where the code for errorHandler can be changed to do just about anything (log, die, warn, etc etc etc). IMO this is just as flexible as an assert subroutine. It is also faster too. Here is some code which benchmarks not only a raw OR against an assert sub, but also benchmakrs using the OR with the errorHandler as well as an example of no-op versions of both errorHandler and assert.

#!/usr/bin/perl use strict; use warnings; use Benchmark qw(:all); ## -- Sub ----------------------------------------------------------- sub assert { my $cond = shift; $cond || die "Test is wrong"; } sub SubAssert { my ($test) = @_; assert($test < 50); return $test; } sub assertNoOp {} sub SubAssertNoOp { my ($test) = @_; assertNoOp($test < 50); return $test; } ## -- OR ------------------------------------------------------------ # Plain Or assertions sub OrAssert { my ($test) = @_; ($test < 50) || die "Test is wrong"; return $test; } # Or assertion with a handler sub errorHandler { my $message = shift; die $message; } sub OrAssertHandler { my ($test) = @_; ($test < 50) || errorHandler("Test is wrong"); return $test; } # Or assertion with no-op handler (turned off) sub NoOpErrorHandler {} sub OrAssertHandlerNoOp { my ($test) = @_; ($test < 50) || NoOpErrorHandler("Test is wrong"); return $test; } ## -- test ---------------------------------------------------------- my @nums = map { ((rand() * 100) % 50) } (0 .. 100); print(("-" x 80) . "\n"); print "Testing raw OR against sub\n"; print(("-" x 80) . "\n"); cmpthese(10_000, { 'SubAssert' => sub { eval { SubAssert($_) } for (@nums) }, 'OrAssert' => sub { eval { OrAssert($_) } for (@nums) }, }); print(("-" x 80) . "\n"); print "Testing with OR Error Handler and raw sub\n"; print(("-" x 80) . "\n"); cmpthese(10_000, { 'SubAssert' => sub { eval { SubAssert($_) } for (@nu +ms) }, 'OrAssertHandler' => sub { eval { OrAssertHandler($_) } for (@nu +ms) }, }); print(("-" x 80) . "\n"); print "Testing with no-op OR Error Handler and no-op sub (assertions o +ff)\n"; print(("-" x 80) . "\n"); cmpthese(10_000, { 'SubAssertNoOp' => sub { eval { SubAssertNoOp($_) } fo +r (@nums) }, 'OrAssertHandlerNoOp' => sub { eval { OrAssertHandlerNoOp($_) } fo +r (@nums) }, });
Here are the results of this script on my machine
---------------------------------------------------------------------- +---------- Testing raw OR against sub ---------------------------------------------------------------------- +---------- Benchmark: timing 10000 iterations of OrAssert, SubAssert... OrAssert: 8 wallclock secs ( 7.20 usr + 0.00 sys = 7.20 CPU) @ 13 +88.89/s (n=10000) SubAssert: 11 wallclock secs (10.83 usr + 0.07 sys = 10.90 CPU) @ 91 +7.43/s (n=10000) Rate SubAssert OrAssert SubAssert 917/s -- -34% OrAssert 1389/s 51% -- ---------------------------------------------------------------------- +---------- Testing with OR Error Handler and raw sub ---------------------------------------------------------------------- +---------- Benchmark: timing 10000 iterations of OrAssertHandler, SubAssert... OrAssertHandler: 7 wallclock secs ( 7.12 usr + 0.06 sys = 7.18 CPU) + @ 1392.76/s (n=10000) SubAssert: 12 wallclock secs (10.56 usr + 0.12 sys = 10.68 CPU) @ 93 +6.33/s (n=10000) Rate SubAssert OrAssertHandler SubAssert 936/s -- -33% OrAssertHandler 1393/s 49% -- ---------------------------------------------------------------------- +---------- Testing with no-op OR Error Handler and no-op sub (assertions off) ---------------------------------------------------------------------- +---------- Benchmark: timing 10000 iterations of OrAssertHandlerNoOp, SubAssertNo +Op... OrAssertHandlerNoOp: 8 wallclock secs ( 7.18 usr + 0.04 sys = 7.22 +CPU) @ 1385.04/s (n=10000) SubAssertNoOp: 10 wallclock secs ( 9.17 usr + 0.02 sys = 9.19 CPU) @ + 1088.14/s (n=10000) Rate SubAssertNoOp OrAssertHandlerNoOp SubAssertNoOp 1088/s -- -21% OrAssertHandlerNoOp 1385/s 27% --

As you can see from the results, using a basic assert subroutine is about 34% slower than the raw OR. When you add the flexibility of an errorHandler subroutine on the other end of OR, you lose only 1% (which is surely insignifagant), and gain the same flexibility an assert subroutine would have. Even when you turn off assertions and make both the assert and errorHandler routines into no-ops, the assert version is still a 21% slower (which really is useless overhead since much of that is likely just the call to assert).

Again, I like my assertions to be on at runtime, and I put them everywhere, so for me, these performance gains are a nice thing to have. But even if you don't need to worry about performance, I see little gain in flexibility over using an assert sub.

This is not a criticism of his post or his method, just a caution to the less-experienced: Premature optimization is the root of all evil .

Yes, quite true, but would you not want to make sure your always using the sharpest knife in the drawer?

I do not mean this as a means of premature optimization, but as a style, which IMO, is clear and readable, but also has the added benefit of being faster than many of the other options out there.

But then again, I hate Pepsi, and I hate Coke,... gimme some Mountain Dew anyday ;-)

-stvn

In reply to Re^2: Being more Assert-ive with Perl by stvn
in thread Being more Assert-ive with Perl by stvn

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (4)
As of 2024-04-18 02:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found