Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n5oi9-00004s-01 for pgsql-hackers@arkaria.postgresql.org; Fri, 07 Jan 2022 12:51:13 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1n5oi7-0004Ul-A4 for pgsql-hackers@arkaria.postgresql.org; Fri, 07 Jan 2022 12:51:11 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n5oi6-0004Ub-Up for pgsql-hackers@lists.postgresql.org; Fri, 07 Jan 2022 12:51:11 +0000 Received: from mail-wr1-x431.google.com ([2a00:1450:4864:20::431]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1n5oi4-0006Y7-0Y for pgsql-hackers@lists.postgresql.org; Fri, 07 Jan 2022 12:51:10 +0000 Received: by mail-wr1-x431.google.com with SMTP id e9so9384343wra.2 for ; Fri, 07 Jan 2022 04:51:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aiven.io; s=google; h=from:to:cc:subject:date:message-id:organization:in-reply-to :references:mime-version:content-transfer-encoding; bh=LEJVUE/N0u2RPSNtI0PknDmqyHCBaPGMIywSSmanLy4=; b=XikEVLWfPfFxBhc5AswWLQVFfgG5dR3nt13x0UYXVlAAAGhptwtLJbBTCV9dO8tNDL 9Hjy5WIqpMX7i+kb23IX9iIFnTgb/eLf0+D/IP5qbGV8ln0PpDjl4aLCXAPAElU+jceX InbqTWRbC1KGt9PkAFmyMpsDnRTxRjQDJYvxk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:organization :in-reply-to:references:mime-version:content-transfer-encoding; bh=LEJVUE/N0u2RPSNtI0PknDmqyHCBaPGMIywSSmanLy4=; b=hPNKOh1pIgO9vkWqgKOF10CigBUjhTkZZ8CPgqJ+BPAxoi5wocCI+nC7AI0liRETSR bsHHHqNJPwqP8ARHk/IMsxweLfBUIVB9oeTxCj+DVwVLeVWKPa4CdSJe1RkxX07xWw8Q PSuP/6VHDDoc1UrLOAM7VDaw5dq0lTbvirTMEd+o4N1RoBE89ELTO/kNkA3UspQ4nPQN oUZpFN93JQzdg/2tDkg+3TCf/WQ5iId3O19TtMmVNWqGTw0PFE/BYGXI27qlmbyQ0aTM WTCaitn0pXAojQHIlRRCKajq7lN2IW9HPb1ftU5nm7sCi8BJRmutysNlOvm9Ic9dvgO1 q/PQ== X-Gm-Message-State: AOAM530qvGMieqqYQlyRDqqtpf5rK8MRcaCi4usLNYC0yLPUhUiEPSI7 5PDhFFhwCxYqIVSDstZEjn9Ar1eR6YQHNA== X-Google-Smtp-Source: ABdhPJxfYf1vOLQPJlRmqjiUYu9B2209zK8Wnc9P9QMaTXOLXcPRJrNEpm3Ws6FMGmmvlanXQuq0Hg== X-Received: by 2002:a5d:6502:: with SMTP id x2mr42188673wru.90.1641559866377; Fri, 07 Jan 2022 04:51:06 -0800 (PST) Received: from aivenronan.localnet (static-176-158-121-96.ftth.abo.bbox.fr. [176.158.121.96]) by smtp.gmail.com with ESMTPSA id bd21sm2295791wmb.8.2022.01.07.04.51.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jan 2022 04:51:06 -0800 (PST) From: Ronan Dunklau To: pgsql-hackers@lists.postgresql.org Cc: Andres Freund , Tomas Vondra , David Rowley , Tomas Vondra Subject: Re: Use generation context to speed up tuplesorts Date: Fri, 07 Jan 2022 13:49:53 +0100 Message-ID: <2096567.Mh6RI2rZIc@aivenronan> Organization: aiven In-Reply-To: <94e34870-9e3b-8c49-6617-016253129f06@enterprisedb.com> References: <3082578.5fSG56mABF@aivenronan> <94e34870-9e3b-8c49-6617-016253129f06@enterprisedb.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="iso-8859-1" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Le vendredi 7 janvier 2022, 13:03:28 CET Tomas Vondra a =E9crit : > On 1/7/22 12:03, Ronan Dunklau wrote: > > Le vendredi 31 d=E9cembre 2021, 22:26:37 CET David Rowley a =E9crit : > >> I've attached some benchmark results that I took recently. The > >> spreadsheet contains results from 3 versions. master, master + 0001 - > >> 0002, then master + 0001 - 0003. The 0003 patch makes the code a bit > >> more conservative about the chunk sizes it allocates and also tries to > >> allocate the tuple array according to the number of tuples we expect > >> to be able to sort in a single batch for when the sort is not > >> estimated to fit inside work_mem. > >=20 > > (Sorry for trying to merge back the discussion on the two sides of the > > thread) > >=20 > > In https://www.postgresql.org/message-id/4776839.iZASKD2KPV%40aivenron= an, > > I expressed the idea of being able to tune glibc's malloc behaviour. > >=20 > > I implemented that (patch 0001) to provide a new hook which is called on > > backend startup, and anytime we set work_mem. This hook is # defined > > depending on the malloc implementation: currently a default, no-op > > implementation is provided as well as a glibc's malloc implementation. >=20 > Not sure I'd call this a hook - that usually means a way to plug-in > custom code through a callback, and this is simply ifdefing a block of > code to pick the right implementation. Which may be a good way to do > that, just let's not call that a hook. >=20 > There's a commented-out MallocTuneHook() call, probably not needed. Ok, I'll clean that up if we decide to proceed with this. >=20 > I wonder if #ifdefing is sufficient solution, because it happens at > compile time, so what if someone overrides the allocator in LD_PRELOAD? > That was a fairly common way to use a custom allocator in an existing > application. But I don't know how many people do that with Postgres (I'm > not aware of anyone doing that) or if we support that (it'd probably > apply to other stuff too, not just malloc). So maybe it's OK, and I > can't think of a better way anyway. I couldn't think of a better way either, maybe there is something to be don= e=20 with trying to dlsym something specific to glibc's malloc implementation ? >=20 > > The glibc's malloc implementation relies on a new GUC, > > glibc_malloc_max_trim_threshold. When set to it's default value of -1, = we > > don't tune malloc at all, exactly as in HEAD. If a different value is > > provided, we set M_MMAP_THRESHOLD to half this value, and M_TRIM_TRESHO= LD > > to this value, capped by work_mem / 2 and work_mem respectively. > >=20 > > The net result is that we can then allow to keep more unused memory at = the > > top of the heap, and to use mmap less frequently, if the DBA chooses to= o. > > A possible other use case would be to on the contrary, limit the > > allocated memory in idle backends to a minimum. > >=20 > > The reasoning behind this is that glibc's malloc default way of handling > > those two thresholds is to adapt to the size of the last freed mmaped > > block. > >=20 > > I've run the same "up to 32 columns" benchmark as you did, with this new > > patch applied on top of both HEAD and your v2 patchset incorporating > > planner estimates for the block sizez. Those are called "aset" and > > "generation" in the attached spreadsheet. For each, I've run it with > > glibc_malloc_max_trim_threshold set to -1, 1MB, 4MB and 64MB. In each c= ase > >=20 > > I've measured two things: > > - query latency, as reported by pgbench > > - total memory allocated by malloc at backend ext after running each > > query > >=20 > > three times. This represents the "idle" memory consumption, and thus wh= at > > we waste in malloc inside of releasing back to the system. This > > measurement has been performed using the very small module presented in > > patch 0002. Please note that I in no way propose that we include this > > module, it was just a convenient way for me to measure memory footprint. > >=20 > > My conclusion is that the impressive gains you see from using the > > generation context with bigger blocks mostly comes from the fact that we > > allocate bigger blocks, and that this moves the mmap thresholds > > accordingly. I wonder how much of a difference it would make on other > > malloc implementation: I'm afraid the optimisation presented here would > > in fact be specific to glibc's malloc, since we have almost the same > > gains with both allocators when tuning malloc to keep more memory. I > > still think both approaches are useful, and would be necessary. > Interesting measurements. It's intriguing that for generation contexts, > the default "-1" often outperforms "1MB" (but not the other options), > while for aset it's pretty much "the higher value the better". =46or generation context with "big block sizes" this result is expected, as= the=20 malloc dynamic tuning will adapt to the big block size. This can also be se= en=20 on the "idle memory" measurement: the memory consumption is identical to th= e=20 64MB value when using -1, since that's what we converge to. This makes it=20 possible to configure postgres to be more conservative with memory: for=20 example, if we have long lived backend where we sometime temporarily set=20 work_mem to a high value, we may end up with a large memory foot print. The= =20 implementation I provide also requests a malloc trim when we lower the=20 threshold, making it possible to release memory that would have otherwise b= een=20 kept around forever.=20 =46or aset, the memory allocation pattern is a bit more complicated, and we= =20 don't end up with such a high value for mmap_threshold.=20 Also, one thing that I haven't explained yet is the weird outlier when ther= e=20 is only one column.=20 >=20 > > Since this affects all memory allocations, I need to come up with other > > meaningful scenarios to benchmarks. >=20 > OK. Are you thinking about a different microbenchmark, or something > closer to real workload? Both. As for microbenchmarking, I'd like to test the following scenarios: - set returning functions allocating a lot of memory - maintenance operations: REINDEX TABLE and the like, where we may end up= =20 with a large amount of memory used. - operations involving large hash tables =46or real workloads, if you have something specific in mind let me know. One thing I didn't mention is that I set max_parallel_workers_per_gather to= 0=20 in all tests. =2D-=20 Ronan Dunklau