Thread: Some performance numbers, with thoughts

Some performance numbers, with thoughts

From
Brian Hurt
Date:
For long involved reasons I'm hanging out late at work today, and rather
than doing real, productive work, I thought I'd run some benchmarks
against our development PostgreSQL database server.  My conclusions are
at the end.

The purpose of the benchmarking was to find out how fast Postgres was,
or to compare Postgres to other databases, but to instead answer the
question: when does it become worthwhile to switch over to using COPYs
instead of INSERTS, and by how much?  This benchmark should in no way be
used to gauge absolute performance of PostgreSQL.

The machine in question: a new HP-145 rack mount server, with a
single-socket dual-core 1.8GHz Opteron 275, 1M of cache per core, with
4G of memory, running Redhat Linux (forget which version).  Database was
on the local single SATA hard disk- no raid.  From the numbers, I'm
assuming the disk honors fsync.  Some tuning of the database was done,
specifically shared_buffers was upped to 2500 and temp_buffers to 1500
(mental note to self: must increase these signifigantly more.  Forgot
they were so low).  fsync is definately on.  Test program was written in
Ocaml, compiled to native code, using the Ocaml Postgresql connection
library (Ocaml bindings of the libpgsql library).  The test was single
threaded- only one insert going on at a time, run over the local gigabit
ethernet network from a remote machine.

The table design was very simple:
CREATE TABLE copytest (
    id SERIAL PRIMARY KEY NOT NULL,
    name VARCHAR(64),
    thread INT,
    block INT,
    num INT);

The id column was not specified either in the inserts or in the copies,
instead it just came from the sequence.  Other than the id, there are no
indexes on the table.  Numbers are approximate.

Results:

Inserts, 1 per transaction*                    83 inserts/second
Inserts, 5 per transaction                    419 inserts/second
Inserts, 10 per transaction                  843 inserts/second
Inserts, 50 per transaction                  ~3,100 inserts/second
Inserts, 100 per transaction                ~4,200 inserts/second
Inserts, 1,000 per transaction             ~5,400 inserts/second
Copy, 5 element blocks                     ~405 inserts/second
Copy, 10 element blocks                   ~700 inserts/second
Copy, 50 element blocks                   ~3,400 inserts/second
Copy, 100 element blocks                 ~6,000 inserts/second
Copy, 1,000 element blocks              ~20,000 inserts/second
Copy, 10,000 element blocks            ~27,500 inserts/second
Copy, 100,000 element blocks          ~27,600 inserts/second

* The singleton inserts were not done in an explicit begin/end block,
but were instead "unadorned" inserts.

Some conclusions:

1) Transaction time is a huge hit on the small block sizes.  Going from
1 insert per transaction to 10 inserts per transaction gives a 10x speed
up.  Once the block size gets large enough (10's to 100's of elements
per block) the cost of a transaction becomes less of a problem.

2) Both insert methods hit fairly hard walls of diminishing returns were
larger block sizes gave little performance advantage, tending to no
performance advantage.

3) For small enough block sizes, inserts are actually faster than
copies- but not by much.  There is a broad plateau, spanning at least
the 5 through 100 elements per block (more than an order of magnitude),
where the performance of the two are roughly identical.  For the general
case, I'd be inclined to switch to copies sooner (at 5 or so elements
per block) rather than later.

4) At the high end, copies vastly outperformed inserts.  At 1,000
elements per block, the copy was almost 4x faster than inserts.  This
widened to ~5x before copy started topping out.

5) The performance of Postgres, at least on inserts, depends critically
on how you program it. One the same hardware, performance for me varied
over a factor of over 300-fold, 2.5 orders of magnitude.  Programs which
are unaware of transactions and are designed to be highly portable are
likely to hit the abysmal side of performance, where the transaction
overhead kills performance.  I'm not sure there is a fix for this (let
alone an easy fix)- simply dropping transactions is obviously not it.
Programs that are transaction aware and willing to use
PostgreSQL-specific features can get surprisingly excellent
performance.  Simply being transaction-aware and doing multiple inserts
per transaction greatly increases performance, giving an easy order of
magnitude increase (wrapping 10 inserts in a transaction gives a 10x
performance boost).

Brian



Re: Some performance numbers, with thoughts

From
Tom Lane
Date:
Brian Hurt <bhurt@janestcapital.com> writes:
> For long involved reasons I'm hanging out late at work today, and rather
> than doing real, productive work, I thought I'd run some benchmarks
> against our development PostgreSQL database server.  My conclusions are
> at the end.

Ummm ... you forgot to mention Postgres version?  Also, which client and
server encodings did you use (that starts to get to be a noticeable
issue for high COPY rates)?

