Optimize generate_orderedappend_paths
authorDavid Rowley <drowley@postgresql.org>
Mon, 20 Feb 2023 09:48:58 +0000 (22:48 +1300)
committerDavid Rowley <drowley@postgresql.org>
Mon, 20 Feb 2023 09:48:58 +0000 (22:48 +1300)
In generate_orderedappend_paths(), when match_partition_order_desc was
true, we would lcons() items to various lists in a loop over each live
partition.  When the number of live partitions was large, the lcons()
could show up in profiles due to it having to perform memmove() to make
way for the new list item.

Here we adjust things so that we just perform the loop over the live
partitions backwards when match_partition_order_desc is true.  This allows
us to simplify the logic in the loop.  Now, as far as the guts of the loop
knows, there's no difference between match_partition_order and
match_partition_order_desc.  We can just set match_partition_order to true
so that we build the correct list of paths for the asc and desc case. Per
idea from Andres Freund.

Discussion: https://postgr.es/m/20230217002351.nyt4y5tdzg6hugdt@awork3.anarazel.de

src/backend/optimizer/path/allpaths.c

index ae0f9bdc8a52297b6c08309b1b75dd86ea7d7734..4a330192f266015b00887de6fad3da31f451962e 100644 (file)
@@ -1700,9 +1700,11 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                List       *total_subpaths = NIL;
                List       *fractional_subpaths = NIL;
                bool            startup_neq_total = false;
-               ListCell   *lcr;
                bool            match_partition_order;
                bool            match_partition_order_desc;
+               int                     end_index;
+               int                     first_index;
+               int                     direction;
 
                /*
                 * Determine if this sort ordering matches any partition pathkeys we
@@ -1723,10 +1725,38 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                         (!partition_pathkeys_desc_partial &&
                          pathkeys_contained_in(partition_pathkeys_desc, pathkeys)));
 
+               /*
+                * When the required pathkeys match the reverse of the partition
+                * order, we must build the list of paths in reverse starting with the
+                * last matching partition first.  We can get away without making any
+                * special cases for this in the loop below by just looping backward
+                * over the child relations in this case.
+                */
+               if (match_partition_order_desc)
+               {
+                       /* loop backward */
+                       first_index = list_length(live_childrels) - 1;
+                       end_index = -1;
+                       direction = -1;
+
+                       /*
+                        * Set this to true to save us having to check for
+                        * match_partition_order_desc in the loop below.
+                        */
+                       match_partition_order = true;
+               }
+               else
+               {
+                       /* for all other case, loop forward */
+                       first_index = 0;
+                       end_index = list_length(live_childrels);
+                       direction = 1;
+               }
+
                /* Select the child paths for this ordering... */
-               foreach(lcr, live_childrels)
+               for (int i = first_index; i != end_index; i += direction)
                {
-                       RelOptInfo *childrel = (RelOptInfo *) lfirst(lcr);
+                       RelOptInfo *childrel = list_nth_node(RelOptInfo, live_childrels, i);
                        Path       *cheapest_startup,
                                           *cheapest_total,
                                           *cheapest_fractional = NULL;
@@ -1822,25 +1852,6 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                                        fractional_subpaths = lappend(fractional_subpaths, cheapest_fractional);
                                }
                        }
-                       else if (match_partition_order_desc)
-                       {
-                               /*
-                                * As above, but we need to reverse the order of the children,
-                                * because nodeAppend.c doesn't know anything about reverse
-                                * ordering and will scan the children in the order presented.
-                                */
-                               cheapest_startup = get_singleton_append_subpath(cheapest_startup);
-                               cheapest_total = get_singleton_append_subpath(cheapest_total);
-
-                               startup_subpaths = lcons(cheapest_startup, startup_subpaths);
-                               total_subpaths = lcons(cheapest_total, total_subpaths);
-
-                               if (cheapest_fractional)
-                               {
-                                       cheapest_fractional = get_singleton_append_subpath(cheapest_fractional);
-                                       fractional_subpaths = lcons(cheapest_fractional, fractional_subpaths);
-                               }
-                       }
                        else
                        {
                                /*
@@ -1859,7 +1870,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                }
 
                /* ... and build the Append or MergeAppend paths */
-               if (match_partition_order || match_partition_order_desc)
+               if (match_partition_order)
                {
                        /* We only need Append */
                        add_path(rel, (Path *) create_append_path(root,