mirror of
https://github.com/postgres/postgres.git
synced 2025-11-29 23:43:17 +03:00
Avoid O(N^2) behavior in SyncPostCheckpoint().
As in commits6301c3adaande9d9ba2a4, avoid doing repetitive list_delete_first() operations, since that would be expensive when there are many files waiting to be unlinked. This is a slightly larger change than in those cases. We have to keep the list state valid for calls to AbsorbSyncRequests(), so it's necessary to invent a "canceled" field instead of immediately deleting PendingUnlinkEntry entries. Also, because we might not be able to process all the entries, we need a new list primitive list_delete_first_n(). list_delete_first_n() is almost list_copy_tail(), but it modifies the input List instead of making a new copy. I found a couple of existing uses of the latter that could profitably use the new function. (There might be more, but the other callers look like they probably shouldn't overwrite the input List.) As before, back-patch to v13. Discussion: https://postgr.es/m/CD2F0E7F-9822-45EC-A411-AE56F14DEA9F@amazon.com
This commit is contained in:
@@ -906,6 +906,72 @@ list_delete_last(List *list)
|
||||
return list_truncate(list, list_length(list) - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the first N cells of the list.
|
||||
*
|
||||
* The List is pfree'd if the request causes all cells to be deleted.
|
||||
*/
|
||||
List *
|
||||
list_delete_first_n(List *list, int n)
|
||||
{
|
||||
check_list_invariants(list);
|
||||
|
||||
/* No-op request? */
|
||||
if (n <= 0)
|
||||
return list;
|
||||
|
||||
/* Delete whole list? */
|
||||
if (n >= list_length(list))
|
||||
{
|
||||
list_free(list);
|
||||
return NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, we normally just collapse out the removed elements. But for
|
||||
* debugging purposes, move the whole list contents someplace else.
|
||||
*
|
||||
* (Note that we *must* keep the contents in the same memory context.)
|
||||
*/
|
||||
#ifndef DEBUG_LIST_MEMORY_USAGE
|
||||
memmove(&list->elements[0], &list->elements[n],
|
||||
(list->length - n) * sizeof(ListCell));
|
||||
list->length -= n;
|
||||
#else
|
||||
{
|
||||
ListCell *newelems;
|
||||
int newmaxlen = list->length - n;
|
||||
|
||||
newelems = (ListCell *)
|
||||
MemoryContextAlloc(GetMemoryChunkContext(list),
|
||||
newmaxlen * sizeof(ListCell));
|
||||
memcpy(newelems, &list->elements[n], newmaxlen * sizeof(ListCell));
|
||||
if (list->elements != list->initial_elements)
|
||||
pfree(list->elements);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* As in enlarge_list(), clear the initial_elements[] space and/or
|
||||
* mark it inaccessible.
|
||||
*/
|
||||
#ifdef CLOBBER_FREED_MEMORY
|
||||
wipe_mem(list->initial_elements,
|
||||
list->max_length * sizeof(ListCell));
|
||||
#else
|
||||
VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements,
|
||||
list->max_length * sizeof(ListCell));
|
||||
#endif
|
||||
}
|
||||
list->elements = newelems;
|
||||
list->max_length = newmaxlen;
|
||||
list->length = newmaxlen;
|
||||
check_list_invariants(list);
|
||||
}
|
||||
#endif
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the union of two lists. This is calculated by copying
|
||||
* list1 via list_copy(), then adding to it all the members of list2
|
||||
|
||||
Reference in New Issue
Block a user