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 1sRYiO-00GTKZ-LG for pgsql-hackers@arkaria.postgresql.org; Wed, 10 Jul 2024 14:54:40 +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 1sRYiN-005zsA-3x for pgsql-hackers@arkaria.postgresql.org; Wed, 10 Jul 2024 14:54:39 +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 1sRYiM-005zs2-Og for pgsql-hackers@lists.postgresql.org; Wed, 10 Jul 2024 14:54:38 +0000 Received: from mail-lf1-x135.google.com ([2a00:1450:4864:20::135]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1sRYiK-001PEv-19 for pgsql-hackers@postgresql.org; Wed, 10 Jul 2024 14:54:38 +0000 Received: by mail-lf1-x135.google.com with SMTP id 2adb3069b0e04-52eafec1e84so4307237e87.0 for ; Wed, 10 Jul 2024 07:54:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720623275; x=1721228075; 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=1rOQPqMuEAMm/ENtlUcIKOklHZiF55qtwjVj4CNKh7o=; b=cWV//RnaSlgYLuBo/Z8AMvdY/nmygfS6+tE1UGfKB4h7KjWgBV9HzxfQVnN3j+TBSu cD/ToY+chu1Lb3uFkyLLl80pyRtLc5SAvIWhvf3/m2km7MmsGgkjy/e2QpeF73Tm8U8t Pk1IeINewH0FRIgje94VHn17DBQlIB2nWWPWwvYiJ8w9JoA0VxCWNNBh/9noeyvrrhim APbh1cWZokjHOr3/3TySgTndopv5UujWJvFYMuiWr8lAjLPaIs/f45wyvY1lySokbrEw grnRVscIGoD99o25tmG6JPS9n23fZUfRvkFWAj682gCqlR6Ntvf4i2Z/NHTn/n9doJa0 HdBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720623275; x=1721228075; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1rOQPqMuEAMm/ENtlUcIKOklHZiF55qtwjVj4CNKh7o=; b=pokRGXduUqRU6ZRVg9n6unjYJntWWCk3PnyACWA+yU0HxhRSBGrjdQql/LMRK1tbto 4IHeGrAXWgtvX+Y3yRR7zyvMLiCXAPNm8+XesWzni4abWrH1+awE3foSCGPWdz+bES8W vS+sOKgr6/q/spzGhw22MMf3HcC2SBuXU+apjYAHnFp6MmhRYF1UTZOZkheJv3ZMKyeQ I3IE0ESrQ/q9/gUU1nmaroEvA50PQ32/8py0CLHuQgL3NJ+yJqMPUpSX7nADVWgkQTVx nvTGx9kFExPVeuzQ/70dGyx+/QYM2vOMfNQ4o/OLxQcFOFW+ZjW/zv2whJksww8jc/m3 y5bg== X-Gm-Message-State: AOJu0Yyp5X+DufAFTHuLE4s5XCSK5tG15WvRfM/b+0kursuDe+8YCYe/ K8qd3O+//Ppe+wFxuah+GkdoTSR2O6Ww4v+2sm5EgEEM2lc52pFuJpCxmiJu79ATVQN6IKh8lH4 kSrdPffEK1AXxFfdA3roJq944d2A= X-Google-Smtp-Source: AGHT+IEubyHStCR0bJOi9qhH6jrgbM23wOrtzeBV8r0HbisB8TKf2mtU7CULeT9kUYiNF9YUT5AXfQM0h2d+jfsA1rs= X-Received: by 2002:a05:6512:3190:b0:52c:8a39:83d7 with SMTP id 2adb3069b0e04-52eb99d15c2mr4400752e87.52.1720623274593; Wed, 10 Jul 2024 07:54:34 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Rafia Sabih Date: Wed, 10 Jul 2024 16:54:22 +0200 Message-ID: Subject: Re: Possible incorrect row estimation for Gather paths To: Anthonin Bonnefoy Cc: PostgreSQL Hackers 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 Hello Anthonin, I spent some time on this problem and your proposed solution. As I understand it, this is the correction for the row count when the number of parallel workers < 4. Once the number of workers is 4 or more, the output from parallel_divisor is the same as the number of parallel workers. And then the number of rows for such cases would be the same with or without the proposed patch. So that way I think it is good to fix this case for a smaller number of wor= kers. But I don't quite understood the purpose of this, + total_groups =3D input_rel->rows; + + /* + * If the number of rows is unknown, fallback to gather rows + * estimation + */ + if (total_groups =3D=3D 0) + total_groups =3D gather_rows_estimate(input_path); why not just use total_groups =3D gather_rows_estimate(input_path), what is the importance of having total_groups =3D input_rel->rows? With respect to the change introduced by the patch in the regression test, I wonder if we should test it on the tables of a larger scale and check for performance issues. Imagine the case when Parallel Seq Scan on extremely_skewed s (cost=3D0.00..167.01 rows=3D1 width=3D4) returns 1000 rows instead of 1 ... I wonder which plan would perform better then or will there be a totally different plan. However, my hunch is that there should not be serious problems, because before this patch the number of estimated rows was incorrect anyway. I don't see a problem in merging the two patches. On Fri, 24 May 2024 at 11:35, Anthonin Bonnefoy wrote: > > Hi, > > While experimenting on an explain option to display all plan candidates (= very rough prototype here [1]), I've noticed some discrepancies in some gen= erated plans. > > EXPLAIN (ALL_CANDIDATES) SELECT * FROM pgbench_accounts order by aid; > Plan 1 > -> Gather Merge (cost=3D11108.32..22505.38 rows=3D100000 width=3D97) > Workers Planned: 1 > -> Sort (cost=3D10108.31..10255.37 rows=3D58824 width=3D97) > Sort Key: aid > -> Parallel Seq Scan on pgbench_accounts (cost=3D0.00..2228.= 24 rows=3D58824 width=3D97) > Plan 2 > -> Gather Merge (cost=3D11108.32..17873.08 rows=3D58824 width=3D97) > Workers Planned: 1 > -> Sort (cost=3D10108.31..10255.37 rows=3D58824 width=3D97) > Sort Key: aid > -> Parallel Seq Scan on pgbench_accounts (cost=3D0.00..2228.= 24 rows=3D58824 width=3D97) > > The 2 plans are similar except one Gather Merge has a lower 58K estimated= rows. > > The first plan is created with generate_useful_gather_paths with override= _rows=3Dfalse then create_gather_merge_path and will use rel->rows as the r= ow count (so the 100K rows of pgbench_accounts). > #0: create_gather_merge_path(...) at pathnode.c:1885:30 > #1: generate_useful_gather_paths(... override_rows=3Dfalse) at allpaths.c= :3286:11 > #2: apply_scanjoin_target_to_paths(...) at planner.c:7744:3 > #3: grouping_planner(...) at planner.c:1638:3 > > The second plan is created through create_ordered_paths then create_gathe= r_merge_path and the number of rows is estimated to (worker_rows * parallel= _workers). Since we only have 1 parallel worker, this yields 58K rows. > #0: create_gather_merge_path(...) at pathnode.c:1885:30 > #1: create_ordered_paths(...) at planner.c:5287:5 > #2: grouping_planner(...) at planner.c:1717:17 > > The 58K row estimate looks possibly incorrect. A worker row count is esti= mated using total_rows/parallel_divisor. The parallel_divisor will include = the possible leader participation and will be 1.7 for 1 worker thus the 58K= rows (100K/1.7=3D58K) > However, the gather merge will only do 58K*1, dropping the leader partici= pation component. > > I have a tentative patch split in two changes: > 1: This is a related refactoring to remove an unnecessary and confusing a= ssignment of rows in create_gather_merge_path. This value is never used and= immediately overwritten in cost_gather_merge > 2: This changes the row estimation of gather path to use worker_rows*para= llel_divisor to get a more accurate estimation. Additionally, when creating= a gather merge path in create_ordered_paths, we try to use the source's re= l rows when available. > > The patch triggered a small change in the hash_join regression test. Pre = patch, we had the following candidates. > Plan 4 > -> Aggregate (cost=3D511.01..511.02 rows=3D1 width=3D8) > -> Gather (cost=3D167.02..511.00 rows=3D2 width=3D0) > Workers Planned: 1 > -> Parallel Hash Join (cost=3D167.02..510.80 rows=3D1 width= =3D0) > Hash Cond: (r.id =3D s.id) > -> Parallel Seq Scan on simple r (cost=3D0.00..299.65 = rows=3D11765 width=3D4) > -> Parallel Hash (cost=3D167.01..167.01 rows=3D1 width= =3D4) > -> Parallel Seq Scan on extremely_skewed s (cost= =3D0.00..167.01 rows=3D1 width=3D4) > Plan 5 > -> Finalize Aggregate (cost=3D510.92..510.93 rows=3D1 width=3D8) > -> Gather (cost=3D510.80..510.91 rows=3D1 width=3D8) > Workers Planned: 1 > -> Partial Aggregate (cost=3D510.80..510.81 rows=3D1 width= =3D8) > -> Parallel Hash Join (cost=3D167.02..510.80 rows=3D1 = width=3D0) > Hash Cond: (r.id =3D s.id) > -> Parallel Seq Scan on simple r (cost=3D0.00..2= 99.65 rows=3D11765 width=3D4) > -> Parallel Hash (cost=3D167.01..167.01 rows=3D1= width=3D4) > -> Parallel Seq Scan on extremely_skewed s = (cost=3D0.00..167.01 rows=3D1 width=3D4) > > Post patch, the plan candidates became: > Plan 4 > -> Finalize Aggregate (cost=3D511.02..511.03 rows=3D1 width=3D8) > -> Gather (cost=3D510.80..511.01 rows=3D2 width=3D8) > Workers Planned: 1 > -> Partial Aggregate (cost=3D510.80..510.81 rows=3D1 width= =3D8) > -> Parallel Hash Join (cost=3D167.02..510.80 rows=3D1 = width=3D0) > Hash Cond: (r.id =3D s.id) > -> Parallel Seq Scan on simple r (cost=3D0.00..2= 99.65 rows=3D11765 width=3D4) > -> Parallel Hash (cost=3D167.01..167.01 rows=3D1= width=3D4) > -> Parallel Seq Scan on extremely_skewed s = (cost=3D0.00..167.01 rows=3D1 width=3D4) > Plan 5 > -> Aggregate (cost=3D511.01..511.02 rows=3D1 width=3D8) > -> Gather (cost=3D167.02..511.00 rows=3D2 width=3D0) > Workers Planned: 1 > -> Parallel Hash Join (cost=3D167.02..510.80 rows=3D1 width= =3D0) > Hash Cond: (r.id =3D s.id) > -> Parallel Seq Scan on simple r (cost=3D0.00..299.65 = rows=3D11765 width=3D4) > -> Parallel Hash (cost=3D167.01..167.01 rows=3D1 width= =3D4) > -> Parallel Seq Scan on extremely_skewed s (cost= =3D0.00..167.01 rows=3D1 width=3D4) > > The FinalizeAggregate plan has an increased cost of 1 post patch due to t= he number of rows in the Gather node that went from 1 to 2 (rint(1 * 1.7)= =3D2). This was enough to make the Agggregate plan cheaper. The test is to = check the parallel hash join so updating it with the new cheapest plan look= ed fine. > > Regards, > Anthonin > > [1]: https://github.com/bonnefoa/postgres/tree/plan-candidates --=20 Regards, Rafia Sabih