*** a/src/backend/commands/explain.c --- b/src/backend/commands/explain.c *************** *** 81,86 **** static void show_sort_keys_common(PlanState *planstate, --- 81,87 ---- List *ancestors, ExplainState *es); static void show_sort_info(SortState *sortstate, ExplainState *es); static void show_hash_info(HashState *hashstate, ExplainState *es); + static void show_bitmapscan_info(BitmapHeapScanState *planstate, ExplainState *es); static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es); static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es); *************** *** 1246,1251 **** ExplainNode(PlanState *planstate, List *ancestors, --- 1247,1254 ---- if (((BitmapHeapScan *) plan)->bitmapqualorig) show_instrumentation_count("Rows Removed by Index Recheck", 2, planstate, es); + if (es->analyze) + show_bitmapscan_info((BitmapHeapScanState *) planstate, es); /* FALL THRU */ case T_SeqScan: case T_ValuesScan: *************** *** 1814,1819 **** show_hash_info(HashState *hashstate, ExplainState *es) --- 1817,1860 ---- } /* + * If it's EXPLAIN ANALYZE, show information for a BitmapHeapScan node. + */ + static void + show_bitmapscan_info(BitmapHeapScanState *planstate, ExplainState *es) + { + long spacePeakKb = (planstate->tbm_spacePeak + 1023) / 1024; + long spaceDesiredKb = (planstate->tbm_spaceDesired + 1023) / 1024; + + if (es->format == EXPLAIN_FORMAT_TEXT) + { + appendStringInfoSpaces(es->str, es->indent * 2); + appendStringInfoString(es->str, "Heap Blocks:"); + if (planstate->exact_pages > 0) + appendStringInfo(es->str, " exact=%ld", planstate->exact_pages); + if (planstate->lossy_pages > 0) + appendStringInfo(es->str, " lossy=%ld", planstate->lossy_pages); + appendStringInfoChar(es->str, '\n'); + appendStringInfoSpaces(es->str, es->indent * 2); + appendStringInfo(es->str, "Bitmap Memory Usage: %ldkB", spacePeakKb); + if (planstate->tbm_lossified) + { + if (planstate->tbm_pageadded) + appendStringInfo(es->str, " (up to %ldkB desired)", spaceDesiredKb); + else + appendStringInfo(es->str, " (%ldkB desired)", spaceDesiredKb); + } + appendStringInfoChar(es->str, '\n'); + } + else + { + ExplainPropertyLong("Exact Heap Blocks", planstate->exact_pages, es); + ExplainPropertyLong("Lossy Heap Blocks", planstate->lossy_pages, es); + ExplainPropertyLong("Peak Memory Usage", spacePeakKb, es); + ExplainPropertyLong("Desired Memory", spaceDesiredKb, es); + } + } + + /* * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node * * "which" identifies which instrumentation counter to print *** a/src/backend/executor/nodeBitmapHeapscan.c --- b/src/backend/executor/nodeBitmapHeapscan.c *************** *** 101,106 **** BitmapHeapNext(BitmapHeapScanState *node) --- 101,111 ---- */ if (tbm == NULL) { + long spacePeak; + long spaceDesired; + bool lossified; + bool pageadded; + tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node)); if (!tbm || !IsA(tbm, TIDBitmap)) *************** *** 110,115 **** BitmapHeapNext(BitmapHeapScanState *node) --- 115,130 ---- node->tbmiterator = tbmiterator = tbm_begin_iterate(tbm); node->tbmres = tbmres = NULL; + tbm_get_stats(tbm, &spacePeak, &spaceDesired, &lossified, &pageadded); + if (spacePeak > node->tbm_spacePeak) + node->tbm_spacePeak = spacePeak; + if (spaceDesired > node->tbm_spaceDesired) + node->tbm_spaceDesired = spaceDesired; + if (lossified) + node->tbm_lossified = true; + if (pageadded) + node->tbm_pageadded = true; + #ifdef USE_PREFETCH if (target_prefetch_pages > 0) { *************** *** 170,175 **** BitmapHeapNext(BitmapHeapScanState *node) --- 185,195 ---- */ bitgetpage(scan, tbmres); + if (tbmres->ntuples >= 0) + node->exact_pages++; + else + node->lossy_pages++; + /* * Set rs_cindex to first slot to examine */ *************** *** 556,561 **** ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) --- 576,587 ---- scanstate->prefetch_iterator = NULL; scanstate->prefetch_pages = 0; scanstate->prefetch_target = 0; + scanstate->exact_pages = 0; + scanstate->lossy_pages = 0; + scanstate->tbm_spacePeak = 0; + scanstate->tbm_spaceDesired = 0; + scanstate->tbm_lossified = false; + scanstate->tbm_pageadded = false; /* * Miscellaneous initialization *** a/src/backend/nodes/tidbitmap.c --- b/src/backend/nodes/tidbitmap.c *************** *** 130,138 **** struct TIDBitmap --- 130,143 ---- TBMStatus status; /* see codes above */ HTAB *pagetable; /* hash table of PagetableEntry's */ int nentries; /* number of entries in pagetable */ + int nentriesPeak; /* peak number of entries in pagetable */ int maxentries; /* limit on same to meet maxbytes */ int npages; /* number of exact entries in pagetable */ int nchunks; /* number of lossy entries in pagetable */ + int totalpages; /* total number of exact/loosy pages */ + int totalpagesPeak; /* peak total number of exact/lossy pages */ + bool lossified; /* tbm_lossify called? */ + bool pageadded; /* tbm_add_page called? */ bool iterating; /* tbm_begin_iterate called? */ PagetableEntry entry1; /* used when status == TBM_ONE_PAGE */ /* these are valid when iterating is true: */ *************** *** 293,298 **** tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, --- 298,306 ---- { /* The page is a lossy chunk header, set bit for itself */ wordnum = bitnum = 0; + tbm->totalpages++; + if (tbm->totalpages > tbm->totalpagesPeak) + tbm->totalpagesPeak = tbm->totalpages; } else { *************** *** 317,322 **** tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, --- 325,331 ---- void tbm_add_page(TIDBitmap *tbm, BlockNumber pageno) { + tbm->pageadded = true; /* Enter the page in the bitmap, or mark it lossy if already present */ tbm_mark_page_lossy(tbm, pageno); /* If we went over the memory limit, lossify some more pages */ *************** *** 333,338 **** void --- 342,355 ---- tbm_union(TIDBitmap *a, const TIDBitmap *b) { Assert(!a->iterating); + if (a->nentriesPeak < b->nentriesPeak) + a->nentriesPeak = b->nentriesPeak; + if (a->totalpagesPeak < b->totalpagesPeak) + a->totalpagesPeak = b->totalpagesPeak; + if (b->lossified) + a->lossified = b->lossified; + if (b->pageadded) + a->pageadded = b->pageadded; /* Nothing to do if b is empty */ if (b->nentries == 0) return; *************** *** 392,397 **** tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage) --- 409,417 ---- { /* The page is a lossy chunk header, set bit for itself */ apage->words[0] |= ((bitmapword) 1 << 0); + a->totalpages++; + if (a->totalpages > a->totalpagesPeak) + a->totalpagesPeak = a->totalpages; } else { *************** *** 415,420 **** void --- 435,448 ---- tbm_intersect(TIDBitmap *a, const TIDBitmap *b) { Assert(!a->iterating); + if (a->nentriesPeak < b->nentriesPeak) + a->nentriesPeak = b->nentriesPeak; + if (a->totalpagesPeak < b->totalpagesPeak) + a->totalpagesPeak = b->totalpagesPeak; + if (b->lossified) + a->lossified = b->lossified; + if (b->pageadded) + a->pageadded = b->pageadded; /* Nothing to do if a is empty */ if (a->nentries == 0) return; *************** *** 494,499 **** tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b) --- 522,528 ---- { /* Page is not in b at all, lose lossy bit */ neww &= ~((bitmapword) 1 << bitnum); + a->totalpages--; } } pg++; *************** *** 536,541 **** tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b) --- 565,572 ---- apage->recheck |= bpage->recheck; } /* If there is no matching b page, we can just delete the a page */ + if (candelete) + a->totalpages--; return candelete; } } *************** *** 754,759 **** tbm_end_iterate(TBMIterator *iterator) --- 785,812 ---- } /* + * tbm_get_stats - extract summary statistics + * + * spacePeak and spaceDesired are measured in bytes respectively. + */ + void + tbm_get_stats(const TIDBitmap *tbm, + long *spacePeak, + long *spaceDesired, + bool *lossified, + bool *pageadded) + { + *spacePeak = tbm->nentriesPeak * + (MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(sizeof(PagetableEntry)) + + sizeof(Pointer) + sizeof(Pointer)); + *spaceDesired = tbm->totalpagesPeak * + (MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(sizeof(PagetableEntry)) + + sizeof(Pointer) + sizeof(Pointer)); + *lossified = tbm->lossified; + *pageadded = tbm->pageadded; + } + + /* * tbm_find_pageentry - find a PagetableEntry for the pageno * * Returns NULL if there is no non-lossy entry for the pageno. *************** *** 829,836 **** tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno) --- 882,894 ---- MemSet(page, 0, sizeof(PagetableEntry)); page->blockno = pageno; /* must count it too */ + tbm->totalpages++; tbm->nentries++; tbm->npages++; + if (tbm->totalpages > tbm->totalpagesPeak) + tbm->totalpagesPeak = tbm->totalpages; + if (tbm->nentries > tbm->nentriesPeak) + tbm->nentriesPeak = tbm->nentries; } return page; *************** *** 901,906 **** tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno) --- 959,965 ---- HASH_REMOVE, NULL) != NULL) { /* It was present, so adjust counts */ + tbm->totalpages--; tbm->nentries--; tbm->npages--; /* assume it must have been non-lossy */ } *************** *** 920,925 **** tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno) --- 979,986 ---- /* must count it too */ tbm->nentries++; tbm->nchunks++; + if (tbm->nentries > tbm->nentriesPeak) + tbm->nentriesPeak = tbm->nentries; } else if (!page->ischunk) { *************** *** 937,943 **** tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno) /* Now set the original target page's bit */ wordnum = WORDNUM(bitno); bitnum = BITNUM(bitno); ! page->words[wordnum] |= ((bitmapword) 1 << bitnum); } /* --- 998,1010 ---- /* Now set the original target page's bit */ wordnum = WORDNUM(bitno); bitnum = BITNUM(bitno); ! if ((page->words[wordnum] & ((bitmapword) 1 << bitnum)) == 0) ! { ! page->words[wordnum] |= ((bitmapword) 1 << bitnum); ! tbm->totalpages++; ! if (tbm->totalpages > tbm->totalpagesPeak) ! tbm->totalpagesPeak = tbm->totalpages; ! } } /* *************** *** 960,965 **** tbm_lossify(TIDBitmap *tbm) --- 1027,1033 ---- */ Assert(!tbm->iterating); Assert(tbm->status == TBM_HASH); + tbm->lossified = true; hash_seq_init(&status, tbm->pagetable); while ((page = (PagetableEntry *) hash_seq_search(&status)) != NULL) *** a/src/include/nodes/execnodes.h --- b/src/include/nodes/execnodes.h *************** *** 1352,1357 **** typedef struct BitmapHeapScanState --- 1352,1363 ---- TBMIterator *prefetch_iterator; int prefetch_pages; int prefetch_target; + long exact_pages; + long lossy_pages; + long tbm_spacePeak; + long tbm_spaceDesired; + bool tbm_lossified; + bool tbm_pageadded; } BitmapHeapScanState; /* ---------------- *** a/src/include/nodes/tidbitmap.h --- b/src/include/nodes/tidbitmap.h *************** *** 63,66 **** extern TBMIterator *tbm_begin_iterate(TIDBitmap *tbm); --- 63,72 ---- extern TBMIterateResult *tbm_iterate(TBMIterator *iterator); extern void tbm_end_iterate(TBMIterator *iterator); + extern void tbm_get_stats(const TIDBitmap *tbm, + long *spacePeak, + long *spaceDesired, + bool *lossified, + bool *pageadded); + #endif /* TIDBITMAP_H */