Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1ivlXm-0003RM-2J for pgsql-hackers@arkaria.postgresql.org; Sun, 26 Jan 2020 17:17:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1ivlXj-0001Sh-JW for pgsql-hackers@arkaria.postgresql.org; Sun, 26 Jan 2020 17:17:51 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1ivlXj-0001Sa-5U for pgsql-hackers@lists.postgresql.org; Sun, 26 Jan 2020 17:17:51 +0000 Received: from mail-yw1-xc44.google.com ([2607:f8b0:4864:20::c44]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1ivlXb-0004Rn-DG for pgsql-hackers@lists.postgresql.org; Sun, 26 Jan 2020 17:17:50 +0000 Received: by mail-yw1-xc44.google.com with SMTP id t141so3622104ywc.11 for ; Sun, 26 Jan 2020 09:17:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telsasoft-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=8F+BlRKGf+KlaFaVMS2STN8anMS5DU66a4y5sPPullI=; b=af8f7mVuzqpZi1TdlJ0Fj53yZ97A+p3RJePPVDD9uivQDII9a5gIO+PnU+xoQOfw9Q khDVqn8NkTbrFFpZFcnEKi3EPxm4ExHFJHJdj806Nkd1GFnjHLZa9TnflUO+MTghQrtx gWqlbBoQqHLkEpaDOgpWimb7aPNIwQg3JGiMbDTvfUj7ODY8yjpWgjmFJQobgaKZjh3w OQqd/bPS6+uHLytToz3sjNtZce+j6KHpPinBFlVDNGoiBa1yMKYtXdIkTNFUtGEZpE7v 1bV4ajoAgMezjGw43cAiKG6hBgT09CEg6slH2JKBXlqzW80+3HM9PfZpesD9jWCkt7g3 dAIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=8F+BlRKGf+KlaFaVMS2STN8anMS5DU66a4y5sPPullI=; b=O0B9CX0WGm8BeUWGDycuFWXEy8AcYxUNel9c+WCE3uZog/Fy4WUlauAFm9fM9H74/n p4bnZGFqu6FcIAnv58GcAWkIbixQHyX/NCRpEfZqAdFjzh1kg71EFmqJCYD5iTMXUse6 H1b0ZMJ7k2VbilXxhM4J7aSBFmDOC7UyrA2T+LEdf/HbBaX2/tlL60BwfGtPDhLRw7J6 /K5UhocE+ZGRTRTdc0xo2Cl1k4YVRZhrlsOxIr4H024Jv9EXBL2puT3rLHOh0Ny+J65l qc2AIGeozkI4yaE1b2QD9pzkyKshLJDe3dbz899HMX1qJY0t3WPW298T0d39xDKBx5Zi jdLA== X-Gm-Message-State: APjAAAVSsfrSHQy1Q0gSrnCX9eX/KADsSjBThwPNz5EJsXT8aJW5AUeG FnmJj3L4cwymH21uXCX2epsIbA== X-Google-Smtp-Source: APXvYqxGjF/5IKJ0FG4QM0pYUlNcxAsaP+p4+79nHhiv/Pj4OUDO7ulobngqRekMedc8zv34YIXKQw== X-Received: by 2002:a81:30a:: with SMTP id 10mr9491606ywd.162.1580059060444; Sun, 26 Jan 2020 09:17:40 -0800 (PST) Received: from pryzbyj (charmander.telsasoft.com. [50.244.222.1]) by smtp.gmail.com with ESMTPSA id s68sm5257901ywg.69.2020.01.26.09.17.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 26 Jan 2020 09:17:39 -0800 (PST) Received: by pryzbyj (Postfix, from userid 1000) id D84C4800831; Sun, 26 Jan 2020 11:17:37 -0600 (CST) Date: Sun, 26 Jan 2020 11:17:37 -0600 From: Justin Pryzby To: pgsql-hackers@lists.postgresql.org Cc: Jeff Janes Subject: Re: explain HashAggregate to report bucket and memory stats Message-ID: <20200126171737.GR13621@telsasoft.com> References: <20200103161925.GM12066@telsasoft.com> <20200126141425.GQ13621@telsasoft.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="JfVplkuTfB13Rsg5" Content-Disposition: inline In-Reply-To: <20200126141425.GQ13621@telsasoft.com> User-Agent: Mutt/1.5.24 (2015-08-30) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --JfVplkuTfB13Rsg5 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Sun, Jan 26, 2020 at 08:14:25AM -0600, Justin Pryzby wrote: > On Fri, Jan 03, 2020 at 10:19:25AM -0600, Justin Pryzby wrote: > > On Sun, Feb 17, 2019 at 11:29:56AM -0500, Jeff Janes wrote: > > https://www.postgresql.org/message-id/CAMkU%3D1zBJNVo2DGYBgLJqpu8fyjCE_ys%2Bmsr6pOEoiwA7y5jrA%40mail.gmail.com > > > What would I find very useful is [...] if the HashAggregate node under > > > "explain analyze" would report memory and bucket stats; and if the Aggregate > > > node would report...anything. > > > > Find attached my WIP attempt to implement this. > > > > Jeff: can you suggest what details Aggregate should show ? > > Rebased on top of 10013684970453a0ddc86050bba813c611114321 > And added https://commitfest.postgresql.org/27/2428/ Attached for real. --JfVplkuTfB13Rsg5 Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-refactor-show_hinstrument-and-avoid-showing-memor.patch" From 42634353f44ab44a06ffe4ae36a0dcbb848408ca Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Wed, 1 Jan 2020 13:09:33 -0600 Subject: [PATCH v1 1/2] refactor: show_hinstrument and avoid showing memory use if not verbose.. This changes explain analyze at least for Hash(join), but doesn't affect regression tests, since all the HashJoin tests seem to run explain without analyze, so nbatch=0, and no stats are shown. But for future patch to show stats for HashAgg (for which nbatch=1, always), we want to show buckets in explain analyze, but don't want to show memory, since it's machine-specific. --- src/backend/commands/explain.c | 79 +++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index f523adb..11b5857 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -103,6 +103,7 @@ static void show_sortorder_options(StringInfo buf, Node *sortexpr, static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es); static void show_sort_info(SortState *sortstate, ExplainState *es); +static void show_hinstrument(ExplainState *es, HashInstrumentation *h); static void show_hash_info(HashState *hashstate, ExplainState *es); static void show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es); @@ -2713,43 +2714,67 @@ show_hash_info(HashState *hashstate, ExplainState *es) } } - if (hinstrument.nbatch > 0) + show_hinstrument(es, &hinstrument); +} + +/* + * Show hash bucket stats and (optionally) memory. + */ +static void +show_hinstrument(ExplainState *es, HashInstrumentation *h) +{ + long spacePeakKb = (h->space_peak + 1023) / 1024; + + // Currently, this isn't shown for explain of hash(join) since nbatch=0 without analyze + // But, it's shown for hashAgg since nbatch=1, always. + // Need to 1) avoid showing memory use if !analyze; and, 2) avoid memory use if not verbose + + if (h->nbatch <= 0) + return; + /* This avoids showing anything if it's explain without analyze; should we just check that, instead ? */ + if (!es->analyze) + return; + + if (es->format != EXPLAIN_FORMAT_TEXT) + { + ExplainPropertyInteger("Hash Buckets", NULL, + h->nbuckets, es); + ExplainPropertyInteger("Original Hash Buckets", NULL, + h->nbuckets_original, es); + ExplainPropertyInteger("Hash Batches", NULL, + h->nbatch, es); + ExplainPropertyInteger("Original Hash Batches", NULL, + h->nbatch_original, es); + ExplainPropertyInteger("Peak Memory Usage", "kB", + spacePeakKb, es); + } + else { - long spacePeakKb = (hinstrument.space_peak + 1023) / 1024; - if (es->format != EXPLAIN_FORMAT_TEXT) - { - ExplainPropertyInteger("Hash Buckets", NULL, - hinstrument.nbuckets, es); - ExplainPropertyInteger("Original Hash Buckets", NULL, - hinstrument.nbuckets_original, es); - ExplainPropertyInteger("Hash Batches", NULL, - hinstrument.nbatch, es); - ExplainPropertyInteger("Original Hash Batches", NULL, - hinstrument.nbatch_original, es); - ExplainPropertyInteger("Peak Memory Usage", "kB", - spacePeakKb, es); - } - else if (hinstrument.nbatch_original != hinstrument.nbatch || - hinstrument.nbuckets_original != hinstrument.nbuckets) - { + if (h->nbatch_original != h->nbatch || + h->nbuckets_original != h->nbuckets) { ExplainIndentText(es); appendStringInfo(es->str, - "Buckets: %d (originally %d) Batches: %d (originally %d) Memory Usage: %ldkB\n", - hinstrument.nbuckets, - hinstrument.nbuckets_original, - hinstrument.nbatch, - hinstrument.nbatch_original, - spacePeakKb); + "Buckets: %d (originally %d) Batches: %d (originally %d)", + h->nbuckets, + h->nbuckets_original, + h->nbatch, + h->nbatch_original); } else { ExplainIndentText(es); appendStringInfo(es->str, - "Buckets: %d Batches: %d Memory Usage: %ldkB\n", - hinstrument.nbuckets, hinstrument.nbatch, - spacePeakKb); + "Buckets: %d Batches: %d", + h->nbuckets, + h->nbatch); } + + if (es->verbose && es->analyze) + appendStringInfo(es->str, + " Memory Usage: %ldkB", + spacePeakKb); + appendStringInfoChar(es->str, '\n'); } } -- 2.7.4 --JfVplkuTfB13Rsg5 Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0002-explain-analyze-to-show-stats-from-hash-aggregate.patch" From 4b61e5ae514f5e5bc430bcf1132597ba4ecd202b Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Tue, 31 Dec 2019 18:49:41 -0600 Subject: [PATCH v1 2/2] explain analyze to show stats from (hash) aggregate.. ..as suggested by Jeff Janes --- src/backend/commands/explain.c | 41 +++++++++++++++++++++++++++++-------- src/backend/executor/execGrouping.c | 10 +++++++++ src/include/nodes/execnodes.h | 27 ++++++++++++------------ 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 11b5857..9493e7d 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -18,6 +18,7 @@ #include "commands/createas.h" #include "commands/defrem.h" #include "commands/prepare.h" +#include "executor/nodeAgg.h" #include "executor/nodeHash.h" #include "foreign/fdwapi.h" #include "jit/jit.h" @@ -84,6 +85,7 @@ static void show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es); static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors, ExplainState *es); +static void show_agg_info(AggState *astate, ExplainState *es); static void show_agg_keys(AggState *astate, List *ancestors, ExplainState *es); static void show_grouping_sets(PlanState *planstate, Agg *agg, @@ -103,7 +105,7 @@ static void show_sortorder_options(StringInfo buf, Node *sortexpr, static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es); static void show_sort_info(SortState *sortstate, ExplainState *es); -static void show_hinstrument(ExplainState *es, HashInstrumentation *h); +static void show_hinstrument(ExplainState *es, HashInstrumentation *h, bool showbatch); static void show_hash_info(HashState *hashstate, ExplainState *es); static void show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es); @@ -1889,6 +1891,8 @@ ExplainNode(PlanState *planstate, List *ancestors, if (plan->qual) show_instrumentation_count("Rows Removed by Filter", 1, planstate, es); + show_agg_info(castNode(AggState, planstate), es); + break; case T_Group: show_group_keys(castNode(GroupState, planstate), ancestors, es); @@ -2072,6 +2076,21 @@ ExplainNode(PlanState *planstate, List *ancestors, } /* + * Show instrumentation info for an Agg node. + */ +static void +show_agg_info(AggState *astate, ExplainState *es) +{ + // perhash->aggnode->numGroups; memctx; AggState-> + + for (int i=0; inum_hashes; ++i) { + HashInstrumentation *hinstrument = &astate->perhash->hashtable->hinstrument; +// fprintf(stderr, "memallocated %lu\n", astate->hashcontext->ecxt_per_query_memory->mem_allocated); + show_hinstrument(es, hinstrument, false); + } +} + +/* * Show the targetlist of a plan node */ static void @@ -2714,14 +2733,14 @@ show_hash_info(HashState *hashstate, ExplainState *es) } } - show_hinstrument(es, &hinstrument); + show_hinstrument(es, &hinstrument, true); } /* * Show hash bucket stats and (optionally) memory. */ static void -show_hinstrument(ExplainState *es, HashInstrumentation *h) +show_hinstrument(ExplainState *es, HashInstrumentation *h, bool showbatch) { long spacePeakKb = (h->space_peak + 1023) / 1024; @@ -2755,9 +2774,12 @@ show_hinstrument(ExplainState *es, HashInstrumentation *h) h->nbuckets_original != h->nbuckets) { ExplainIndentText(es); appendStringInfo(es->str, - "Buckets: %d (originally %d) Batches: %d (originally %d)", + "Buckets: %d (originally %d)", h->nbuckets, - h->nbuckets_original, + h->nbuckets_original); + if (showbatch) + appendStringInfo(es->str, + " Batches: %d (originally %d)", h->nbatch, h->nbatch_original); } @@ -2765,9 +2787,12 @@ show_hinstrument(ExplainState *es, HashInstrumentation *h) { ExplainIndentText(es); appendStringInfo(es->str, - "Buckets: %d Batches: %d", - h->nbuckets, - h->nbatch); + "Buckets: %d", + h->nbuckets); + if (showbatch) + appendStringInfo(es->str, + " Batches: %d", + h->nbatch); } if (es->verbose && es->analyze) diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c index 3603c58..cf0fe3c 100644 --- a/src/backend/executor/execGrouping.c +++ b/src/backend/executor/execGrouping.c @@ -203,6 +203,11 @@ BuildTupleHashTableExt(PlanState *parent, hashtable->hash_iv = 0; hashtable->hashtab = tuplehash_create(metacxt, nbuckets, hashtable); + hashtable->hinstrument.nbuckets_original = nbuckets; + hashtable->hinstrument.nbuckets = nbuckets; + hashtable->hinstrument.space_peak = entrysize * hashtable->hashtab->size; + hashtable->hinstrument.nbatch_original = 1; /* Unused */ + hashtable->hinstrument.nbatch = 1; /* Unused */ /* * We copy the input tuple descriptor just for safety --- we assume all @@ -328,6 +333,11 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, { /* created new entry */ *isnew = true; + /* maybe grew ? XXX: need to call max() here ? */ + hashtable->hinstrument.nbuckets = hashtable->hashtab->size; + hashtable->hinstrument.space_peak = sizeof(TupleHashEntryData) * hashtable->hashtab->size + + sizeof(MinimalTuple) * hashtable->hashtab->size; // members ? + /* zero caller data */ entry->additional = NULL; MemoryContextSwitchTo(hashtable->tablecxt); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 1f6f5bb..913416f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -671,6 +671,19 @@ typedef struct ExecAuxRowMark typedef struct TupleHashEntryData *TupleHashEntry; typedef struct TupleHashTableData *TupleHashTable; +/* ---------------- + * Values displayed by EXPLAIN ANALYZE + * ---------------- + */ +typedef struct HashInstrumentation +{ + int nbuckets; /* number of buckets at end of execution */ + int nbuckets_original; /* planned number of buckets */ + int nbatch; /* number of batches at end of execution */ + int nbatch_original; /* planned number of batches */ + size_t space_peak; /* peak memory usage in bytes */ +} HashInstrumentation; + typedef struct TupleHashEntryData { MinimalTuple firstTuple; /* copy of first tuple in this group */ @@ -705,6 +718,7 @@ typedef struct TupleHashTableData ExprState *cur_eq_func; /* comparator for input vs. table */ uint32 hash_iv; /* hash-function IV */ ExprContext *exprcontext; /* expression context */ + HashInstrumentation hinstrument; /* instrumentation for EXPLAIN */ } TupleHashTableData; typedef tuplehash_iterator TupleHashIterator; @@ -2243,19 +2257,6 @@ typedef struct GatherMergeState } GatherMergeState; /* ---------------- - * Values displayed by EXPLAIN ANALYZE - * ---------------- - */ -typedef struct HashInstrumentation -{ - int nbuckets; /* number of buckets at end of execution */ - int nbuckets_original; /* planned number of buckets */ - int nbatch; /* number of batches at end of execution */ - int nbatch_original; /* planned number of batches */ - size_t space_peak; /* peak memory usage in bytes */ -} HashInstrumentation; - -/* ---------------- * Shared memory container for per-worker hash information * ---------------- */ -- 2.7.4 --JfVplkuTfB13Rsg5--