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 1rBNYD-00Achc-Mp for pgsql-hackers@arkaria.postgresql.org; Thu, 07 Dec 2023 23:13:01 +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 1rBNYB-002v1L-Dc for pgsql-hackers@arkaria.postgresql.org; Thu, 07 Dec 2023 23:12:59 +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 1rBNYB-002v1A-3v for pgsql-hackers@lists.postgresql.org; Thu, 07 Dec 2023 23:12:59 +0000 Received: from mail-io1-xd34.google.com ([2607:f8b0:4864:20::d34]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rBNY8-00AdLo-42 for pgsql-hackers@postgresql.org; Thu, 07 Dec 2023 23:12:58 +0000 Received: by mail-io1-xd34.google.com with SMTP id ca18e2360f4ac-7b3b78e6729so66169039f.0 for ; Thu, 07 Dec 2023 15:12:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1701990774; x=1702595574; darn=postgresql.org; h=content-disposition:mime-version:message-id:subject:cc:to:from:date :from:to:cc:subject:date:message-id:reply-to; bh=HEetbNTizkXP9cmdF2dqRJWFDD87W5bDSImBRESnbxg=; b=DL1KgAZK3w1xSFl5+GUGpnGvzLjzBivJKDhv38V5vIGjDGt0KI2uXe8ita6pIES+8z yLrTvbHj+5eWOB1V6TDA/ZaXlHGrbOClVSH/Ekdm9DjZrAF77Z9l0L4D4pz7aqPrNBAS Jxu7l17sq7WMtjPTw6vjxN86vbyVGJu3HKkOgp8HP8i12O98waBz8iAxqYL9UeCCDDTP 0P+x1px7X7E8UIIUU8tkNhN9ws6Fl6Ky7iewqy8G9cPaPi4jvY05rzatl3L2aPfMw4gR pNZ9gmQUzrFXH6umRHO7/EAoB4DHzaVZYFkWkCszfIAp26SssuPqr4MIjjN5oS786Q7C GOmw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701990774; x=1702595574; h=content-disposition:mime-version:message-id:subject:cc:to:from:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=HEetbNTizkXP9cmdF2dqRJWFDD87W5bDSImBRESnbxg=; b=urhA7No0TbzTn7iPY6uKzOws3USOOOUWGllxqfrMgwdoYBXZLtaZHADC8wSn/h4hCu GtRwHksDKTfNUTyomeB8ZRbloJyGBmBq9r1i0J/lHO7Xv4IoDoGwjdZLfm62x0WFX2qc Insu/dEYCjwIKJaqElnURBAM1+89Qiq5sMzC7nk6xbFbw+3e9sXGfaT2May6uyUlN6qL DhS4a75PhoXQKm2FlSY9ex18Hqumj/1UY4KcbjfGIffByqdedSXpcHhzY9kqeSmcWl1U Z+CVMgu5twCgYknW4YLVBZR2UJXNt/6E6bjYDi8zlZZQxV56T0LU/+geHZhaIqy65dF4 b21A== X-Gm-Message-State: AOJu0YyAmNPHchMklXr5opk7fgrBnhsT7m2nYecEzPEok46/PNGyc5WE SNNKdyQ5YMD/n4G5mkoe1Xk3DIdZ2sc= X-Google-Smtp-Source: AGHT+IHvA3ZnnQKVPoz2o9bnmCX+81qitg93tcVE0ZrH6fvVh1uAGgJAkMGyHDZU6W3IIjLKxq/GLg== X-Received: by 2002:a6b:f10b:0:b0:7b7:bba:cdc7 with SMTP id e11-20020a6bf10b000000b007b70bbacdc7mr433069iog.30.1701990773810; Thu, 07 Dec 2023 15:12:53 -0800 (PST) Received: from nathanxps13 (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id fm12-20020a0566382b0c00b00469104b00f9sm169922jab.56.2023.12.07.15.12.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Dec 2023 15:12:53 -0800 (PST) Date: Thu, 7 Dec 2023 17:12:51 -0600 From: Nathan Bossart To: Joe Conway Cc: Tom Lane , Andrew Dunstan , Davin Shearer , PostgreSQL-development Subject: micro-optimizing json.c Message-ID: <20231207231251.GB3359478@nathanxps13> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="J2SCkAp4GZ/dPZZf" Content-Disposition: inline List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --J2SCkAp4GZ/dPZZf Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Moving this to a new thread... On Thu, Dec 07, 2023 at 07:15:28AM -0500, Joe Conway wrote: > On 12/6/23 21:56, Nathan Bossart wrote: >> On Wed, Dec 06, 2023 at 03:20:46PM -0500, Tom Lane wrote: >> > If Nathan's perf results hold up elsewhere, it seems like some >> > micro-optimization around the text-pushing (appendStringInfoString) >> > might be more useful than caching. The 7% spent in cache lookups >> > could be worth going after later, but it's not the top of the list. >> >> Hah, it turns out my benchmark of 110M integers really stresses the >> JSONTYPE_NUMERIC path in datum_to_json_internal(). That particular path >> calls strlen() twice: once for IsValidJsonNumber(), and once in >> appendStringInfoString(). If I save the result from IsValidJsonNumber() >> and give it to appendBinaryStringInfo() instead, the COPY goes ~8% faster. >> It's probably worth giving datum_to_json_internal() a closer look in a new >> thread. > > Yep, after looking through that code I was going to make the point that your > 11 integer test was over indexing on that one type. I am sure there are > other micro-optimizations to be made here, but I also think that it is > outside the scope of the COPY TO JSON patch. Here's a patch that removes a couple of strlen() calls that showed up prominently in perf for a COPY TO (FORMAT json) on 110M integers. On my laptop, I see a 20% speedup from ~23.6s to ~18.9s for this test. I plan to test the other types as well, and I'd also like to look into the caching mentioned above if/when COPY TO (FORMAT json) is committed. -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --J2SCkAp4GZ/dPZZf Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-avoid-some-strlen-calls-in-json.c.patch" From d69046e5f6d9c570aafa54bf3a99eb9d63fd439e Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Thu, 7 Dec 2023 16:45:45 -0600 Subject: [PATCH v1 1/1] avoid some strlen() calls in json.c --- src/backend/utils/adt/json.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index cb4311e75f..b22d9d5b4c 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -178,6 +178,7 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result, { char *outputstr; text *jsontext; + int len; check_stack_depth(); @@ -220,9 +221,12 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result, /* * Don't call escape_json for a non-key if it's a valid JSON * number. + * + * Saving the strlen() result allows us to avoid another expensive + * strlen() call in appendStringInfoString(). */ - if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr))) - appendStringInfoString(result, outputstr); + if (!key_scalar && IsValidJsonNumber(outputstr, (len = strlen(outputstr)))) + appendBinaryStringInfo(result, outputstr, len); else escape_json(result, outputstr); pfree(outputstr); @@ -502,8 +506,14 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) int i; bool needsep = false; const char *sep; + int seplen; + /* + * We can avoid expensive strlen() calls by precalculating the separator + * length. + */ sep = use_line_feeds ? ",\n " : ","; + seplen = use_line_feeds ? sizeof(",\n ") - 1 : sizeof(",") - 1; td = DatumGetHeapTupleHeader(composite); @@ -532,7 +542,7 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) continue; if (needsep) - appendStringInfoString(result, sep); + appendBinaryStringInfo(result, sep, seplen); needsep = true; attname = NameStr(att->attname); -- 2.25.1 --J2SCkAp4GZ/dPZZf--