> 1) Transaction time is a huge hit on the small block sizes.

Right.  For small transactions with a drive honoring fsync, you should
expect to get a max of about one commit per platter revolution.  Your
numbers work out to a shade under 5000 commits/minute, from which I
speculate a 7200 RPM drive ... do you know what it really is?

            regards, tom lane

Re: Some performance numbers, with thoughts

From
"Luke Lonergan"
Date:
Brian,

Any idea what your bottleneck is?  You can find out at a crude level by
attaching an strace to the running backend, assuming it¹s running long
enough to grab it, then look at what the system call breakdown is.
Basically, run one of your long insert streams, do a ³top² to find which
process id the backend is using (the <pid>), then run this:

  strace -p <pid> -c

And CTRL-C after a few seconds to see a breakdown of system calls.

I think what you'll see is that for the small number of inserts per TXN,
you'll be bottlenecked on fsync() calls, or fdatasync() if you defaulted it.
Things might speed up a whole lot there depending on your choice of one or
the other.

- Luke


On 6/19/06 5:09 PM, "Brian Hurt" <bhurt@janestcapital.com> wrote:

>
>
> For long involved reasons I'm hanging out late at work today, and rather
> than doing real, productive work, I thought I'd run some benchmarks
> against our development PostgreSQL database server.  My conclusions are
> at the end.
>
> The purpose of the benchmarking was to find out how fast Postgres was,
> or to compare Postgres to other databases, but to instead answer the
> question: when does it become worthwhile to switch over to using COPYs
> instead of INSERTS, and by how much?  This benchmark should in no way be
> used to gauge absolute performance of PostgreSQL.
>
> The machine in question: a new HP-145 rack mount server, with a
> single-socket dual-core 1.8GHz Opteron 275, 1M of cache per core, with
> 4G of memory, running Redhat Linux (forget which version).  Database was
> on the local single SATA hard disk- no raid.  From the numbers, I'm
> assuming the disk honors fsync.  Some tuning of the database was done,
> specifically shared_buffers was upped to 2500 and temp_buffers to 1500
> (mental note to self: must increase these signifigantly more.  Forgot
> they were so low).  fsync is definately on.  Test program was written in
> Ocaml, compiled to native code, using the Ocaml Postgresql connection
> library (Ocaml bindings of the libpgsql library).  The test was single
> threaded- only one insert going on at a time, run over the local gigabit
> ethernet network from a remote machine.
>
> The table design was very simple:
> CREATE TABLE copytest (
>     id SERIAL PRIMARY KEY NOT NULL,
>     name VARCHAR(64),
>     thread INT,
>     block INT,
>     num INT);
>
> The id column was not specified either in the inserts or in the copies,
> instead it just came from the sequence.  Other than the id, there are no
> indexes on the table.  Numbers are approximate.
>
> Results:
>
> Inserts, 1 per transaction*                    83 inserts/second
> Inserts, 5 per transaction                    419 inserts/second
> Inserts, 10 per transaction                  843 inserts/second
> Inserts, 50 per transaction                  ~3,100 inserts/second
> Inserts, 100 per transaction                ~4,200 inserts/second
> Inserts, 1,000 per transaction             ~5,400 inserts/second
> Copy, 5 element blocks                     ~405 inserts/second
> Copy, 10 element blocks                   ~700 inserts/second
> Copy, 50 element blocks                   ~3,400 inserts/second
> Copy, 100 element blocks                 ~6,000 inserts/second
> Copy, 1,000 element blocks              ~20,000 inserts/second
> Copy, 10,000 element blocks            ~27,500 inserts/second
> Copy, 100,000 element blocks          ~27,600 inserts/second
>
> * The singleton inserts were not done in an explicit begin/end block,
> but were instead "unadorned" inserts.
>
> Some conclusions:
>
> 1) Transaction time is a huge hit on the small block sizes.  Going from
> 1 insert per transaction to 10 inserts per transaction gives a 10x speed
> up.  Once the block size gets large enough (10's to 100's of elements
> per block) the cost of a transaction becomes less of a problem.
>
> 2) Both insert methods hit fairly hard walls of diminishing returns were
> larger block sizes gave little performance advantage, tending to no
> performance advantage.
>
> 3) For small enough block sizes, inserts are actually faster than
> copies- but not by much.  There is a broad plateau, spanning at least
> the 5 through 100 elements per block (more than an order of magnitude),
> where the performance of the two are roughly identical.  For the general
> case, I'd be inclined to switch to copies sooner (at 5 or so elements
> per block) rather than later.
>
> 4) At the high end, copies vastly outperformed inserts.  At 1,000
> elements per block, the copy was almost 4x faster than inserts.  This
> widened to ~5x before copy started topping out.
>
> 5) The performance of Postgres, at least on inserts, depends critically
> on how you program it. One the same hardware, performance for me varied
> over a factor of over 300-fold, 2.5 orders of magnitude.  Programs which
> are unaware of transactions and are designed to be highly portable are
> likely to hit the abysmal side of performance, where the transaction
> overhead kills performance.  I'm not sure there is a fix for this (let
> alone an easy fix)- simply dropping transactions is obviously not it.
> Programs that are transaction aware and willing to use
> PostgreSQL-specific features can get surprisingly excellent
> performance.  Simply being transaction-aware and doing multiple inserts
> per transaction greatly increases performance, giving an easy order of
> magnitude increase (wrapping 10 inserts in a transaction gives a 10x
> performance boost).
>
> Brian
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend
>
>




