Skip to content

Commit 4c119fb

Browse files
committed
Improve performance of SendRowDescriptionMessage.
There's three categories of changes leading to better performance: - Splitting the per-attribute part of SendRowDescriptionMessage into a v2 and a v3 version allows avoiding branches for every attribute. - Preallocating the size of the buffer to be big enough for all attributes and then using pq_write* avoids unnecessary buffer size checks & resizing. - Reusing a persistently allocated StringInfo for all SendRowDescriptionMessage() invocations avoids repeated allocations & reallocations. Author: Andres Freund Discussion: https://postgr.es/m/20170914063418.sckdzgjfrsbekae4@alap3.anarazel.de
1 parent cff440d commit 4c119fb

File tree

3 files changed

+138
-47
lines changed

3 files changed

+138
-47
lines changed

src/backend/access/common/printtup.c

Lines changed: 108 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
3232
static void printtup_shutdown(DestReceiver *self);
3333
static void printtup_destroy(DestReceiver *self);
3434

35+
static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
36+
List *targetlist, int16 *formats);
37+
static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo,
38+
List *targetlist, int16 *formats);
3539

3640
/* ----------------------------------------------------------------
3741
* printtup / debugtup support
@@ -161,7 +165,8 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
161165
* descriptor of the tuples.
162166
*/
163167
if (myState->sendDescrip)
164-
SendRowDescriptionMessage(typeinfo,
168+
SendRowDescriptionMessage(&myState->buf,
169+
typeinfo,
165170
FetchPortalTargetList(portal),
166171
portal->formats);
167172

@@ -189,61 +194,126 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
189194
* send zeroes for the format codes in that case.
190195
*/
191196
void
192-
SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
197+
SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
198+
List *targetlist, int16 *formats)
193199
{
194200
int natts = typeinfo->natts;
195201
int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
202+
203+
/* tuple descriptor message type */
204+
pq_beginmessage_reuse(buf, 'T');
205+
/* # of attrs in tuples */
206+
pq_sendint16(buf, natts);
207+
208+
if (proto >= 3)
209+
SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats);
210+
else
211+
SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats);
212+
213+
pq_endmessage_reuse(buf);
214+
}
215+
216+
/*
217+
* Send description for each column when using v3+ protocol
218+
*/
219+
static void
220+
SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
221+
{
222+
int natts = typeinfo->natts;
196223
int i;
197-
StringInfoData buf;
198224
ListCell *tlist_item = list_head(targetlist);
199225

200-
pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
201-
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
226+
/*
227+
* Preallocate memory for the entire message to be sent. That allows to
228+
* use the significantly faster inline pqformat.h functions and to avoid
229+
* reallocations.
230+
*
231+
* Have to overestimate the size of the column-names, to account for
232+
* character set overhead.
233+
*/
234+
enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
235+
+ sizeof(Oid) /* resorigtbl */
236+
+ sizeof(AttrNumber) /* resorigcol */
237+
+ sizeof(Oid) /* atttypid */
238+
+ sizeof(int16) /* attlen */
239+
+ sizeof(int32) /* attypmod */
240+
+ sizeof(int16) /* format */
241+
) * natts);
202242

203243
for (i = 0; i < natts; ++i)
204244
{
205245
Form_pg_attribute att = TupleDescAttr(typeinfo, i);
206246
Oid atttypid = att->atttypid;
207247
int32 atttypmod = att->atttypmod;
248+
Oid resorigtbl;
249+
AttrNumber resorigcol;
250+
int16 format;
251+
252+
/*
253+
* If column is a domain, send the base type and typmod instead.
254+
* Lookup before sending any ints, for efficiency.
255+
*/
256+
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
208257

209-
pq_sendstring(&buf, NameStr(att->attname));
210-
/* column ID info appears in protocol 3.0 and up */
211-
if (proto >= 3)
258+
/* Do we have a non-resjunk tlist item? */
259+
while (tlist_item &&
260+
((TargetEntry *) lfirst(tlist_item))->resjunk)
261+
tlist_item = lnext(tlist_item);
262+
if (tlist_item)
212263
{
213-
/* Do we have a non-resjunk tlist item? */
214-
while (tlist_item &&
215-
((TargetEntry *) lfirst(tlist_item))->resjunk)
216-
tlist_item = lnext(tlist_item);
217-
if (tlist_item)
218-
{
219-
TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
220-
221-
pq_sendint(&buf, tle->resorigtbl, 4);
222-
pq_sendint(&buf, tle->resorigcol, 2);
223-
tlist_item = lnext(tlist_item);
224-
}
225-
else
226-
{
227-
/* No info available, so send zeroes */
228-
pq_sendint(&buf, 0, 4);
229-
pq_sendint(&buf, 0, 2);
230-
}
264+
TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
265+
266+
resorigtbl = tle->resorigtbl;
267+
resorigcol = tle->resorigcol;
268+
tlist_item = lnext(tlist_item);
231269
}
232-
/* If column is a domain, send the base type and typmod instead */
233-
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
234-
pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
235-
pq_sendint(&buf, att->attlen, sizeof(att->attlen));
236-
pq_sendint(&buf, atttypmod, sizeof(atttypmod));
237-
/* format info appears in protocol 3.0 and up */
238-
if (proto >= 3)
270+
else
239271
{
240-
if (formats)
241-
pq_sendint(&buf, formats[i], 2);
242-
else
243-
pq_sendint(&buf, 0, 2);
272+
/* No info available, so send zeroes */
273+
resorigtbl = 0;
274+
resorigcol = 0;
244275
}
276+
277+
if (formats)
278+
format = formats[i];
279+
else
280+
format = 0;
281+
282+
pq_writestring(buf, NameStr(att->attname));
283+
pq_writeint32(buf, resorigtbl);
284+
pq_writeint16(buf, resorigcol);
285+
pq_writeint32(buf, atttypid);
286+
pq_writeint16(buf, att->attlen);
287+
pq_writeint32(buf, atttypmod);
288+
pq_writeint16(buf, format);
289+
}
290+
}
291+
292+
/*
293+
* Send description for each column when using v2 protocol
294+
*/
295+
static void
296+
SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
297+
{
298+
int natts = typeinfo->natts;
299+
int i;
300+
301+
for (i = 0; i < natts; ++i)
302+
{
303+
Form_pg_attribute att = TupleDescAttr(typeinfo, i);
304+
Oid atttypid = att->atttypid;
305+
int32 atttypmod = att->atttypmod;
306+
307+
/* If column is a domain, send the base type and typmod instead */
308+
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
309+
310+
pq_sendstring(buf, NameStr(att->attname));
311+
/* column ID only info appears in protocol 3.0 and up */
312+
pq_sendint32(buf, atttypid);
313+
pq_sendint16(buf, att->attlen);
314+
pq_sendint32(buf, atttypmod);
315+
/* format info only appears in protocol 3.0 and up */
245316
}
246-
pq_endmessage(&buf);
247317
}
248318

