faster add, subtract, multiply
authorDavid Mitchell <davem@iabyn.com>
Thu, 22 Oct 2015 11:04:40 +0000 (12:04 +0100)
committerDavid Mitchell <davem@iabyn.com>
Tue, 10 Nov 2015 13:52:34 +0000 (13:52 +0000)
commit230ee21f3e366901ce5769d324124c522df7ce8a
treefd772e2d8d5d940426b6037f163500cc7ee89dfe
parentedba15b0ccf22f00ea6d7cc58eb2c173437a09cc
faster add, subtract, multiply

In pp_add, pp_subtract and pp_multiply, special-case the following:

* both args IV, neither arg large enough to under/overflow
* both args NV.

Starting in 5.8.0, the implementation of the arithmetic pp functions
became a lot more complex (and famously, much slower), due to the need
to support 64-bit integers.

For example, formerly  pp_add just converted both its args to an NV and
returned an NV. On 64-bit systems, that could gave bad results if the
mantissa of an NV was < 64 bits; for example:

    $ perl561 -e'$x = 0x1000000000000000; printf "%x\n", $x+1'
    1000000000000000
    $ perl580 -e'$x = 0x1000000000000000; printf "%x\n", $x+1'
    1000000000000001

This led to a lot of complex code that covered all the possibilities
of overflow etc.

This commit adds some special casing to these three common arithmetic ops.
It does some quick checks (mainly involving fast boolean and bit ops)
to determine if both args are valid IVs (and not UVs), are not magic,
and aren't very big (+ve or -ve). In this case, the result is simply
SvIVX(svl) + SvIVX(svr) (or - or *) with no possibility of overflow.
Failing that, if both args are NV's and not magic, then if both NVs
can be converted to IVs without loss, handle as for the IV case; failing
that, just return SvNVX(svl) + SvNVX(svr);

For all other cases, such as mixed IV and NV or PV, fall back to the old
code.

On my platform (x86_64), it (along with the previous commit) reduces the
execution time of the nbody benchmark (lots of floating-point vector
arithmetic) by a third and in fact makes it 10% faster than 5.6.1.
pp.c
pp_hot.c
t/op/64bitint.t
t/op/taint.t
t/perf/benchmarks