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
This commit is contained in:
parent
cff440d368
commit
4c119fbcd4
@ -32,6 +32,10 @@ static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
|
|||||||
static void printtup_shutdown(DestReceiver *self);
|
static void printtup_shutdown(DestReceiver *self);
|
||||||
static void printtup_destroy(DestReceiver *self);
|
static void printtup_destroy(DestReceiver *self);
|
||||||
|
|
||||||
|
static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
|
||||||
|
List *targetlist, int16 *formats);
|
||||||
|
static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo,
|
||||||
|
List *targetlist, int16 *formats);
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* printtup / debugtup support
|
* printtup / debugtup support
|
||||||
@ -161,7 +165,8 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||||||
* descriptor of the tuples.
|
* descriptor of the tuples.
|
||||||
*/
|
*/
|
||||||
if (myState->sendDescrip)
|
if (myState->sendDescrip)
|
||||||
SendRowDescriptionMessage(typeinfo,
|
SendRowDescriptionMessage(&myState->buf,
|
||||||
|
typeinfo,
|
||||||
FetchPortalTargetList(portal),
|
FetchPortalTargetList(portal),
|
||||||
portal->formats);
|
portal->formats);
|
||||||
|
|
||||||
@ -189,16 +194,109 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||||||
* send zeroes for the format codes in that case.
|
* send zeroes for the format codes in that case.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
|
SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
|
||||||
|
List *targetlist, int16 *formats)
|
||||||
{
|
{
|
||||||
int natts = typeinfo->natts;
|
int natts = typeinfo->natts;
|
||||||
int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
|
int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
|
||||||
|
|
||||||
|
/* tuple descriptor message type */
|
||||||
|
pq_beginmessage_reuse(buf, 'T');
|
||||||
|
/* # of attrs in tuples */
|
||||||
|
pq_sendint16(buf, natts);
|
||||||
|
|
||||||
|
if (proto >= 3)
|
||||||
|
SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats);
|
||||||
|
else
|
||||||
|
SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats);
|
||||||
|
|
||||||
|
pq_endmessage_reuse(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send description for each column when using v3+ protocol
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
|
||||||
|
{
|
||||||
|
int natts = typeinfo->natts;
|
||||||
int i;
|
int i;
|
||||||
StringInfoData buf;
|
|
||||||
ListCell *tlist_item = list_head(targetlist);
|
ListCell *tlist_item = list_head(targetlist);
|
||||||
|
|
||||||
pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
|
/*
|
||||||
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
|
* Preallocate memory for the entire message to be sent. That allows to
|
||||||
|
* use the significantly faster inline pqformat.h functions and to avoid
|
||||||
|
* reallocations.
|
||||||
|
*
|
||||||
|
* Have to overestimate the size of the column-names, to account for
|
||||||
|
* character set overhead.
|
||||||
|
*/
|
||||||
|
enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
|
||||||
|
+ sizeof(Oid) /* resorigtbl */
|
||||||
|
+ sizeof(AttrNumber) /* resorigcol */
|
||||||
|
+ sizeof(Oid) /* atttypid */
|
||||||
|
+ sizeof(int16) /* attlen */
|
||||||
|
+ sizeof(int32) /* attypmod */
|
||||||
|
+ sizeof(int16) /* format */
|
||||||
|
) * natts);
|
||||||
|
|
||||||
|
for (i = 0; i < natts; ++i)
|
||||||
|
{
|
||||||
|
Form_pg_attribute att = TupleDescAttr(typeinfo, i);
|
||||||
|
Oid atttypid = att->atttypid;
|
||||||
|
int32 atttypmod = att->atttypmod;
|
||||||
|
Oid resorigtbl;
|
||||||
|
AttrNumber resorigcol;
|
||||||
|
int16 format;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If column is a domain, send the base type and typmod instead.
|
||||||
|
* Lookup before sending any ints, for efficiency.
|
||||||
|
*/
|
||||||
|
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
|
||||||
|
|
||||||
|
/* Do we have a non-resjunk tlist item? */
|
||||||
|
while (tlist_item &&
|
||||||
|
((TargetEntry *) lfirst(tlist_item))->resjunk)
|
||||||
|
tlist_item = lnext(tlist_item);
|
||||||
|
if (tlist_item)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
|
||||||
|
|
||||||
|
resorigtbl = tle->resorigtbl;
|
||||||
|
resorigcol = tle->resorigcol;
|
||||||
|
tlist_item = lnext(tlist_item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No info available, so send zeroes */
|
||||||
|
resorigtbl = 0;
|
||||||
|
resorigcol = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats)
|
||||||
|
format = formats[i];
|
||||||
|
else
|
||||||
|
format = 0;
|
||||||
|
|
||||||
|
pq_writestring(buf, NameStr(att->attname));
|
||||||
|
pq_writeint32(buf, resorigtbl);
|
||||||
|
pq_writeint16(buf, resorigcol);
|
||||||
|
pq_writeint32(buf, atttypid);
|
||||||
|
pq_writeint16(buf, att->attlen);
|
||||||
|
pq_writeint32(buf, atttypmod);
|
||||||
|
pq_writeint16(buf, format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send description for each column when using v2 protocol
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
|
||||||
|
{
|
||||||
|
int natts = typeinfo->natts;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < natts; ++i)
|
for (i = 0; i < natts; ++i)
|
||||||
{
|
{
|
||||||
@ -206,44 +304,16 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
|
|||||||
Oid atttypid = att->atttypid;
|
Oid atttypid = att->atttypid;
|
||||||
int32 atttypmod = att->atttypmod;
|
int32 atttypmod = att->atttypmod;
|
||||||
|
|
||||||
pq_sendstring(&buf, NameStr(att->attname));
|
|
||||||
/* column ID info appears in protocol 3.0 and up */
|
|
||||||
if (proto >= 3)
|
|
||||||
{
|
|
||||||
/* Do we have a non-resjunk tlist item? */
|
|
||||||
while (tlist_item &&
|
|
||||||
((TargetEntry *) lfirst(tlist_item))->resjunk)
|
|
||||||
tlist_item = lnext(tlist_item);
|
|
||||||
if (tlist_item)
|
|
||||||
{
|
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
|
|
||||||
|
|
||||||
pq_sendint(&buf, tle->resorigtbl, 4);
|
|
||||||
pq_sendint(&buf, tle->resorigcol, 2);
|
|
||||||
tlist_item = lnext(tlist_item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* No info available, so send zeroes */
|
|
||||||
pq_sendint(&buf, 0, 4);
|
|
||||||
pq_sendint(&buf, 0, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* If column is a domain, send the base type and typmod instead */
|
/* If column is a domain, send the base type and typmod instead */
|
||||||
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
|
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
|
||||||
pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
|
|
||||||
pq_sendint(&buf, att->attlen, sizeof(att->attlen));
|
pq_sendstring(buf, NameStr(att->attname));
|
||||||
pq_sendint(&buf, atttypmod, sizeof(atttypmod));
|
/* column ID only info appears in protocol 3.0 and up */
|
||||||
/* format info appears in protocol 3.0 and up */
|
pq_sendint32(buf, atttypid);
|
||||||
if (proto >= 3)
|
pq_sendint16(buf, att->attlen);
|
||||||
{
|
pq_sendint32(buf, atttypmod);
|
||||||
if (formats)
|
/* format info only appears in protocol 3.0 and up */
|
||||||
pq_sendint(&buf, formats[i], 2);
|
|
||||||
else
|
|
||||||
pq_sendint(&buf, 0, 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pq_endmessage(&buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -165,6 +165,10 @@ static bool RecoveryConflictPending = false;
|
|||||||
static bool RecoveryConflictRetryable = true;
|
static bool RecoveryConflictRetryable = true;
|
||||||
static ProcSignalReason RecoveryConflictReason;
|
static ProcSignalReason RecoveryConflictReason;
|
||||||
|
|
||||||
|
/* reused buffer to pass to SendRowDescriptionMessage() */
|
||||||
|
static MemoryContext row_description_context = NULL;
|
||||||
|
static StringInfoData row_description_buf;
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* decls for routines only used in this file
|
* decls for routines only used in this file
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
@ -2315,7 +2319,6 @@ static void
|
|||||||
exec_describe_statement_message(const char *stmt_name)
|
exec_describe_statement_message(const char *stmt_name)
|
||||||
{
|
{
|
||||||
CachedPlanSource *psrc;
|
CachedPlanSource *psrc;
|
||||||
StringInfoData buf;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2371,16 +2374,17 @@ exec_describe_statement_message(const char *stmt_name)
|
|||||||
/*
|
/*
|
||||||
* First describe the parameters...
|
* First describe the parameters...
|
||||||
*/
|
*/
|
||||||
pq_beginmessage(&buf, 't'); /* parameter description message type */
|
pq_beginmessage_reuse(&row_description_buf, 't'); /* parameter description
|
||||||
pq_sendint(&buf, psrc->num_params, 2);
|
* message type */
|
||||||
|
pq_sendint(&row_description_buf, psrc->num_params, 2);
|
||||||
|
|
||||||
for (i = 0; i < psrc->num_params; i++)
|
for (i = 0; i < psrc->num_params; i++)
|
||||||
{
|
{
|
||||||
Oid ptype = psrc->param_types[i];
|
Oid ptype = psrc->param_types[i];
|
||||||
|
|
||||||
pq_sendint(&buf, (int) ptype, 4);
|
pq_sendint(&row_description_buf, (int) ptype, 4);
|
||||||
}
|
}
|
||||||
pq_endmessage(&buf);
|
pq_endmessage_reuse(&row_description_buf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Next send RowDescription or NoData to describe the result...
|
* Next send RowDescription or NoData to describe the result...
|
||||||
@ -2392,7 +2396,10 @@ exec_describe_statement_message(const char *stmt_name)
|
|||||||
/* Get the plan's primary targetlist */
|
/* Get the plan's primary targetlist */
|
||||||
tlist = CachedPlanGetTargetList(psrc, NULL);
|
tlist = CachedPlanGetTargetList(psrc, NULL);
|
||||||
|
|
||||||
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
|
SendRowDescriptionMessage(&row_description_buf,
|
||||||
|
psrc->resultDesc,
|
||||||
|
tlist,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pq_putemptymessage('n'); /* NoData */
|
pq_putemptymessage('n'); /* NoData */
|
||||||
@ -2444,7 +2451,8 @@ exec_describe_portal_message(const char *portal_name)
|
|||||||
return; /* can't actually do anything... */
|
return; /* can't actually do anything... */
|
||||||
|
|
||||||
if (portal->tupDesc)
|
if (portal->tupDesc)
|
||||||
SendRowDescriptionMessage(portal->tupDesc,
|
SendRowDescriptionMessage(&row_description_buf,
|
||||||
|
portal->tupDesc,
|
||||||
FetchPortalTargetList(portal),
|
FetchPortalTargetList(portal),
|
||||||
portal->formats);
|
portal->formats);
|
||||||
else
|
else
|
||||||
@ -3830,6 +3838,19 @@ PostgresMain(int argc, char *argv[],
|
|||||||
"MessageContext",
|
"MessageContext",
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create memory context and buffer used for RowDescription messages. As
|
||||||
|
* SendRowDescriptionMessage(), via exec_describe_statement_message(), is
|
||||||
|
* frequently executed for ever single statement, we don't want to
|
||||||
|
* allocate a separate buffer every time.
|
||||||
|
*/
|
||||||
|
row_description_context = AllocSetContextCreate(TopMemoryContext,
|
||||||
|
"RowDescriptionContext",
|
||||||
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
MemoryContextSwitchTo(row_description_context);
|
||||||
|
initStringInfo(&row_description_buf);
|
||||||
|
MemoryContextSwitchTo(TopMemoryContext);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remember stand-alone backend startup time
|
* Remember stand-alone backend startup time
|
||||||
*/
|
*/
|
||||||
|
@ -20,8 +20,8 @@ extern DestReceiver *printtup_create_DR(CommandDest dest);
|
|||||||
|
|
||||||
extern void SetRemoteDestReceiverParams(DestReceiver *self, Portal portal);
|
extern void SetRemoteDestReceiverParams(DestReceiver *self, Portal portal);
|
||||||
|
|
||||||
extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
|
extern void SendRowDescriptionMessage(StringInfo buf,
|
||||||
int16 *formats);
|
TupleDesc typeinfo, List *targetlist, int16 *formats);
|
||||||
|
|
||||||
extern void debugStartup(DestReceiver *self, int operation,
|
extern void debugStartup(DestReceiver *self, int operation,
|
||||||
TupleDesc typeinfo);
|
TupleDesc typeinfo);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user