Re: In PG12, query with float calculations is slower than PG11 - Mailing list pgsql-hackers

From Amit Langote
Subject Re: In PG12, query with float calculations is slower than PG11
Date
Msg-id CA+HiwqGg_LkuWcEQ=Zzdq8TD_PrcPpWJGCV9FmWRbcHe9G+2Gg@mail.gmail.com
Whole thread Raw
In response to Re: In PG12, query with float calculations is slower than PG11  (Andres Freund <andres@anarazel.de>)
Responses Re: In PG12, query with float calculations is slower than PG11
List pgsql-hackers
Hi,

On Thu, Feb 6, 2020 at 2:55 PM Andres Freund <andres@anarazel.de> wrote:
> On 2020-02-06 14:25:03 +0900, keisuke kuroda wrote:
> > That's because check_float8_val() (in PG 12) is a function
> > whose arguments must be evaluated before
> > it is called (it is inline, but that's irrelevant),
> > whereas CHECKFLOATVAL() (in PG11) is a macro
> > whose arguments are only substituted into its body.
>
> Hm - it's not that clear to me that it is irrelevant that the function
> gets inlined. The compiler should know that isinf is side-effect free,
> and that it doesn't have to evaluate before necessary.
>
> Normally isinf is implemented by a compiler intrisic within the system
> headers. But not in your profile:
> > ★ 5.41%  postgres  libc-2.17.so       [.] __isinf
>
> I checked, and I don't see any references to isinf from within float.c
> (looking at the disassembly - there's some debug strings containing the
> word, but that's it).
>
> What compiler & compiler version on what kind of architecture is this?

As Kuroda-san mentioned, I also checked the behavior that he reports.
The compiler I used is an ancient one (CentOS 7 default):

$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)

Compiler dependent behavior of inlining might be relevant here, but
there is one more thing to consider. The if () condition in
check_float8_val (PG 12) and CHECKFLOATVAL (PG 11) is calculated
differently, causing isinf() to be called more times in PG 12:

static inline void
check_float8_val(const float8 val, const bool inf_is_valid,
                 const bool zero_is_valid)
{
    if (!inf_is_valid && unlikely(isinf(val)))
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("value out of range: overflow")));

#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid)         \
do {                                                            \
    if (isinf(val) && !(inf_is_valid))                          \
        ereport(ERROR,                                          \
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),   \
          errmsg("value out of range: overflow")));             \

called thusly:

    check_float8_val(result, isinf(val1) || isinf(val2),
                     val1 == 0.0 || val2 == 0.0);

and

    CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
                  arg1 == 0 || arg2 == 0);

from float8_mul() and float8mul() in PG 12 and PG 11, respectively.

You may notice that the if () condition is reversed, so while PG 12
calculates isinf(arg1) || isinf(arg2) first and isinf(result) only if
the first is false, which it is in most cases, PG 11 calculates
isinf(result) first, followed by isinf(arg1) || isinf(arg2) if the
former is true.  I don't understand why such reversal was necessary,
but it appears to be the main factor behind this slowdown.  So, even
if PG 12's check_float8_val() is perfectly inlined, this slowdown
couldn't be helped.

Thanks,
Amit



pgsql-hackers by date:

Previous
From: Amit Kapila
Date:
Subject: Re: typo in set_rel_consider_parallel()
Next
From: Amit Langote
Date:
Subject: Re: Identifying user-created objects