]> granicus.if.org Git - postgresql/commitdiff
Adjust pgstat_initstats() to avoid repeated searches of the TabStat arrays
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 21 Apr 2007 04:10:53 +0000 (04:10 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 21 Apr 2007 04:10:53 +0000 (04:10 +0000)
when a relation is opened multiple times in the same transaction.  This is
particularly useful for system catalogs, which we may heap_open or index_open
many times in a transaction, and it doesn't really cost anything extra even
if the rel is touched but once.  Motivated by study of an example from Greg
Stark, in which pgstat_initstats() accounted for an unreasonably large
fraction of the runtime.

src/backend/postmaster/pgstat.c

index e1699cd0bdf75cf0c908554190889f3c34bb51d7..be9b236d5455c201b6c75592c10f4bde6713296c 100644 (file)
@@ -13,7 +13,7 @@
  *
  *     Copyright (c) 2001-2007, PostgreSQL Global Development Group
  *
- *     $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.152 2007/03/30 18:34:55 mha Exp $
+ *     $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.153 2007/04/21 04:10:53 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -114,6 +114,14 @@ static bool pgStatRunningInCollector = false;
  * Place where backends store per-table info to be sent to the collector.
  * We store shared relations separately from non-shared ones, to be able to
  * send them in separate messages.
+ *
+ * NOTE: once allocated, a PgStat_MsgTabstat struct belonging to a
+ * TabStatArray is never moved or deleted for the life of the backend.
+ * Also, we zero out the t_id fields of the contained PgStat_TableEntry
+ * structs whenever they are not actively in use.  This allows PgStat_Info
+ * pointers to be treated as long-lived data, avoiding repeated searches in
+ * pgstat_initstats() when a relation is repeatedly heap_open'd or
+ * index_open'd during a transaction.
  */
 typedef struct TabStatArray
 {
@@ -169,6 +177,7 @@ static void pgstat_write_statsfile(void);
 static HTAB *pgstat_read_statsfile(Oid onlydb);
 static void backend_read_statsfile(void);
 static void pgstat_read_current_status(void);
+static void pgstat_report_one_tabstat(TabStatArray *tsarr, Oid dbid);
 static HTAB *pgstat_collect_oids(Oid catalogid);
 
 static void pgstat_setup_memcxt(void);
@@ -606,25 +615,22 @@ void allow_immediate_pgstat_restart(void)
 void
 pgstat_report_tabstat(void)
 {
-       int                     i;
-
-       if (pgStatSock < 0 ||
-               (!pgstat_collect_tuplelevel &&
-                !pgstat_collect_blocklevel))
-       {
-               /* Not reporting stats, so just flush whatever we have */
-               RegularTabStat.tsa_used = 0;
-               SharedTabStat.tsa_used = 0;
-               return;
-       }
-
        /*
         * For each message buffer used during the last query set the header
-        * fields and send it out.
+        * fields and send it out; then mark the entries unused.
         */
-       for (i = 0; i < RegularTabStat.tsa_used; i++)
+       pgstat_report_one_tabstat(&RegularTabStat, MyDatabaseId);
+       pgstat_report_one_tabstat(&SharedTabStat, InvalidOid);
+}
+
+static void
+pgstat_report_one_tabstat(TabStatArray *tsarr, Oid dbid)
+{
+       int                     i;
+
+       for (i = 0; i < tsarr->tsa_used; i++)
        {
-               PgStat_MsgTabstat *tsmsg = RegularTabStat.tsa_messages[i];
+               PgStat_MsgTabstat *tsmsg = tsarr->tsa_messages[i];
                int                     n;
                int                     len;
 
@@ -637,32 +643,24 @@ pgstat_report_tabstat(void)
                pgStatXactCommit = 0;
                pgStatXactRollback = 0;
 
-               pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT);
-               tsmsg->m_databaseid = MyDatabaseId;
-               pgstat_send(tsmsg, len);
-       }
-       RegularTabStat.tsa_used = 0;
-
-       /* Ditto, for shared relations */
-       for (i = 0; i < SharedTabStat.tsa_used; i++)
-       {
-               PgStat_MsgTabstat *tsmsg = SharedTabStat.tsa_messages[i];
-               int                     n;
-               int                     len;
-
-               n = tsmsg->m_nentries;
-               len = offsetof(PgStat_MsgTabstat, m_entry[0]) +
-                       n * sizeof(PgStat_TableEntry);
-
-               /* We don't report transaction commit/abort here */
-               tsmsg->m_xact_commit = 0;
-               tsmsg->m_xact_rollback = 0;
+               /*
+                * It's unlikely we'd get here with no socket, but maybe not
+                * impossible
+                */
+               if (pgStatSock >= 0)
+               {
+                       pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT);
+                       tsmsg->m_databaseid = dbid;
+                       pgstat_send(tsmsg, len);
+               }
 
-               pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT);
-               tsmsg->m_databaseid = InvalidOid;
-               pgstat_send(tsmsg, len);
+               /*
+                * Zero out the entries, to mark them unused and prepare them
+                * for next use.
+                */
+               MemSet(tsmsg, 0, len);
        }
