Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1VAPow-0002QP-Qt for pgsql-performance@arkaria.postgresql.org; Fri, 16 Aug 2013 19:36:26 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.80) (envelope-from ) id 1VAPow-0002xE-9m for pgsql-performance@arkaria.postgresql.org; Fri, 16 Aug 2013 19:36:26 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1VAPou-0002vx-Vx for pgsql-performance@postgresql.org; Fri, 16 Aug 2013 19:36:25 +0000 Received: from mail.gransy.com ([2001:1528:137::f1]) by magus.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1VAPos-0003pn-JW for pgsql-performance@postgresql.org; Fri, 16 Aug 2013 19:36:24 +0000 Received: from antispam.gransy.com (antispam.gransy.com [77.78.104.124]) by mail.gransy.com (Postfix) with ESMTP id DB89F508C8E for ; Fri, 16 Aug 2013 21:36:20 +0200 (CEST) X-Virus-Scanned: G-Hosting.cz + Station.cz + Klenot.cz + Subreg.cz amavisd-new at antispam.gransy.com Received: from mail.gransy.com ([82.208.44.189]) by antispam.gransy.com (antispam.gransy.com [77.78.104.124]) (amavisd-new, port 10024) with ESMTP id 0pqr0e3PcFNp for ; Fri, 16 Aug 2013 21:36:31 +0200 (CEST) Received: by mail.gransy.com (Postfix, from userid 5000) id 70632508C90; Fri, 16 Aug 2013 21:36:19 +0200 (CEST) Received: from [192.168.1.237] (ip-78-45-139-138.net.upcbroadband.cz [78.45.139.138]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.gransy.com (Postfix) with ESMTPSA id 4EC22508C8E for ; Fri, 16 Aug 2013 21:36:19 +0200 (CEST) Message-ID: <520E7F33.7010305@fuzzy.cz> Date: Fri, 16 Aug 2013 21:36:19 +0200 From: Tomas Vondra User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130801 Thunderbird/17.0.8 MIME-Version: 1.0 To: pgsql-performance@postgresql.org Subject: Re: queries with DISTINCT / GROUP BY giving different plans References: <3a56c4d2c0b2daa11a625c2983823901.squirrel@sq.gransy.com> <11586.1376505314@sss.pgh.pa.us> In-Reply-To: <11586.1376505314@sss.pgh.pa.us> Content-Type: text/plain; charset=ISO-8859-2 Content-Transfer-Encoding: 7bit X-Pg-Spam-Score: -1.9 (-) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-performance Precedence: bulk Sender: pgsql-performance-owner@postgresql.org On 14.8.2013 20:35, Tom Lane wrote: > "Tomas Vondra" writes: >> I've run into a strange plan difference on 9.1.9 - the first query >> does "DISTINCT" by doing a GROUP BY on the columns (both INT). ... >> Now, this takes ~45 seconds to execute, but after rewriting the >> query to use the regular DISTINCT it suddenly switches to >> HashAggregate with ~1/3 the cost (although it produces the same >> output, AFAIK), and it executes in ~15 seconds. > > [ scratches head... ] I guess you're running into some corner case > where choose_hashed_grouping and choose_hashed_distinct make > different choices. It's going to be tough to debug without a test > case though. I couldn't reproduce the behavior in a few tries here. > >> BTW I can't test this on 9.2 or 9.3 easily, as this is our >> production environment and I can't just export the data. I've tried >> to simulate this but so far no luck. > > I suppose they won't yet you step through those two functions with a > debugger either ... OK, this time the complete message ... I've managed to get the data to a different machine, and I've spent some time on debugging it. It seems that the difference is in evaluating hashentrysize - while choose_hashed_distinct does this: /* * Don't do it if it doesn't look like the hashtable will fit into * work_mem. */ hashentrysize = MAXALIGN(path_width) + MAXALIGN(sizeof(MinimalTupleData)); if (hashentrysize * dNumDistinctRows > work_mem * 1024L) return false; while choose_hashed_grouping does this: /* Estimate per-hash-entry space at tuple width... */ hashentrysize = MAXALIGN(path_width) + MAXALIGN(sizeof(MinimalTupleData)); /* plus space for pass-by-ref transition values... */ hashentrysize += agg_costs->transitionSpace; /* plus the per-hash-entry overhead */ hashentrysize += hash_agg_entry_size(agg_costs->numAggs); if (hashentrysize * dNumGroups > work_mem * 1024L) return false; In both cases the common parameter values are dNumGroups = dNumDistinctRows = 20451018 work_mem = 819200 but the hashentrysize size is 24 (choose_hashed_distinct) or 56 (choose_hashed_grouping). This causes that while _distinct evaluates the condition as false, and _grouping as true (and thus returns false). Now, the difference between 24 and 56 is caused by hash_agg_entry_size. It's called with numAggs=0 but returns 32. I'm wondering if it should return 0 in such cases, i.e. something like this: Size hash_agg_entry_size(int numAggs) { Size entrysize; if (numAggs == 0) return 0; /* This must match build_hash_table */ entrysize = sizeof(AggHashEntryData) + (numAggs - 1) * sizeof(AggStatePerGroupData); entrysize = MAXALIGN(entrysize); /* Account for hashtable overhead (assuming fill factor = 1) */ entrysize += 3 * sizeof(void *); return entrysize; } I've tested that after this both queries use HashAggregate (which is the right choice), but I haven't done any extensive checking so maybe I'm missing something. Tomas -- Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-performance