lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Parallel blues


From: Greg Chicares
Subject: Re: [lmi] Parallel blues
Date: Thu, 22 Jun 2017 13:32:53 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0

On 2017-06-21 12:50, Vadim Zeitlin wrote:
> On Wed, 21 Jun 2017 12:26:36 +0000 Greg Chicares <address@hidden> wrote:
[...]
> GC> Then I realized that I could implement
> GC>   bool is_null_stream(std::ostream&);
> GC> and use that to conditionalize stream output in 'zero.hpp', and its
> GC> effect on calendar_date_test's speed is the same as using #ifdef.
> 
> [...] OTOH, forgetting to use is_null_stream() will almost
> certainly go unnoticed and just result in much poorer performance during
> run-time, even in production code. This doesn't seem like a good trade off
> to me.

Yes, we can do better. Referring back to earlier messages in this thread:

On 2017-06-21 12:26, Greg Chicares wrote:
> On 2017-06-20 23:28, Vadim Zeitlin wrote:
[...]
>> And we do need to be able to test for stream absence because
>> otherwise we wouldn't be able to get rid of value_cast<> calls overhead
>> which is probably more significant here than that of actually inserting
>> into the stream. And to avoid it, we simply must make the calls to
>> operator<<() conditional, hence we need something to test for.
> 
> Indeed. My first attempt was to set the null stream's 'badbit', which I
> thought would block operator<<() because no sentry object is created;
> but that didn't help, because in a '<<' chain like
>   os << foo() << bar()
> foo() and bar() are still called.

Setting 'badbit' works quite nicely, though, if we use only built-in
stream formatting. In HEAD, 'zero.hpp' has code like:

    iteration_stream
        << "iteration " << number_of_iterations++
        << " iterand "  << value_cast<std::string>(a)
        << " value "    << value_cast<std::string>(fa)
        << std::endl
        ;

where, even if 'badbit' is set, value_cast<>() is always called.
But we don't really need value_cast<>() here, because this trace
is just as useful if we do this instead[0]:
  iteration_stream.precision(DECIMAL_DIG);
to make sure no precision is lost. Then, if we add
+ iteration_stream.setstate(std::ios::badbit);
to the patch below[0] (as a careless expedient, knowing that badbit
really should be set in the null_stream() implementation), we get
almost all the benefit of #ifdef:

On 2017-06-20 23:10, Greg Chicares wrote:
> with iteration_stream #ifdef'd out in 'calendar_date_test.cpp'
[...]
> ..and now each TestBirthdateLimitsExhaustively() call takes about
> one-sixth of a second instead of two and a half seconds, so that
> function runs fifteen times faster.

because we observe that each such call takes about one-third of a
second. That's only about seven times faster--it saves only 87% of
the overhead instead of 93%--but it lets us write clean code that
just works (as long as we avoid calling free functions like
value_cast<>(), and let the iostreams library handle formatting
instead). If we set 'badbit' as a postcondition of null_stream(),
then we can't forget to do it whenever we use a null stream.

Examining the other cases where null_stream() is used:

'progress_meter_cli.cpp': we do have
    os_ << progress_message() << std::flush;
but that virtual function has this default implementation:
        return ".";
so it shouldn't be too costly.

'system_command_wx.cpp': this is cheap anyway, and probably is
optimized away:
    statusbar_if_available << "Running..." << std::flush;
and this is pretty cheap (and it's only called once):
    statusbar_if_available << timer.stop().elapsed_msec_str() << std::flush;

---------

[0] "do this instead", "the patch below":

diff --git a/zero.hpp b/zero.hpp
index 7ac9f1b..8800bb8 100644
--- a/zero.hpp
+++ b/zero.hpp
@@ -26,8 +26,8 @@
 
 #include "null_stream.hpp"
 #include "round_to.hpp"
-#include "value_cast.hpp"
 
+#include <cfloat>                       // DECIMAL_DIG
 #include <cmath>
 #include <limits>
 #include <ostream>
@@ -241,6 +241,7 @@ root_type decimal_root
     ,std::ostream&   iteration_stream       = null_stream()
     )
 {
+    iteration_stream.precision(DECIMAL_DIG);
     static double const epsilon = std::numeric_limits<double>::epsilon();
 
     int number_of_iterations = 0;
@@ -255,8 +256,8 @@ root_type decimal_root
     double fa = static_cast<double>(f(a));
     iteration_stream
         << "iteration " << number_of_iterations++
-        << " iterand "  << value_cast<std::string>(a)
-        << " value "    << value_cast<std::string>(fa)
+        << " iterand "  << a
+        << " value "    << fa
         << std::endl
         ;
     if(0.0 == fa)
@@ -267,8 +268,8 @@ root_type decimal_root
     double fb = static_cast<double>(f(b));
     iteration_stream
         << "iteration " << number_of_iterations++
-        << " iterand "  << value_cast<std::string>(b)
-        << " value "    << value_cast<std::string>(fb)
+        << " iterand "  << b
+        << " value "    << fb
         << std::endl
         ;
     double last_evaluated_iterand = b; // Note 1.
@@ -401,8 +402,8 @@ root_type decimal_root
             last_evaluated_iterand = b;
             iteration_stream
                 << "iteration " << number_of_iterations++
-                << " iterand "  << value_cast<std::string>(b)
-                << " value "    << value_cast<std::string>(fb)
+                << " iterand "  << b
+                << " value "    << fb
                 << std::endl
                 ;
             }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]