-       SharedTabStat.tsa_used = 0;
+       tsarr->tsa_used = 0;
 }
 
 
@@ -1013,7 +1011,7 @@ more_tabstat_space(TabStatArray *tsarr)
 
        newAlloc = tsarr->tsa_alloc + TABSTAT_QUANTUM;
 
-       /* Create (another) quantum of message buffers */
+       /* Create (another) quantum of message buffers, and zero them */
        newMessages = (PgStat_MsgTabstat *)
                MemoryContextAllocZero(TopMemoryContext,
                                                           sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM);
@@ -1043,6 +1041,17 @@ more_tabstat_space(TabStatArray *tsarr)
  *     of Relation or Scan structures. The data placed into these
  *     structures from here tell where later to count for buffer reads,
  *     scans and tuples fetched.
+ *
+ *     NOTE: PgStat_Info pointers in scan structures are really redundant
+ *     with those in relcache entries.  The passed stats pointer might point
+ *     either to the Relation struct's own pgstat_info field, or to one in
+ *     a scan structure; we'll set the Relation pg_statinfo and copy it to
+ *     the scan struct.
+ *
+ *     We assume that a relcache entry's pgstat_info field is zeroed by
+ *     relcache.c when the relcache entry is made; thereafter it is long-lived
+ *     data.  We can avoid repeated searches of the TabStat arrays when the
+ *     same relation is touched repeatedly within a transaction.
  * ----------
  */
 void
@@ -1055,21 +1064,31 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
        int                     mb;
        int                     i;
 
-       /*
-        * Initialize data not to count at all.
-        */
-       stats->tabentry = NULL;
-
        if (pgStatSock < 0 ||
                !(pgstat_collect_tuplelevel ||
                  pgstat_collect_blocklevel))
+       {
+               /* We're not counting at all. */
+               stats->tabentry = NULL;
                return;
+       }
 
-       tsarr = rel->rd_rel->relisshared ? &SharedTabStat : &RegularTabStat;
+       /*
+        * If we already set up this relation in the current transaction,
+        * just copy the pointer.
+        */
+       if (rel->pgstat_info.tabentry != NULL &&
+               ((PgStat_TableEntry *) rel->pgstat_info.tabentry)->t_id == rel_id)
+       {
+               stats->tabentry = rel->pgstat_info.tabentry;
+               return;
+       }
 
        /*
         * Search the already-used message slots for this relation.
         */
+       tsarr = rel->rd_rel->relisshared ? &SharedTabStat : &RegularTabStat;
+
        for (mb = 0; mb < tsarr->tsa_used; mb++)
        {
                tsmsg = tsarr->tsa_messages[mb];
@@ -1078,7 +1097,8 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
                {
                        if (tsmsg->m_entry[i].t_id == rel_id)
                        {
-                               stats->tabentry = (void *) &(tsmsg->m_entry[i]);
+                               rel->pgstat_info.tabentry = (void *) &(tsmsg->m_entry[i]);
+                               stats->tabentry = rel->pgstat_info.tabentry;
                                return;
                        }
                }
@@ -1088,13 +1108,14 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
 
                /*
                 * Not found, but found a message buffer with an empty slot instead.
-                * Fine, let's use this one.
+                * Fine, let's use this one.  We assume the entry was already zeroed,
+                * either at creation or after last use.
                 */
                i = tsmsg->m_nentries++;
                useent = &tsmsg->m_entry[i];
-               MemSet(useent, 0, sizeof(PgStat_TableEntry));
                useent->t_id = rel_id;
-               stats->tabentry = (void *) useent;
+               rel->pgstat_info.tabentry = (void *) useent;
+               stats->tabentry = rel->pgstat_info.tabentry;
                return;
        }
 
@@ -1111,9 +1132,9 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
        tsmsg = tsarr->tsa_messages[mb];
        tsmsg->m_nentries = 1;
        useent = &tsmsg->m_entry[0];
-       MemSet(useent, 0, sizeof(PgStat_TableEntry));
        useent->t_id = rel_id;
-       stats->tabentry = (void *) useent;
+       rel->pgstat_info.tabentry = (void *) useent;
+       stats->tabentry = rel->pgstat_info.tabentry;
 }