Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sjtl4-005LqI-Ck for pgsql-hackers@arkaria.postgresql.org; Fri, 30 Aug 2024 05:01:14 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1sjtl1-00E6di-7t for pgsql-hackers@arkaria.postgresql.org; Fri, 30 Aug 2024 05:01:11 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sjtl0-00E6as-Ag for pgsql-hackers@lists.postgresql.org; Fri, 30 Aug 2024 05:01:11 +0000 Received: from m16.mail.163.com ([220.197.31.4]) by magus.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1sjtks-002C4N-Ig for pgsql-hackers@postgresql.org; Fri, 30 Aug 2024 05:01:08 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-ID:MIME-Version: Content-Type; bh=eUCPahoqnGTOE9gS+o4g92LwIJEcofIkJS6e0IXc+L0=; b=hDn7RCO+Y8x64flfS9ehQdVVlVi2p3T/b5DjJrd80pkyZphdfpfJgqVA0rRPR+ 1uRsdcM9D4oPva0a/C51LOaP2ufA1dE1MPySkNp9daXVrTBpF59v8GFVop5SkfxL uyVaXlG63J4+x5DQQqlQvKfwz39ZO+0MYHvr9NutmKMcM= Received: from lovely-coding (unknown [101.227.46.166]) by gzga-smtp-mta-g3-5 (Coremail) with SMTP id _____wDnbw8CUtFm0uOjKA--.6860S3; Fri, 30 Aug 2024 13:00:51 +0800 (CST) From: Andy Fan To: David Rowley Cc: Tom Lane , pgsql-hackers Subject: Re: Make printtup a bit faster In-Reply-To: (David Rowley's message of "Fri, 30 Aug 2024 12:31:07 +1200") References: <87wmjzfz0h.fsf@163.com> <3601908.1724945588@sss.pgh.pa.us> Date: Fri, 30 Aug 2024 13:00:50 +0800 Message-ID: <87v7zihaf1.fsf@163.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-CM-TRANSID: _____wDnbw8CUtFm0uOjKA--.6860S3 X-Coremail-Antispam: 1Uf129KBjvJXoW7WFWftF4xZFy8urW5JF45Awb_yoW8uF4kpa y29w17KrZYyrW7ArnrWF18Jr1YkrZ5AFW2vFs3GF95A345uFyIvF97K3y0va4Uur40kw45 ZFWvqa4DurZ8ZFDanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0zRrR6sUUUUU= X-Originating-IP: [101.227.46.166] X-CM-SenderInfo: x2klx3xlid0iqsrtqiywtou0bp/1tbiNglLU2XAnP+7QAAAsa List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --=-=-= Content-Type: text/plain David Rowley writes: > On Fri, 30 Aug 2024 at 03:33, Tom Lane wrote: >> >> David Rowley writes: >> > [ redesign I/O function APIs ] >> > I had planned to work on this for PG18, but I'd be happy for some >> > assistance if you're willing. >> >> I'm skeptical that such a thing will ever be practical. To avoid >> breaking un-converted data types, all the call sites would have to >> support both old and new APIs. To avoid breaking non-core callers, >> all the I/O functions would have to support both old and new APIs. >> That probably adds enough overhead to negate whatever benefit you'd >> get. > > So, we currently return cstrings in our output functions. Let's take > jsonb_out() as an example, to build that cstring, we make a *new* > StringInfoData on *every call* inside JsonbToCStringWorker(). That > gives you 1024 bytes before you need to enlarge it. However, it's > maybe not all bad as we have some size estimations there to call > enlargeStringInfo(), only that's a bit wasteful as it does a > repalloc() which memcpys the freshly allocated 1024 bytes allocated in > initStringInfo() when it doesn't yet contain any data. After > jsonb_out() has returned and we have the cstring, only we forgot the > length of the string, so most places will immediately call strlen() or > do that indirectly via appendStringInfoString(). For larger JSON > documents, that'll likely require pulling cachelines back into L1 > again. I don't know how modern CPU cacheline eviction works, but if it > was as simple as FIFO then the strlen() would flush all those > cachelines only for memcpy() to have to read them back again for > output strings larger than L1. The attached is PoC of this idea, not matter which method are adopted (rewrite all the outfunction or a optional print function), I think the benefit will be similar. In the blew test case, it shows us 10%+ improvements. (0.134ms vs 0.110ms) create table demo as select oid as oid1, relname::text as text1, relam, relname::text as text2 from pg_class; pgbench: select * from demo; -- Best Regards Andy Fan --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v20240830-0001-Avoiding-some-memcpy-strlen-palloc-in-prin.patch From 5ace763a5126478da1c3cb68d5221d83e45d2f34 Mon Sep 17 00:00:00 2001 From: Andy Fan Date: Fri, 30 Aug 2024 12:50:54 +0800 Subject: [PATCH v20240830 1/1] Avoiding some memcpy, strlen, palloc in printtup. https://www.postgresql.org/message-id/87wmjzfz0h.fsf%40163.com --- src/backend/access/common/printtup.c | 40 ++++++++++++++++++++++------ src/backend/utils/adt/oid.c | 20 ++++++++++++++ src/backend/utils/adt/varlena.c | 17 ++++++++++++ src/include/catalog/pg_proc.dat | 9 ++++++- 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index c78cc39308..ecba4a7113 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -19,6 +19,7 @@ #include "libpq/pqformat.h" #include "libpq/protocol.h" #include "tcop/pquery.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/memdebug.h" #include "utils/memutils.h" @@ -49,6 +50,7 @@ typedef struct bool typisvarlena; /* is it varlena (ie possibly toastable)? */ int16 format; /* format code for this column */ FmgrInfo finfo; /* Precomputed call info for output fn */ + FmgrInfo p_finfo; /* Precomputed call info for print fn if any */ } PrinttupAttrInfo; typedef struct @@ -274,10 +276,25 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) thisState->format = format; if (format == 0) { - getTypeOutputInfo(attr->atttypid, - &thisState->typoutput, - &thisState->typisvarlena); - fmgr_info(thisState->typoutput, &thisState->finfo); + /* + * If the type defines a print function, then use it + * rather than outfunction. + * + * XXX: need a generic function to improve the if-elseif. + */ + if (attr->atttypid == OIDOID) + fmgr_info(F_OIDPRINT, &thisState->p_finfo); + else if (attr->atttypid == TEXTOID) + fmgr_info(F_TEXTPRINT, &thisState->p_finfo); + else + { + getTypeOutputInfo(attr->atttypid, + &thisState->typoutput, + &thisState->typisvarlena); + fmgr_info(thisState->typoutput, &thisState->finfo); + /* mark print function is invalid */ + thisState->p_finfo.fn_oid = InvalidOid; + } } else if (format == 1) { @@ -355,10 +372,17 @@ printtup(TupleTableSlot *slot, DestReceiver *self) if (thisState->format == 0) { /* Text output */ - char *outputstr; - - outputstr = OutputFunctionCall(&thisState->finfo, attr); - pq_sendcountedtext(buf, outputstr, strlen(outputstr)); + if (thisState->p_finfo.fn_oid) + { + FunctionCall2(&thisState->p_finfo, attr, PointerGetDatum(buf)); + } + else + { + char *outputstr; + + outputstr = OutputFunctionCall(&thisState->finfo, attr); + pq_sendcountedtext(buf, outputstr, strlen(outputstr)); + } } else { diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c index 56fb1fd77c..cc85d920c8 100644 --- a/src/backend/utils/adt/oid.c +++ b/src/backend/utils/adt/oid.c @@ -53,6 +53,26 @@ oidout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +oidprint(PG_FUNCTION_ARGS) +{ + Oid o = PG_GETARG_OID(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + uint32 *lenp; + uint32 data_len; + + /* 12 is the max length for an oid's text presentation. */ + enlargeStringInfo(buf, sizeof(int32) + 12); + + /* note the position for len */ + lenp = (uint32 *) (buf->data + buf->len); + data_len = pg_snprintf(buf->data + buf->len + sizeof(int), 12, "%u", o); + *lenp = pg_hton32(data_len); + buf->len += sizeof(uint32) + data_len; + + PG_RETURN_VOID(); +} + /* * oidrecv - converts external binary format to oid */ diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 7c6391a276..3b7006d54a 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -594,6 +594,23 @@ textout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(TextDatumGetCString(txt)); } + +Datum +textprint(PG_FUNCTION_ARGS) +{ + text *txt = (text *) pg_detoast_datum((struct varlena *)PG_GETARG_POINTER(0)); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + uint32 text_len = VARSIZE(txt) - VARHDRSZ; + uint32 ni = pg_hton32(text_len); + + enlargeStringInfo(buf, sizeof(int32) + text_len); + memcpy((char *pg_restrict) buf->data + buf->len, &ni, sizeof(uint32)); + memcpy(buf->data + buf->len + sizeof(int), VARDATA(txt), text_len); + buf->len += sizeof(uint32) + text_len; + + PG_RETURN_VOID(); +} + /* * textrecv - converts external binary format to text */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 85f42be1b3..74eeead4de 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -100,6 +100,10 @@ { oid => '47', descr => 'I/O', proname => 'textout', prorettype => 'cstring', proargtypes => 'text', prosrc => 'textout' }, +{ + oid => '8907', descr => 'I/O', + proname => 'textprint', prorettype => 'void', proargtypes => 'text internal', + prosrc => 'textprint' }, { oid => '48', descr => 'I/O', proname => 'tidin', prorettype => 'tid', proargtypes => 'cstring', prosrc => 'tidin' }, @@ -4718,7 +4722,10 @@ { oid => '1799', descr => 'I/O', proname => 'oidout', prorettype => 'cstring', proargtypes => 'oid', prosrc => 'oidout' }, - +{ + oid => '9771', descr => 'I/O', + proname => 'oidprint', prorettype => 'void', proargtypes => 'oid internal', + prosrc => 'oidprint'}, { oid => '3058', descr => 'concatenate values', proname => 'concat', provariadic => 'any', proisstrict => 'f', provolatile => 's', prorettype => 'text', proargtypes => 'any', -- 2.45.1 --=-=-=--