249319
/*

src/backend/tcop/postgres.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ static bool RecoveryConflictPending = false;
165165
static bool RecoveryConflictRetryable = true;
166166
static ProcSignalReason RecoveryConflictReason;
167167

168+
/* reused buffer to pass to SendRowDescriptionMessage() */
169+
static MemoryContext row_description_context = NULL;
170+
static StringInfoData row_description_buf;
171+
168172
/* ----------------------------------------------------------------
169173
* decls for routines only used in this file
170174
* ----------------------------------------------------------------
@@ -2315,7 +2319,6 @@ static void
23152319
exec_describe_statement_message(const char *stmt_name)
23162320
{
23172321
CachedPlanSource *psrc;
2318-
StringInfoData buf;
23192322
int i;
23202323

23212324
/*
@@ -2371,16 +2374,17 @@ exec_describe_statement_message(const char *stmt_name)
23712374
/*
23722375
* First describe the parameters...
23732376
*/
2374-
pq_beginmessage(&buf, 't'); /* parameter description message type */
2375-
pq_sendint(&buf, psrc->num_params, 2);
2377+
pq_beginmessage_reuse(&row_description_buf, 't'); /* parameter description
2378+
* message type */
2379+
pq_sendint(&row_description_buf, psrc->num_params, 2);
23762380

23772381
for (i = 0; i < psrc->num_params; i++)
23782382
{
23792383
Oid ptype = psrc->param_types[i];
23802384

2381-
pq_sendint(&buf, (int) ptype, 4);
2385+
pq_sendint(&row_description_buf, (int) ptype, 4);
23822386
}
2383-
pq_endmessage(&buf);
2387+
pq_endmessage_reuse(&row_description_buf);
23842388

23852389
/*
23862390
* Next send RowDescription or NoData to describe the result...
@@ -2392,7 +2396,10 @@ exec_describe_statement_message(const char *stmt_name)
23922396
/* Get the plan's primary targetlist */
23932397
tlist = CachedPlanGetTargetList(psrc, NULL);
23942398

2395-
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
2399+
SendRowDescriptionMessage(&row_description_buf,
2400+
psrc->resultDesc,
2401+
tlist,
2402+
NULL);
23962403
}
23972404
else
23982405
pq_putemptymessage('n'); /* NoData */
@@ -2444,7 +2451,8 @@ exec_describe_portal_message(const char *portal_name)
24442451
return; /* can't actually do anything... */
24452452

24462453
if (portal->tupDesc)
2447-
SendRowDescriptionMessage(portal->tupDesc,
2454+
SendRowDescriptionMessage(&row_description_buf,
2455+
portal->tupDesc,
24482456
FetchPortalTargetList(portal),
24492457
portal->formats);
24502458
else
@@ -3830,6 +3838,19 @@ PostgresMain(int argc, char *argv[],
38303838
"MessageContext",
38313839
ALLOCSET_DEFAULT_SIZES);
38323840

3841+
/*
3842+
* Create memory context and buffer used for RowDescription messages. As
3843+
* SendRowDescriptionMessage(), via exec_describe_statement_message(), is
3844+
* frequently executed for ever single statement, we don't want to
3845+
* allocate a separate buffer every time.
3846+
*/
3847+
row_description_context = AllocSetContextCreate(TopMemoryContext,
3848+
"RowDescriptionContext",
3849+
ALLOCSET_DEFAULT_SIZES);
3850+
MemoryContextSwitchTo(row_description_context);
3851+
initStringInfo(&row_description_buf);
3852+
MemoryContextSwitchTo(TopMemoryContext);
3853+
38333854
/*
38343855
* Remember stand-alone backend startup time
38353856
*/

src/include/access/printtup.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ extern DestReceiver *printtup_create_DR(CommandDest dest);
2020

2121
extern void SetRemoteDestReceiverParams(DestReceiver *self, Portal portal);
2222

23-
extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
24-
int16 *formats);
23+
extern void SendRowDescriptionMessage(StringInfo buf,
24+
TupleDesc typeinfo, List *targetlist, int16 *formats);
2525

2626
extern void debugStartup(DestReceiver *self, int operation,
2727
TupleDesc typeinfo);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy