Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m42kG-0004V4-7N for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jul 2021 14:53:48 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1m42kD-00085g-IQ for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jul 2021 14:53:45 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m42kD-00082I-5F for pgsql-hackers@lists.postgresql.org; Thu, 15 Jul 2021 14:53:45 +0000 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1m42k9-0000ZN-Ox for pgsql-hackers@lists.postgresql.org; Thu, 15 Jul 2021 14:53:43 +0000 Received: by mail-wm1-x32a.google.com with SMTP id l8-20020a05600c1d08b02902333d79327aso3209530wms.3 for ; Thu, 15 Jul 2021 07:53:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aiven.io; s=google; h=from:to:cc:subject:date:message-id:organization:in-reply-to :references:mime-version:content-transfer-encoding; bh=pca5WnLuEakeX7vZ3mPbL/KTprlx+NljA7wr+V6Ejpw=; b=GuWhbbTK7PS8+6YyKcCHEpo809XFx0iYA6IUtJi/2ZcCwyCLllxiW8zVYCx7xpL0i/ 5pWBlAeD/QfO2tl+UdOjvbnZbFfQ3lusAGPFiYAi9drmgcxT4Sy3s7YdBuXdvJC3K3qB B4NUqocp4jIoAeiDmCFEmTdTVmn+IU7X7yMNs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:organization :in-reply-to:references:mime-version:content-transfer-encoding; bh=pca5WnLuEakeX7vZ3mPbL/KTprlx+NljA7wr+V6Ejpw=; b=uc6u9w0NDwIe2SQ8KWWUuMFuXFAZAuevgz2cUHJfP9F/qhOS0FLwLBPsiiHxRsK0ZT ji/UmRRHUiuSZXHUgPPCyK0sa5wDj40xUAlQvyeRCfAwzG1yDW5fZLlxqFtRabyHYUTf OoVwFkddnaL8PT8Bes7cWuXkSkpyccH4ykbcjWPpHgDj16kS8OfIlx4Znsx9Ucs3mYM+ 7Tp9taI1xkjszWML3h1146kDKUAj5KEJHuP3IXlEg0x9w6N1bB+9vdOpo8CNJClfaLeN fDEpgUutDvFblQ4oLeYqP2VtEDw/6LNWdl6Ou84wgA60BiyA/J/OcXOnymiT1zcvGfPt iE4g== X-Gm-Message-State: AOAM5322uqQenCnjQWp/jGNWPYAhkI1hFynYls8yn592ckMERWxUBf31 VZnimfOisGf3J//G9BbXh7m71Q== X-Google-Smtp-Source: ABdhPJwB9orsnkK1Ng96Tf7DFd+qHgAzBMlWAy0KTvtM4+mAXtiUTCcOkamkR7S/wH5yegS/i39fyQ== X-Received: by 2002:a7b:c949:: with SMTP id i9mr5072695wml.168.1626360820124; Thu, 15 Jul 2021 07:53:40 -0700 (PDT) Received: from aivenronan.localnet (static-176-158-121-96.ftth.abo.bbox.fr. [176.158.121.96]) by smtp.gmail.com with ESMTPSA id t16sm6709879wrp.44.2021.07.15.07.53.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Jul 2021 07:53:39 -0700 (PDT) From: Ronan Dunklau To: David Rowley Cc: Ranier Vilela , PostgreSQL Developers , Dilip Kumar , James Coleman Subject: Re: [PATCH] Use optimized single-datum tuplesort in ExecSort Date: Thu, 15 Jul 2021 16:53:29 +0200 Message-ID: <13249598.64SMogt8gv@aivenronan> Organization: aiven In-Reply-To: References: <3177670.itZtoPt7T5@aivenronan> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart2221971.Zxo41UTgxu" Content-Transfer-Encoding: 7Bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --nextPart2221971.Zxo41UTgxu Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="iso-8859-1" Le jeudi 15 juillet 2021, 16:19:23 CEST David Rowley a =E9crit :>=20 > Ronan's latest results plus John's make me think there's no need to > separate out the node function as I did in v8. However, I do think v6 > could learn a little from v8. I think I'd rather see the sort method > determined in ExecInitSort() rather than ExecSort(). I think > minimising those few extra instructions in ExecSort() might help the > L1 instruction cache. >=20 I'm not sure I understand what you expect from moving that to ExecInitSort = ?=20 Maybe we should also implement the tuplesort_state initialization in=20 ExecInitSort ? (not the actual feeding and sorting of course). Please find attached a v9 just moving the flag setting to ExecInitSort, and= my=20 apologies if I misunderstood your point. =2D-=20 Ronan Dunklau --nextPart2221971.Zxo41UTgxu Content-Disposition: attachment; filename="v9-0001-Allow-Sort-nodes-to-use-the-fast-single-datum-tuples.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="v9-0001-Allow-Sort-nodes-to-use-the-fast-single-datum-tuples.patch" commit 2bf8db23c8ddb53f7dbcba33c60350bda9d703ea Author: Ronan Dunklau Date: Tue Jul 6 16:34:31 2021 +0200 Use optimized single-datum tuplesort in ExecSort Previously, with optimization was only done in ExecAgg. Doing this optimization on regular sort nodes provides for a nice performance improvement, when the input / output tuple has only one attribute. diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index b99027e0d7..861e4c6e9e 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -29,6 +29,10 @@ * which saves the results in a temporary file or memory. After the * initial call, returns a tuple from the file with each call. * + * The tuplesort can either occur on the whole tuple (this is the nominal + * case) or, when the input / output tuple consists of only one attribute, + * we switch to the tuplesort_*_datum API, optimized for that specific case. + * * Conditions: * -- none. * @@ -86,31 +90,63 @@ ExecSort(PlanState *pstate) outerNode = outerPlanState(node); tupDesc = ExecGetResultType(outerNode); - tuplesortstate = tuplesort_begin_heap(tupDesc, - plannode->numCols, - plannode->sortColIdx, - plannode->sortOperators, - plannode->collations, - plannode->nullsFirst, - work_mem, - NULL, - node->randomAccess); + /* + * Switch to the tuplesort_*_datum interface when we have only one + * key, as it is much more efficient especially when the type is + * pass-by-value. + */ + if (node->datumSort) + { + tuplesortstate = tuplesort_begin_datum(TupleDescAttr(tupDesc, 0)->atttypid, + plannode->sortOperators[0], + plannode->collations[0], + plannode->nullsFirst[0], + work_mem, + NULL, + node->randomAccess); + } + else + tuplesortstate = tuplesort_begin_heap(tupDesc, + plannode->numCols, + plannode->sortColIdx, + plannode->sortOperators, + plannode->collations, + plannode->nullsFirst, + work_mem, + NULL, + node->randomAccess); if (node->bounded) tuplesort_set_bound(tuplesortstate, node->bound); node->tuplesortstate = (void *) tuplesortstate; /* - * Scan the subplan and feed all the tuples to tuplesort. + * Scan the subplan and feed all the tuples to tuplesort, using either + * the _putdatum or _puttupleslot API as appropriate. */ - - for (;;) + if (node->datumSort) { - slot = ExecProcNode(outerNode); - - if (TupIsNull(slot)) - break; - - tuplesort_puttupleslot(tuplesortstate, slot); + for (;;) + { + slot = ExecProcNode(outerNode); + + if (TupIsNull(slot)) + break; + slot_getsomeattrs(slot, 1); + tuplesort_putdatum(tuplesortstate, + slot->tts_values[0], + slot->tts_isnull[0]); + } + } + else + { + for (;;) + { + slot = ExecProcNode(outerNode); + + if (TupIsNull(slot)) + break; + tuplesort_puttupleslot(tuplesortstate, slot); + } } /* @@ -150,9 +186,18 @@ ExecSort(PlanState *pstate) * next fetch from the tuplesort. */ slot = node->ss.ps.ps_ResultTupleSlot; - (void) tuplesort_gettupleslot(tuplesortstate, - ScanDirectionIsForward(dir), - false, slot, NULL); + if (node->datumSort) + { + ExecClearTuple(slot); + if (tuplesort_getdatum(tuplesortstate, ScanDirectionIsForward(dir), + &(slot->tts_values[0]), &(slot->tts_isnull[0]), NULL)) + ExecStoreVirtualTuple(slot); + } + else + (void) tuplesort_gettupleslot(tuplesortstate, + ScanDirectionIsForward(dir), + false, slot, NULL); + return slot; } @@ -191,6 +236,7 @@ ExecInitSort(Sort *node, EState *estate, int eflags) sortstate->bounded = false; sortstate->sort_Done = false; sortstate->tuplesortstate = NULL; + sortstate->datumSort = false; /* * Miscellaneous initialization @@ -220,6 +266,15 @@ ExecInitSort(Sort *node, EState *estate, int eflags) */ ExecInitResultTupleSlotTL(&sortstate->ss.ps, &TTSOpsMinimalTuple); sortstate->ss.ps.ps_ProjInfo = NULL; + + /* + * We perform a Datum sort when we're sorting just a single column, + * otherwise we perform a tuple sort. + */ + if (ExecGetResultType(outerPlanState(sortstate))->natts == 1) + sortstate->datumSort = true; + else + sortstate->datumSort = false; SO1_printf("ExecInitSort: %s\n", "sort node initialized"); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 0ec5509e7e..89f944a9c4 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2151,6 +2151,7 @@ typedef struct SortState int64 bound_Done; /* value of bound we did the sort with */ void *tuplesortstate; /* private state of tuplesort.c */ bool am_worker; /* are we a worker? */ + bool datumSort; /* are we using the single value optimization ? */ SharedSortInfo *shared_info; /* one entry per worker */ } SortState; --nextPart2221971.Zxo41UTgxu--