v4-0002-Make-some-minor-performance-improvements-in-psql-.patch
text/x-diff
Filename: v4-0002-Make-some-minor-performance-improvements-in-psql-.patch
Type: text/x-diff
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v4-0002
Subject: Make some minor performance improvements in psql's line-counting.
| File | + | − |
|---|---|---|
| src/fe_utils/print.c | 73 | 24 |
From d4fe1827f338e38ed5dbc2042ab199fb09954fbd Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 1 Oct 2025 18:04:02 -0400
Subject: [PATCH v4 2/2] Make some minor performance improvements in psql's
line-counting.
Arrange to not run count_table_lines at all unless we will use
its result, and teach it to quit early as soon as it's proven
that the output is long enough to require use of the pager.
When dealing with large tables this should save a noticeable
amount of time, since pg_wcssize() isn't exactly cheap.
In passing, fix print_aligned_text to avoid using repeated
"i % col_count" when it could just make the value of i wrap
around. In a quick check with the version of gcc I'm using,
the compiler is smart enough to recognize the common subexpressions
and issue only one integer divide per loop, but it's not smart enough
to avoid the integer divide altogether. In any case, this coding
was randomly unlike the way it's done in count_table_lines.
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/2dd2430f-dd20-4c89-97fd-242616a3d768@ewie.name
---
src/fe_utils/print.c | 97 +++++++++++++++++++++++++++++++++-----------
1 file changed, 73 insertions(+), 24 deletions(-)
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 71bc14d499b..d8c8d7d2a3b 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -266,13 +266,18 @@ static const unicodeStyleFormat unicode_style = {
/* Local functions */
static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
+static FILE *PageOutputInternal(int lines, const printTableOpt *topt,
+ const printTableContent *cont,
+ const unsigned int *width_wrap,
+ bool expanded);
static void IsPagerNeeded(const printTableContent *cont,
const unsigned int *width_wrap,
bool expanded,
FILE **fout, bool *is_pager);
static int count_table_lines(const printTableContent *cont,
const unsigned int *width_wrap,
- bool expanded);
+ bool expanded,
+ int threshold);
static void print_aligned_vertical(const printTableContent *cont,
FILE *fout, bool is_pager);
@@ -730,7 +735,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
}
/* scan all cells, find maximum width, compute cell_count */
- for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
+ for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
{
int width,
nl_lines,
@@ -739,14 +744,18 @@ print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
&width, &nl_lines, &bytes_required);
- if (width > max_width[i % col_count])
- max_width[i % col_count] = width;
- if (nl_lines > max_nl_lines[i % col_count])
- max_nl_lines[i % col_count] = nl_lines;
- if (bytes_required > max_bytes[i % col_count])
- max_bytes[i % col_count] = bytes_required;
+ if (width > max_width[i])
+ max_width[i] = width;
+ if (nl_lines > max_nl_lines[i])
+ max_nl_lines[i] = nl_lines;
+ if (bytes_required > max_bytes[i])
+ max_bytes[i] = bytes_required;
+
+ width_average[i] += width;
- width_average[i % col_count] += width;
+ /* i is the current column number: increment with wrap */
+ if (++i >= col_count)
+ i = 0;
}
/* If we have rows, compute average */
@@ -3046,12 +3055,31 @@ set_sigpipe_trap_state(bool ignore)
/*
* PageOutput
*
- * Tests if pager is needed and returns appropriate FILE pointer.
+ * Tests if pager is needed and returns appropriate FILE pointer
+ * (either a pipe, or stdout if we don't need the pager).
+ *
+ * lines: number of lines that will be printed
+ * topt: print formatting options
*
* If the topt argument is NULL no pager is used.
*/
FILE *
PageOutput(int lines, const printTableOpt *topt)
+{
+ return PageOutputInternal(lines, topt, NULL, NULL, false);
+}
+
+/*
+ * Private version that allows for line-counting to be avoided when
+ * not needed. If "cont" is not null then the input value of "lines"
+ * is ignored and we count lines based on cont + width_wrap + expanded
+ * (see count_table_lines).
+ */
+static FILE *
+PageOutputInternal(int lines, const printTableOpt *topt,
+ const printTableContent *cont,
+ const unsigned int *width_wrap,
+ bool expanded)
{
/* check whether we need / can / are supposed to use pager */
if (topt && topt->pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
@@ -3059,15 +3087,29 @@ PageOutput(int lines, const printTableOpt *topt)
#ifdef TIOCGWINSZ
unsigned short int pager = topt->pager;
int min_lines = topt->pager_min_lines;
- int result;
- struct winsize screen_size;
- result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
+ if (pager == 1)
+ {
+ int result;
+ struct winsize screen_size;
- /* >= accounts for a one-line prompt */
- if (result == -1
- || (lines >= screen_size.ws_row && lines >= min_lines)
- || pager > 1)
+ result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
+ if (result < 0)
+ pager = 2; /* force use of pager */
+ else
+ {
+ int threshold = Max(screen_size.ws_row, min_lines);
+
+ if (cont) /* caller wants us to calculate lines */
+ lines = count_table_lines(cont, width_wrap, expanded,
+ threshold);
+ /* >= accounts for a one-line prompt */
+ if (lines >= threshold)
+ pager = 2;
+ }
+ }
+
+ if (pager > 1)
#endif
{
const char *pagerprog;
@@ -3379,11 +3421,7 @@ IsPagerNeeded(const printTableContent *cont, const unsigned int *width_wrap,
{
if (*fout == stdout)
{
- int lines;
-
- lines = count_table_lines(cont, width_wrap, expanded);
-
- *fout = PageOutput(lines, cont->opt);
+ *fout = PageOutputInternal(0, cont->opt, cont, width_wrap, expanded);
*is_pager = (*fout != stdout);
}
else
@@ -3396,13 +3434,18 @@ IsPagerNeeded(const printTableContent *cont, const unsigned int *width_wrap,
* cont: table data to be printed
* width_wrap[]: per-column maximum width, or NULL if caller will not wrap
* expanded: expanded mode?
+ * threshold: we can stop counting once we pass threshold
*
* We currently handle only regular and expanded modes here.
+ *
+ * The value of the threshold parameter is that when the table is very
+ * long, we'll typically be able to stop scanning after not many rows.
*/
static int
count_table_lines(const printTableContent *cont,
const unsigned int *width_wrap,
- bool expanded)
+ bool expanded,
+ int threshold)
{
int *header_height;
int lines,
@@ -3468,11 +3511,14 @@ count_table_lines(const printTableContent *cont,
lines += max_lines;
max_lines = 0;
}
+ /* Stop scanning table body once we pass threshold */
+ if (lines > threshold)
+ break;
}
}
/* Account for header and footer decoration */
- if (!cont->opt->tuples_only)
+ if (!cont->opt->tuples_only && lines <= threshold)
{
if (cont->title)
{
@@ -3500,6 +3546,9 @@ count_table_lines(const printTableContent *cont,
pg_wcssize((const unsigned char *) f->data, strlen(f->data),
encoding, NULL, &nl_lines, NULL);
lines += nl_lines;
+ /* Stop scanning footers once we pass threshold */
+ if (lines > threshold)
+ break;
}
}
--
2.43.7