Thread

Commits

  1. Hand code string to integer conversion for performance.

  2. Deduplicate "invalid input syntax" messages for various types.

  1. Table with large number of int columns, very slow COPY FROM

    Alex Tokarev <dwalin@dwalin.ru> — 2017-12-08T04:21:45Z

    Hi,
    
    I have a set of tables with fairly large number of columns, mostly int with
    a few bigints and short char/varchar columns. I¹ve noticed that Postgres is
    pretty slow at inserting data in such a table. I tried to tune every
    possible setting: using unlogged tables, increased shared_buffers, etc; even
    placed the db cluster on ramfs and turned fsync off. The results are pretty
    much the same with the exception of using unlogged tables that improves
    performance just a little bit.
    
    I have made a minimally reproducible test case consisting of a table with
    848 columns, inserting partial dataset of 100,000 rows with 240 columns. On
    my dev VM the COPY FROM operation takes just shy of 3 seconds to complete,
    which is entirely unexpected for such a small dataset.
    
    Here¹s a tarball with test schema and data:
    http://nohuhu.org/copy_perf.tar.bz2; it¹s 338k compressed but expands to
    ~50mb. Here¹s the result of profiling session with perf:
    https://pastebin.com/pjv7JqxD
    
    
    -- 
    Regards,
    Alex.
    
    
    
    
  2. Re: Table with large number of int columns, very slow COPY FROM

    Andreas Kretschmer <andreas@a-kretschmer.de> — 2017-12-08T07:20:40Z

    
    On 08.12.2017 05:21, Alex Tokarev wrote:
    > I have made a minimally reproducible test case consisting of a table 
    > with 848 columns
    
    Such a high number of columns is maybe a sign of a wrong table / 
    database design, why do you have such a lot of columns? How many indexes 
    do you have?
    
    Regards, Andreas
    
    
    
  3. Re: Table with large number of int columns, very slow COPY FROM

    Andres Freund <andres@anarazel.de> — 2017-12-08T18:17:34Z

    Hi,
    
    On 2017-12-07 20:21:45 -0800, Alex Tokarev wrote:
    > I have a set of tables with fairly large number of columns, mostly int with
    > a few bigints and short char/varchar columns. I¹ve noticed that Postgres is
    > pretty slow at inserting data in such a table. I tried to tune every
    > possible setting: using unlogged tables, increased shared_buffers, etc; even
    > placed the db cluster on ramfs and turned fsync off. The results are pretty
    > much the same with the exception of using unlogged tables that improves
    > performance just a little bit.
    
    > I have made a minimally reproducible test case consisting of a table with
    > 848 columns, inserting partial dataset of 100,000 rows with 240 columns. On
    > my dev VM the COPY FROM operation takes just shy of 3 seconds to complete,
    > which is entirely unexpected for such a small dataset.
    
    I don't find this to be this absurdly slow. On my laptop loading with a
    development checkout this takes 1223.950 ms. This is 20mio fields
    parsed/sec, rows with 69mio fields/sec inserted.  Removing the TRUNCATE
    and running the COPYs concurrently scales well to a few clients, and
    only stops because my laptop's SSD stops being able to keep up.
    
    
    That said, I do think there's a few places that could stand some
    improvement. Locally the profile shows up as:
    +   15.38%  postgres  libc-2.25.so        [.] __GI_____strtoll_l_internal
    +   11.79%  postgres  postgres            [.] heap_fill_tuple
    +    8.00%  postgres  postgres            [.] CopyFrom
    +    7.40%  postgres  postgres            [.] CopyReadLine
    +    6.79%  postgres  postgres            [.] ExecConstraints
    +    6.68%  postgres  postgres            [.] NextCopyFromRawFields
    +    6.36%  postgres  postgres            [.] heap_compute_data_size
    +    6.02%  postgres  postgres            [.] pg_atoi
    
    the strtoll is libc functionality triggered by pg_atoi(), something I've
    seen show up in numerous profiles. I think it's probably time to have
    our own optimized version of it rather than relying on libcs.
    
    That heap_fill_tuple(), which basically builds a tuple from the parsed
    datums, takes time somewhat proportional to the number of columns in the
    table seems hard to avoid, especially because this isn't something we
    want to optimize for with the price of making more common workloads with
    fewer columns slower. But there seems quite some micro-optimization
    potential.
    
    That ExecConstraints() shows up seems unsurprising, it has to walk
    through all the table's columns checking for constraints. We could
    easily optimize this so we have a separate datastructure listing
    constraints, but that'd be slower in the very common case of more
    reasonable numbers of columns.
    
    The copy implementation deserves some optimization too...
    
    > Here¹s a tarball with test schema and data:
    > http://nohuhu.org/copy_perf.tar.bz2; it¹s 338k compressed but expands to
    > ~50mb. Here¹s the result of profiling session with perf:
    > https://pastebin.com/pjv7JqxD
    
    Thanks!
    
    Greetings,
    
    Andres Freund
    
    
    
  4. Faster str to int conversion (was Table with large number of int columns, very slow COPY FROM)

    Andres Freund <andres@anarazel.de> — 2017-12-08T21:44:37Z

    Hi,
    
    
    On 2017-12-08 10:17:34 -0800, Andres Freund wrote:
    > the strtoll is libc functionality triggered by pg_atoi(), something I've
    > seen show up in numerous profiles. I think it's probably time to have
    > our own optimized version of it rather than relying on libcs.
    
    Attached is a hand-rolled version. After quickly hacking up one from
    scratch, I noticed we already kind of have one for int64 (scanint8), so
    I changed the structure of this one to be relatively similar.
    
    It's currently using the overflow logic from [1], but that's not
    fundamentally required, we could rely on fwrapv for this one too.
    
    This one improves performance of the submitted workload from 1223.950ms
    to 1020.640ms (best of three). The profile's shape changes quite
    noticeably:
    
    master:
    +   15.38%  postgres  libc-2.25.so      [.] __GI_____strtoll_l_internal
    +   11.79%  postgres  postgres          [.] heap_fill_tuple
    +    8.00%  postgres  postgres          [.] CopyFrom
    +    7.40%  postgres  postgres          [.] CopyReadLine
    +    6.79%  postgres  postgres          [.] ExecConstraints
    +    6.68%  postgres  postgres          [.] NextCopyFromRawFields
    +    6.36%  postgres  postgres          [.] heap_compute_data_size
    +    6.02%  postgres  postgres          [.] pg_atoi
    patch:
    +   13.70%  postgres  postgres          [.] heap_fill_tuple
    +   10.46%  postgres  postgres          [.] CopyFrom
    +    9.31%  postgres  postgres          [.] pg_strto32
    +    8.39%  postgres  postgres          [.] CopyReadLine
    +    7.88%  postgres  postgres          [.] ExecConstraints
    +    7.63%  postgres  postgres          [.] InputFunctionCall
    +    7.41%  postgres  postgres          [.] heap_compute_data_size
    +    7.21%  postgres  postgres          [.] pg_verify_mbstr
    +    5.49%  postgres  postgres          [.] NextCopyFromRawFields
    
    
    This probably isn't going to resolve Alex's performance concerns
    meaningfully, but seems quite worthwhile to do anyway.
    
    We probably should have int8/16/64 version coded just as use the 32bit
    version, but I decided to leave that out for now. Primarily interested
    in comments.  Wonder a bit whether it's worth providing an 'errorOk'
    mode like scanint8 does, but surveying its callers suggests we should
    rather change them to not need it...
    
    Greetings,
    
    Andres Freund
    
    [1] http://archives.postgresql.org/message-id/20171030112751.mukkriz2rur2qkxc%40alap3.anarazel.de
    
  5. Re: Faster str to int conversion (was Table with large number of int columns, very slow COPY FROM)

    Andres Freund <andres@anarazel.de> — 2018-07-07T20:01:58Z

    Hi,
    
    On 2017-12-08 13:44:37 -0800, Andres Freund wrote:
    > On 2017-12-08 10:17:34 -0800, Andres Freund wrote:
    > > the strtoll is libc functionality triggered by pg_atoi(), something I've
    > > seen show up in numerous profiles. I think it's probably time to have
    > > our own optimized version of it rather than relying on libcs.
    > 
    > Attached is a hand-rolled version. After quickly hacking up one from
    > scratch, I noticed we already kind of have one for int64 (scanint8), so
    > I changed the structure of this one to be relatively similar.
    > 
    > It's currently using the overflow logic from [1], but that's not
    > fundamentally required, we could rely on fwrapv for this one too.
    > 
    > This one improves performance of the submitted workload from 1223.950ms
    > to 1020.640ms (best of three). The profile's shape changes quite
    > noticeably:
    
    FWIW, here's a rebased version of this patch. Could probably be polished
    further. One might argue that we should do a bit more wide ranging
    changes, to convert scanint8 and pg_atoi to be also unified. But it
    might also just be worthwhile to apply without those, given the
    performance benefit.
    
    Anybody have an opinion on that?
    
    Greetings,
    
    Andres Freund
    
  6. Re: Faster str to int conversion (was Table with large number of int columns, very slow COPY FROM)

    Robert Haas <robertmhaas@gmail.com> — 2018-07-18T18:34:34Z

    On Sat, Jul 7, 2018 at 4:01 PM, Andres Freund <andres@anarazel.de> wrote:
    > FWIW, here's a rebased version of this patch. Could probably be polished
    > further. One might argue that we should do a bit more wide ranging
    > changes, to convert scanint8 and pg_atoi to be also unified. But it
    > might also just be worthwhile to apply without those, given the
    > performance benefit.
    
    Wouldn't hurt to do that one too, but might be OK to just do this
    much.  Questions:
    
    1. Why the error message changes?  If there's a good reason, it should
    be done as a separate commit, or at least well-documented in the
    commit message.
    
    2. Does the likely/unlikely stuff make a noticeable difference?
    
    3. If this is a drop-in replacement for pg_atoi, why not just recode
    pg_atoi this way -- or have it call this -- and leave the callers
    unchanged?
    
    4. Are we sure this is faster on all platforms, or could it work out
    the other way on, say, BSD?
    
    -- 
    Robert Haas
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
  7. Re: Faster str to int conversion (was Table with large number of int columns, very slow COPY FROM)

    Andres Freund <andres@anarazel.de> — 2018-07-19T20:32:12Z

    Hi,
    
    On 2018-07-18 14:34:34 -0400, Robert Haas wrote:
    > On Sat, Jul 7, 2018 at 4:01 PM, Andres Freund <andres@anarazel.de> wrote:
    > > FWIW, here's a rebased version of this patch. Could probably be polished
    > > further. One might argue that we should do a bit more wide ranging
    > > changes, to convert scanint8 and pg_atoi to be also unified. But it
    > > might also just be worthwhile to apply without those, given the
    > > performance benefit.
    > 
    > Wouldn't hurt to do that one too, but might be OK to just do this
    > much.  Questions:
    > 
    > 1. Why the error message changes?  If there's a good reason, it should
    > be done as a separate commit, or at least well-documented in the
    > commit message.
    
    Because there's a lot of "invalid input syntax for type %s: \"%s\"",
    error messages, and we shouldn't force translators to have separate
    version that inlines the first %s.  But you're right, it'd be worthwhile
    to point that out in the commit message.
    
    
    > 2. Does the likely/unlikely stuff make a noticeable difference?
    
    Yes. It's also largely a copy from existing code (scanint8), so I don't
    really want to differ here.
    
    
    > 3. If this is a drop-in replacement for pg_atoi, why not just recode
    > pg_atoi this way -- or have it call this -- and leave the callers
    > unchanged?
    
    Because pg_atoi supports a variable 'terminator'. Supporting that would
    create a bit slower code, without being particularly useful.  I think
    there's only a single in-core caller left after the patch
    (int2vectorin). There's a fair argument that that should just be
    open-coded to handle the weird space parsing, but given there's probably
    external pg_atoi() callers, I'm not sure it's worth doing so?
    
    I don't think it's a good idea to continue to have pg_atoi as a wrapper
    - it takes a size argument, which makes efficient code hard.
    
    
    > 4. Are we sure this is faster on all platforms, or could it work out
    > the other way on, say, BSD?
    
    I'd be *VERY* surprised if any would be faster. It's not easy to write a
    faster implmentation, than what I've proposed, and especially not so if
    you use strtol() as the API (variable bases, a bit of locale support).
    
    Greetings,
    
    Andres Freund
    
    
    
  8. Re: Faster str to int conversion (was Table with large number of int columns, very slow COPY FROM)

    Robert Haas <robertmhaas@gmail.com> — 2018-07-20T12:27:34Z

    On Thu, Jul 19, 2018 at 4:32 PM, Andres Freund <andres@anarazel.de> wrote:
    >> 1. Why the error message changes?  If there's a good reason, it should
    >> be done as a separate commit, or at least well-documented in the
    >> commit message.
    >
    > Because there's a lot of "invalid input syntax for type %s: \"%s\"",
    > error messages, and we shouldn't force translators to have separate
    > version that inlines the first %s.  But you're right, it'd be worthwhile
    > to point that out in the commit message.
    
    It just seems weird that they're bundled together in one commit like this.
    
    >> 2. Does the likely/unlikely stuff make a noticeable difference?
    >
    > Yes. It's also largely a copy from existing code (scanint8), so I don't
    > really want to differ here.
    
    OK.
    
    >> 3. If this is a drop-in replacement for pg_atoi, why not just recode
    >> pg_atoi this way -- or have it call this -- and leave the callers
    >> unchanged?
    >
    > Because pg_atoi supports a variable 'terminator'.
    
    OK.
    
    >> 4. Are we sure this is faster on all platforms, or could it work out
    >> the other way on, say, BSD?
    >
    > I'd be *VERY* surprised if any would be faster. It's not easy to write a
    > faster implmentation, than what I've proposed, and especially not so if
    > you use strtol() as the API (variable bases, a bit of locale support).
    
    OK.
    
    Nothing else from me...
    
    -- 
    Robert Haas
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
  9. Re: Faster str to int conversion (was Table with large number of int columns, very slow COPY FROM)

    Andres Freund <andres@anarazel.de> — 2018-07-20T16:45:10Z

    Hi,
    
    On 2018-07-20 08:27:34 -0400, Robert Haas wrote:
    > On Thu, Jul 19, 2018 at 4:32 PM, Andres Freund <andres@anarazel.de> wrote:
    > >> 1. Why the error message changes?  If there's a good reason, it should
    > >> be done as a separate commit, or at least well-documented in the
    > >> commit message.
    > >
    > > Because there's a lot of "invalid input syntax for type %s: \"%s\"",
    > > error messages, and we shouldn't force translators to have separate
    > > version that inlines the first %s.  But you're right, it'd be worthwhile
    > > to point that out in the commit message.
    > 
    > It just seems weird that they're bundled together in one commit like this.
    
    I'll push it separately.
    
    > Nothing else from me...
    
    Thanks for looking!
    
    Greetings,
    
    Andres Freund