Boost logo

Boost :

Subject: [boost] [xpressive] Performance Tuning?
From: Stewart, Robert (Robert.Stewart_at_[hidden])
Date: 2009-06-30 17:04:00


I was experimenting with Xpressive to see how it would compare with some custom, string-based numeric input parsing. The Xpressive code is over 175X slower than the custom code. Have I done anything to make it really slow? Can it be made faster? I have included the Xpressive code and the performance testing code below for your study. Can you spot anything I've done wrong?

(BTW, I tried using Spirit.Qi for this -- with Boost 1.37 -- but never could get things to compile. I gave up and decided I'd wait until I can use a newer release before I fight that fight again. :-} )

FYI: bar::numeric_cast winds up calling strtod(), strtol(), etc. It is semantically equivalent to lexical_cast.

#include <boost/xpressive/xpressive_static.hpp>
#include <boost/xpressive/regex_actions.hpp>

namespace foo
{
   namespace op
   {
      template <class T>
      struct numeric_cast
      {
         typedef T result_type;

         template <class Value>
         T
         operator ()(Value const & _value) const
         {
            return bar::numeric_cast<T>(_value);
         }
      };
   }

   struct direct_impl
   {
      typedef int64_t result_type;

      template <class Value>
      int64_t
      operator ()(Value const & _value) const
      {
         int64_t result(bar::numeric_cast<int64_t>(_value));
         result *= 160000;
         return result;
      }
   };

   struct to_price_impl
   {
      typedef int64_t result_type;

      template <class Value>
      int64_t
      operator ()(Value const & _value) const
      {
         int64_t result(0);
         if (_value)
         {
            double const value(bar::numeric_cast<double>(_value));
            result = static_cast<int64_t>(
               std::floor(160000.0 * value + 0.5));
         }
         return result;
      }

      template <class Value>
      int64_t
      operator ()(Value const & _numerator, Value const & _denominator) const
      {
         unsigned const numerator(bar::numeric_cast<unsigned>(_numerator));
         unsigned const denominator(bar::numeric_cast<unsigned>(_denominator));
         double const fraction((160000.0 * numerator) / denominator);
         return static_cast<int64_t>(std::floor(fraction + 0.5));
      }

      template <class Value>
      int64_t
      operator ()(Value const & _whole, double const _fraction) const
      {
         int const whole(bar::numeric_cast<int>(_whole));
         double const value(160000.0 * whole + _fraction);
         return static_cast<int64_t>(std::floor(value + 0.5));
      }
   };

   int64_t
   parse(std::string const & _value);

   boost::xpressive::function<direct_impl>::type const direct = {{}};
   boost::xpressive::function<to_price_impl>::type const to_price = {{}};
   BOOST_PROTO_DEFINE_FUNCTION_TEMPLATE(
      1
      , numeric_cast
      , boost::proto::default_domain
      , (boost::proto::tag::function)
      , ((op::numeric_cast)(typename))
      )
}

int64_t
foo::parse(std::string const & _value)
{
   using namespace boost::xpressive;

   if (_value.empty())
   {
      return 0;
   }
   int64_t result;
   bool negative(false);
   sregex zero = as_xpr('0');
   sregex sign =
      as_xpr('-') [ ref(negative) = true ]
      | as_xpr('+'); // ignore
   // ignore leading zeroes to avoid octal parsing
   sregex fraction =
      (
         !zero >> (s1= +_d) >> !blank >> '/' >> !blank >> !zero >> (s2= +_d)
      ) [ ref(result) = to_price(s1, s2) ];
   sregex real =
      (
         *_d >> '.' >> *_d >> !((set= 'E', 'e', 'G', 'g') >> +_d)
      ) [ ref(result) = to_price(_) ];
   // ignore leading zeroes to avoid octal parsing
   sregex whole_number =
      (
         !zero >> (s1= +_d) >> +blank >> fraction
      ) [ ref(result) = to_price(s1, ref(result)) ];
   // ignore leading zeroes to avoid octal parsing
   sregex integer =
      (
         !zero >> (s1= +_d)
      ) [ ref(result) = direct(s1) ];
   sregex price =
      *blank
>> !sign
>> (real | whole_number | fraction | integer)
>> *space;
   smatch match;
   if (!regex_match(_value.begin(), _value.end(), match, price))
   {
      throw price_parsing_failed(_value);
   }
   if (negative)
   {
      result = -result;
   }
   return result;
}

void
test_performance()
{
   Timer timer;
   std::string const input("1234.5678e10");
   int64_t total(0);
   foo::parse(input); // prime the cache
   timer.start();
   for (int i(0); i < 10000000; ++i)
   {
      int64_t price(foo::parse(input));
      price += i;
      total += price;
   }
   timer.stop();
   std::cout << timer.elapsed() << "s elapsed (total = " << total ')'
      << std::endl;
}

_____
Rob Stewart robert.stewart_at_[hidden]
Software Engineer, Core Software using std::disclaimer;
Susquehanna International Group, LLP http://www.sig.com

IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk