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.96) (envelope-from ) id 1vnIk6-004qFg-1f for pgsql-hackers@arkaria.postgresql.org; Tue, 03 Feb 2026 15:55:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vnIk5-005WEH-0a for pgsql-hackers@arkaria.postgresql.org; Tue, 03 Feb 2026 15:55:05 +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.96) (envelope-from ) id 1vnIk4-005WE9-2l for pgsql-hackers@lists.postgresql.org; Tue, 03 Feb 2026 15:55:04 +0000 Received: from mail-ed1-x531.google.com ([2a00:1450:4864:20::531]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vnIk2-00000000rMN-1zBh for pgsql-hackers@postgresql.org; Tue, 03 Feb 2026 15:55:04 +0000 Received: by mail-ed1-x531.google.com with SMTP id 4fb4d7f45d1cf-64b9cb94ff5so7544879a12.2 for ; Tue, 03 Feb 2026 07:55:02 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1770134101; cv=none; d=google.com; s=arc-20240605; b=H6KEj82bBhM/DA7FQuMckL36hC4DRnai3GgsR4cLghHw/VjYPIbcjQNcIiCTKmBuY9 iP0aeeg9NGos12SCLNhDF/ClegpXc0Jq02AfVo9rncJDLGnn4yrOEZqo7Gt8vyV5SIzp d4PIsMmguuK/LdfiB7mfbVqGVfK/TLuG0JBwFGWOLMi8+4E0FAPCQYjsW2GYrtyRQqdB j7KcMK+rEYUnltBVbqjjppGAOYeTposBo48Q+NycFrnU+E5aLh6fn1Kr41qhMdz6iVtX Sgcswi0GAEQKipoAFpnlOj7Uh9YgvBp2v6BYF8yiATNJRmJ7mFIRNSWX76UKvoEv+Mtr TvBg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=kSzfRxvUbKCf/OxiOeVhV/20kVbWGhFfDVWvQ5dfDSw=; fh=I/dYi20i3FyE77xK1Aj73LqqIxh39laBgX5B9G6/SxM=; b=IsEx/ZjYTFz/+A0KmDtQ2FWB8I/DN/Ca40cucSaE183xp4nxhEYXXC8Nd+3KLYfr/U gfSfeZVcRTBH2FEGhnJSoIEcMIPge5UEPY5KcLoLLBuZX4S3rhgLu3yzRO7CDXtov1dj zDv/Ryx9Lc1wnpmKmPeCuhDGwvWSSENQz78+XcMXCZTAFWeuKVYAUEc4+0sJZ99bUu06 qa7xgiulEFzFTtNI1GMSxGwUdni8EloIqWrdDAzzdAEh/2seKKdY508qGiqggfgMLeHC z2BJvp58eUq6mBY5YNQaWv1TjO+CTYobJhkordpmct2wKlvLmlkC2KrXJY8Hu4v0VQm1 fzHQ==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770134101; x=1770738901; darn=postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=kSzfRxvUbKCf/OxiOeVhV/20kVbWGhFfDVWvQ5dfDSw=; b=AyGWQLPqVsWEJvcHxlCj5b8pFmiHZndvObEazd6+3etw0MhFUd3XqbjE1ewqknhIXm uMb/aMiAe4xABrPP52pcm7y2nBOQXj2cj50mXSgW7amq3u/PN54S+SHz47u0E7cfdmm/ 3+9IXTitfvn0hyhxNnT5jxP3xTRujvKgzy/fYToWq9+x7kAgQskMNSk0GdDM9hFZEU4k 6F9SIQlNMm2o3pWxosKtEN8p8JXwwq4BCjKFCOPKb1lWYYyJcwVU94Qso5PdPBY94w+T FOQjKeCXB2crx2M2FAC+tS/O80pmzwJj9WP7d2DfgpAIgZceqVLNJYexnrTaZ9fgDGny yw4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770134101; x=1770738901; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kSzfRxvUbKCf/OxiOeVhV/20kVbWGhFfDVWvQ5dfDSw=; b=jKCzGX+BuYAi91flLe5btRsaJGo7lU/fRpXfcHkm8RZjTiQMM5XPGlhFgfXs94bF3Z VIY6qkvTA8rw3LJdOCdL+qAY9fuAYhzmWdeDHKXq1+BJDASfzcr5vf4z90vVvEe5xPdu VSyutxKVoFMBbJszDbO6XvXS5VDSR+M4Pkja3dlAkVy8wYKTci2da+wX0SsMTUIXFNsF Q/ADWXrZcFGvu4wJnhCey0Eyl3c27dzo+6hoOYCndS+XF7tIQDDHzKaNSEixxldgmzs5 17PpfJoAfl18ZwWPHeeUbevh+tBz5QWtQSCHLKQ965baMlRdHF+PxAQ/toY90CXdvytg bqZA== X-Forwarded-Encrypted: i=1; AJvYcCXVi2TS2c+JTqYNy3fTvSn6Ph8Z+ruRKkzU3X3GkqfDeFKCK82uH287bfR5s6jcRidiFrudf20dWzBxxvkm@postgresql.org X-Gm-Message-State: AOJu0YxEbrX5S9ymCHAbV0qDho/7fMFX8p0RzMIXFEqClMrEqa0n2i76 pjd7Xgp4GFDmCkXf6Pxs3km0U12zsL+Ib34FKA0nXuvgiHE55+qy5ilD6AsfAkUzY5bercl3Lp2 urbENz48Sv7nBBSCFkCB/+2FFOZwwQrI= X-Gm-Gg: AZuq6aJJsxqbrco8A/96rmnl4aINvf4/jRIfCnq7O0kTKcMPux0sXn5X8nb4N4RHh5q NYXLAebQ7S/wEvPGIuxfPPds/J6R7EUqCY4CUl/TOmdHUIXZmPqF0PK+ALNb+yU2iwDv463ba27 Z1Hu9tCL1cpNB3LUL8AbtgeuQSfSKSu6MZUnOBqGFQMsoimfGYoE0ev/oInOd+fqi3/acQbiBQT kJMO2YUORo3szCeeT5pgzYl1AQ+o59C3jtfjZ9+2QNaUCUKq1n1wirPR6LN1DsXtp5LIPzKlEXV RSNvRnk1qjTfuZbjp6U5LtgREz35/MiM4y2iHIdyrVKewr/rhGI08ywMJcrUJRkDh6Sc X-Received: by 2002:a05:6402:2787:b0:650:8563:fdee with SMTP id 4fb4d7f45d1cf-65949dc9609mr52042a12.25.1770134100882; Tue, 03 Feb 2026 07:55:00 -0800 (PST) MIME-Version: 1.0 References: <68f3771f-91f5-4cb7-b1de-74d9abbf0b96@vondra.me> In-Reply-To: From: Junwang Zhao Date: Tue, 3 Feb 2026 23:54:48 +0800 X-Gm-Features: AZwV_QizR0hk-6RYWSkoJg6HkUnq_U3uG93JcDIx7nj10J_M_DUGMxA7YrTRdeY Message-ID: Subject: Re: Batching in executor To: cca5507 Cc: Amit Langote , Daniil Davydov <3danissimo@gmail.com>, PostgreSQL-development , Tomas Vondra Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Tue, Feb 3, 2026 at 9:30=E2=80=AFPM cca5507 wrote: > > Hi, > > Some comments for v5: > > 0001 > =3D=3D=3D=3D > > 1) heap_begin_batch() > > ``` > /* Single allocation for HeapBatch header + tupdata array */ > alloc_size =3D sizeof(HeapBatch) + sizeof(HeapTupleData) * maxite= ms; > hb =3D palloc(alloc_size); > hb->tupdata =3D (HeapTupleData *) ((char *) hb + sizeof(HeapBatch= )); > ``` > > Do we need a MAXALIGN() here to avoid unaligned access? Something like th= is: TBH I don't think this single allocation helps too much, it's not on the hot path, but makes the code harder to read ;( > > ``` > /* Single allocation for HeapBatch header + tupdata array */ > alloc_size =3D MAXALIGN(sizeof(HeapBatch)) + sizeof(HeapTupleData= ) * maxitems; > hb =3D palloc(alloc_size); > hb->tupdata =3D (HeapTupleData *) ((char *) hb + MAXALIGN(sizeof(= HeapBatch))); > ``` > > Or how about just using zero-length array: > > ``` > typedef struct HeapBatch > { > Buffer buf; > int maxitems; > int nitems; > HeapTupleData tupdata[FLEXIBLE_ARRAY_MEMBER]; > } HeapBatch; > > // and > hb =3D palloc(offsetof(HeapBatch, tupdata) + sizeof(HeapTupleData) * maxi= tems); > ``` > > 2) pgstat_count_heap_getnext_batch() > > ``` > #define pgstat_count_heap_getnext_batch(rel, n) = \ > do { = \ > if (pgstat_should_count_relation(rel)) = \ > (rel)->pgstat_info->counts.tuples_returned +=3D n= ; \ > } while (0) > ``` > > "+=3D n" -> "+=3D (n)", just like pgstat_count_index_tuples(). > > 0002 > =3D=3D=3D=3D > > 1) TupleBatchCreate() > > ``` > /* Single allocation for TupleBatch + inslots + outslots arrays *= / > alloc_size =3D sizeof(TupleBatch) + 2 * sizeof(TupleTableSlot *) = * capacity; > b =3D palloc(alloc_size); > inslots =3D (TupleTableSlot **) ((char *) b + sizeof(TupleBatch))= ; > outslots =3D (TupleTableSlot **) ((char *) b + sizeof(TupleBatch)= + > sizeof(TupleTableSlot *) * capacity); > ``` > > Do we need a MAXALIGN() here to avoid unaligned access? > > 2) TupleBatchReset() > > ``` > for (int i =3D 0; i < b->maxslots; i++) > { > ExecClearTuple(b->inslots[i]); > if (drop_slots) > ExecDropSingleTupleTableSlot(b->inslots[i]); > } > ``` > > ExecDropSingleTupleTableSlot() will call ExecClearTuple(), so ExecClearTu= ple() will be > called twice if drop_slots is true, I think we can avoid this. > > 3) ScanCanUseBatching() > > In heap_beginscan(), we may disable page-at-a-time mode: > > ``` > /* > * Disable page-at-a-time mode if it's not a MVCC-safe snapshot. > */ > if (!(snapshot && IsMVCCSnapshot(snapshot))) > scan->rs_base.rs_flags &=3D ~SO_ALLOW_PAGEMODE; > ``` > > It seems that ScanCanUseBatching() didn't consider this. > > 4) struct TupleBatch > > ``` > struct TupleTableSlot **inslots; /* slots for tuples read "into" = batch */ > struct TupleTableSlot **outslots; /* slots for tuples going "out = of" > = * batch */ > struct TupleTableSlot **activeslots; > ``` > > I think we can remove the word "struct". > > 5) ExecScanExtendedBatchSlot() > > ``` > /* Get next input slot from current batch, or refill */ > if (!TupleBatchHasMore(b)) > { > if (!accessBatchMtd(node)) > return NULL; > } > ``` > > I think we cannot just return NULL here, see comments in ExecScanExtended= (): > > ``` > /* > * if the slot returned by the accessMtd contains NULL, t= hen it means > * there is nothing more to scan so we just return an emp= ty slot, > * being careful to use the projection result slot so it = has correct > * tupleDesc. > */ > if (TupIsNull(slot)) > { > if (projInfo) > return ExecClearTuple(projInfo->pi_state.= resultslot); > else > return slot; > } > ``` > > And why not just write this function like ExecScanExtended() and ExecScan= Fetch()? > > -- > Regards, > ChangAo Chen --=20 Regards Junwang Zhao