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 1vaDRH-005IZQ-29 for pgsql-hackers@arkaria.postgresql.org; Mon, 29 Dec 2025 13:37:36 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vaDRF-00GoUu-1m for pgsql-hackers@arkaria.postgresql.org; Mon, 29 Dec 2025 13:37:34 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vaDRF-00GoUl-01 for pgsql-hackers@lists.postgresql.org; Mon, 29 Dec 2025 13:37:34 +0000 Received: from mail-ej1-x62b.google.com ([2a00:1450:4864:20::62b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vaDRD-003BOB-1k for pgsql-hackers@lists.postgresql.org; Mon, 29 Dec 2025 13:37:32 +0000 Received: by mail-ej1-x62b.google.com with SMTP id a640c23a62f3a-b7ce5d6627dso1592765966b.2 for ; Mon, 29 Dec 2025 05:37:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=garret-ru.20230601.gappssmtp.com; s=20230601; t=1767015448; x=1767620248; darn=lists.postgresql.org; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=zyusrXLsXHDIrLhKKCtp6ITULzl6N9YPkEkzqQiro68=; b=Y2GscSS1NOeDFLwwVUY2sBEh6aRRnIvmxnv2cHNrbwUPGI+ThOrkDuK3FEQgBXzI6H mYphd/04Gz5w4aUNuxebE/HW+4Wdy0bZFtueIRfHjpJOOMq2G/E81v8V2TTw5wX+8JHQ XuOfD231rCSci3OZI4n8ry++zbANTwf4SS19yltingcI+GLxQXXJ/eJ88mcLtTbkglb9 u83cylRJW/zXbWCc5wGW2Et5MzQEBb9zkCjLT8w48NI67fT5ueWUUTYjHF+024xdjqfC H1T8k88I4JE+o9MJ1Y6Zn7Cna39YiQ1JljNLlGyruAt/1Ki214dtrsL3FLFiGeCcMqQy kpnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767015448; x=1767620248; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=zyusrXLsXHDIrLhKKCtp6ITULzl6N9YPkEkzqQiro68=; b=mT+M3kFmFHFIw/zvYy0FtRLMh249veJj3LSoV6POGDYo2R5p+idCZGG7R6fVlzIMJy ffeHwzwVUbxEPEDigW/vsskeIFkFKVqAhgM2c2J4sTFQgZc/qOvxkk2rENabvxyeLjXk r1SOuedCVnfBmiOaSEP/KfzJdrXnYS+LvZHby2KpScrh/BxGn+aqOk0R8MCmGN+teIsj UivsucmDirKOBVxcamPv+LQWjV0T4qJURR9rlzJIYMfLTIMqVyrEw+uZ9+iwUUQowu1/ 1HpYS95NNVOnzHYW2GHdEtO0+rrk/rHjRFQVv+kRXJQ9TRTb4bl0EWrd1TA+04KZTV2e rVEg== X-Forwarded-Encrypted: i=1; AJvYcCVf8jiTED1+TPkvo2gCviVzmyAFtBQI8toA+u/8HIr55DCRX0Bfhyc9qM2xaicEALPLnNbgwdA9aPi3N1dq@lists.postgresql.org X-Gm-Message-State: AOJu0YyZNSdSzBc5wy/9Kj3AuKjTIMJn8X9LIevdWSMkfYatPkvmk+JC quOaml9VTM7IuY8Yk8lUqQcRplncooxv5nUku7o/dSXtVugcWcMlxfyNwwsrA2aObg== X-Gm-Gg: AY/fxX6hDRoPDawX8CLrTw/dAhfAIMOJ+DMRIEY3M25JiYoWViA3nDhS001Brfhm9xc S7UJTGqdza/1enq/r9txlqUs61o+0v9GXbf+X4MwVnVSQTHfV4UGJUd3MC3teBFDbFYoANFd9Td RvLQIvUugikJXmuBudsTnAm2CPMvqT4ekKLrfyZLSoqCA5mRhI0fPCWkAHd7Quh6E1BF+7DREVZ ixzUAY+Rk9AbcQPRZrgDHW0bpW4oCv0gBF9KPHMCI5c+h/cNDTwT91QC/mOknQ8stVnAsiRpngr 1ThqKuDMAnfBSrYP16DgqzDuqKAtJl7ZjIATG8q4KSpbxwznDCZKmk4aeC75W0hQvAK5f+574f9 ziSct79UIB//uix8sAYzC9IEXBZKa3aRIebLaxveHEK+mkNLkdrtAw5PCoDKNTclnGJlBIZ4Hv5 yTXaQ3R5rdbayLN+NgySuASBZGsiHcPPU= X-Google-Smtp-Source: AGHT+IG3A9L1AkjuPhkpNyxXysew3F67VM7TTTIRWQs3dqhE1S7dAneaVn/gpjYlB1o+dnDtRBLPXQ== X-Received: by 2002:a17:907:9303:b0:b83:1340:1a2c with SMTP id a640c23a62f3a-b8313401b75mr1289687566b.64.1767015447786; Mon, 29 Dec 2025 05:37:27 -0800 (PST) Received: from [10.248.37.148] ([130.41.208.2]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b8037ddffd2sm3170752566b.33.2025.12.29.05.37.25 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 29 Dec 2025 05:37:26 -0800 (PST) Message-ID: <6ba8a743-2b58-4c5a-8a39-28ccc130a069@garret.ru> Date: Mon, 29 Dec 2025 15:37:24 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: index prefetching To: Tomas Vondra , Peter Geoghegan Cc: Andres Freund , Thomas Munro , Nazir Bilal Yavuz , Robert Haas , Melanie Plageman , PostgreSQL Hackers , Georgios , Dilip Kumar References: <6d59c277-c440-4d1f-a46e-157958c06a5f@vondra.me> <4zeu5yb73byiquvf3eefsunnrydyqfxy3eup66jrliutrtd4xl@5iifjey4n5m5> <8f123089-49ed-4c35-ab28-e3df70d28043@garret.ru> <69908539-3cc5-4572-b69f-055fa40d3bea@garret.ru> <95b6c6ef-4ba1-44f6-b1ac-eef0bbb5327a@vondra.me> Content-Language: en-US From: Konstantin Knizhnik In-Reply-To: <95b6c6ef-4ba1-44f6-b1ac-eef0bbb5327a@vondra.me> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On 29/12/2025 1:53 AM, Tomas Vondra wrote: > It seems this is due to sending an extra SET (for the new GUC) in the > pgbench script, which is recognized only on the v5+threshold build. > > That's a thinko on my side, I should have realized the extra command > might affect this. It doesn't really affect the behavior, because 10 is > the default value for read_stream_threshold. I've fixed the script, will > check fresh results tomorrow. > > Still, I think most of what I said about heuristics when to initialize > the read stream, and the risk/benefit tradeoff, still applies. I did a lot of experiments this morning but could not find any noticeable difference at any configuration when all working set fits in shared buffers. And frankly speaking after more thinking I do not see good reasons which can explain such difference. Just initialization of read stream should not add much overhead - it seems to be not expensive operation. What is actually matter is async IO. Without read stream, Postgres reads heap pages using sync operation: backend just calls pread. With read stream, AIO is used. By default "worker" AIO mode is used, it means that backend sends request to one of the workers and wait for it's completion. Worker receives request, performs IO and notifies backend. Such interprocess communication adds significant overhead and this is why if we initialize read stream from the very beginning, then we get about ~4x worse performance with LIMIT 1. Please correct me if I wrong (or it is Mac specific), but it is not caused by any overhead related with read_stream, but by AIO. I have not made such experiment, but it seems to me that if we make read stream to perform sync calls, then there will be almost no difference in performance. When all data is cached in shared buffers, then we do not perform IO at all. It means there it doesn't matter whether and when we initialize read_stream. We can do it after processing 10 items (current default), or immediately - it should not affect performance. And this is what I have tested: performance actually not depends on `read_stream_threshold` (if data fits in shared buffers). At least it is within few percents and may be it is just random fluctuations. Obviously there is no 25% degradation. It definitely doesn't mean that it is not possible to find scenario where this approach with enabling prefetch after processing N items will show worse performance than master or v5. We just need to properly choose cache hit rate. But the same is true IMHO for v5 itself: it is possible to find workload where it will show the same degradation comparing with master. More precise heuristic should IMHO take in account actual number of performed disk read. Please notice that I do not want to predict number of disk reads - i.e. check if candidates for prefetch are present in shared buffers. It will really adds significant overhead. I think that it is better to use as threshold number of performed reads. Unfortunately looks like it is not possible to accumulate such information without changing other Postgres code. For example, if `ReadBuffer` can somehow inform caller that it actually  performs read, then it can be easily calculate number of reads in `heapam_index_fetch_tuple`: ``` static pg_attribute_always_inline Buffer ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,                   ForkNumber forkNum,                   BlockNumber blockNum, ReadBufferMode mode,                   BufferAccessStrategy strategy,                   bool* fast_path) {      ...     if (StartReadBuffer(&operation,                         &buffer,                         blockNum,                         flags))     {         WaitReadBuffers(&operation);         *fast_path = false;     }     else          *fast_path = true;     return buffer; } It can be certainly achieved without changed ReadBuffer* family, just by directly calling StartReadBuffer from `heapam_index_fetch_tuple` instead of `ReadBuffer`. Not so nice because we have to duplicate some bufmgr code. Not so much - check for local relation and filling `ReadBuffersOperation`structure. But it is better to avoid it.