Re: Some performance numbers, with thoughts

From
Simon Riggs
Date:
On Mon, 2006-06-19 at 20:09 -0400, Brian Hurt wrote:

> 5) The performance of Postgres, at least on inserts, depends critically
> on how you program it. One the same hardware, performance for me varied
> over a factor of over 300-fold, 2.5 orders of magnitude.  Programs which
> are unaware of transactions and are designed to be highly portable are
> likely to hit the abysmal side of performance, where the transaction
> overhead kills performance.

I'm quite interested in this comment. Transactions have always been part
of the SQL standard, so being unaware of them when using SQL is strange
to me. Can you talk more about what your expectations of what
performance "should have been" - I don't want to flame you, just to
understand that viewpoint.

What are you implicitly comparing against? With which options enabled?

How are you submitting these SQL statements? Through what API?

> I'm not sure there is a fix for this (let
> alone an easy fix)- simply dropping transactions is obviously not it.

I'd like to see what other "fixes" we might think of.

Perhaps we might consider a session-level mode that groups together
atomic INSERTs into the same table into a single larger transaction.
That might be something we can do at the client level, for example.

> Programs that are transaction aware and willing to use
> PostgreSQL-specific features can get surprisingly excellent
> performance.  Simply being transaction-aware and doing multiple inserts
> per transaction greatly increases performance, giving an easy order of
> magnitude increase (wrapping 10 inserts in a transaction gives a 10x
> performance boost).

This is exactly the same as most other transactional-RDBMS.

--
  Simon Riggs
  EnterpriseDB          http://www.enterprisedb.com


Re: Some performance numbers, with thoughts

From
Michael Stone
Date:
On Mon, Jun 26, 2006 at 08:33:34PM +0100, Simon Riggs wrote:
>of the SQL standard, so being unaware of them when using SQL is strange
>to me.

Welcome to the world of programs designed for mysql. You'll almost never
see them batch inserts, take advantage of referential integrity, etc.
You end up with lots of selects & inserts in loops that expect
autocommit-like behavior because it doesn't matter in that world.

Mike Stone

Re: Some performance numbers, with thoughts

From
Simon Riggs
Date:
On Mon, 2006-06-26 at 17:20 -0400, Michael Stone wrote:
> On Mon, Jun 26, 2006 at 08:33:34PM +0100, Simon Riggs wrote:
> >of the SQL standard, so being unaware of them when using SQL is strange
> >to me.
>
> Welcome to the world of programs designed for mysql. You'll almost never
> see them batch inserts, take advantage of referential integrity, etc.
> You end up with lots of selects & inserts in loops that expect
> autocommit-like behavior because it doesn't matter in that world.

Yes, I suspected that was the case. I was interested in understanding
why anybody thought it was acceptable, and in what conditions that might
be the case. Brian's open approach has helped explain things for me.

--
  Simon Riggs
  EnterpriseDB          http://www.enterprisedb.com


Re: Some performance numbers, with thoughts

From
Ron Mayer
Date:
Combining the "insert" statements in a big concatenated string
joined by semicolons - rather than sending each individually
can drastically speed up your inserts; making them much closer
to the speed of copy.

For example, instead of sending them separately, it's much faster
to send a single string like this
  "insert into tbl (c1,c2) values (v1,v2);insert into tbl (c1,c2) values (v3,v4);..."
presumably due to the round-trip packets sending each insert takes.

Brian Hurt wrote:
>
> Inserts, 1,000 per transaction             ~5,400 inserts/second
> Copy, 1,000 element blocks              ~20,000 inserts/second
>

When I last measured it it was about a factor of 4 speedup
(3 seconds vs 0.7 seconds) by concatenating the inserts with
sample code shown her [1].

If the same ratio holds for your test case, these concatenated
inserts would be almost the exact same speed as a copy.

   Ron M

[1] http://archives.postgresql.org/pgsql-performance/2005-09/msg00327.php