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 1rBRPp-00As77-E9 for pgsql-hackers@arkaria.postgresql.org; Fri, 08 Dec 2023 03:20:37 +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 1rBRPo-003rT7-39 for pgsql-hackers@arkaria.postgresql.org; Fri, 08 Dec 2023 03:20:36 +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 1rBRPn-003rSz-PB for pgsql-hackers@lists.postgresql.org; Fri, 08 Dec 2023 03:20:35 +0000 Received: from mail-il1-x131.google.com ([2607:f8b0:4864:20::131]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rBRPk-00AeyK-Qd for pgsql-hackers@postgresql.org; Fri, 08 Dec 2023 03:20:35 +0000 Received: by mail-il1-x131.google.com with SMTP id e9e14a558f8ab-35d67599613so7642315ab.3 for ; Thu, 07 Dec 2023 19:20:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702005631; x=1702610431; darn=postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=k+MFhTwiV916xko9KaOzE5BMkKKvIKRwdhTQGToWSBw=; b=IJrw8hzN/shKaqc2CBxj21Zw8zvKxYM9WIU3jMd7r18/2pcKy8vm9CqtzDvcUy5K6E t4C37s0N8BrvHFSv33L4jccMnGSlE690BwjyYvbULnnadRvFWpVlpk2dZPdFwpt0xAoK stJWHBCIYYSPeHJB6OT8Rd33XDScb4WNWTRI7aZvFAge0WueZpYjS2wzQqiagWnUQBWS 34jw1G+XzSBfnJxiDyzAMfoGtvTvLhDom1J1c/jbAgo/Z6G+8XzxEl5YVyFqmhJOllAt rUt4h84FUrZ+HMcMM8vURjper5ai1tKeQV5SY8mzf7C0yhS9wCSNwvbSuBHgOb59XfBM EHlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702005631; x=1702610431; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=k+MFhTwiV916xko9KaOzE5BMkKKvIKRwdhTQGToWSBw=; b=t6aP8Wn2O1wbB6zUo8TrLahw8EVbJ+548sgZPlxLp9nTHw7S/bDHiDvHvt9BZcFo2j 7GzBWwhVowdQVyxmIeNiWCHzAw8YYoSJ1eqCROKgdMw906pgYiLYcwJrAIPD/VskWC3U RhA4IY2vQoxZj5239Wt8F+4amlOGguuwNVU5gG4K30Ewn0S7F3dcz0GI8CV9kLOrB5ix PDM1B/VTOHjtuGD1tg82Y+SK7ml/oyziKZ1XTpE9P54rP9dUfjZN9mBxwr8BlrM0hE/u IHuq3bF0gWehANyQBu1oJIsiD/MHoYFjRuvoLK+0q0qeJTmwWy+IsccAz86nlgNETiV1 35aw== X-Gm-Message-State: AOJu0YwyNsOWjA5Q4dfOqcfOvjxgIG2GaJ6sB3XxLLFOU/Rsrv9SEw5U CNdTogrW1X1XW1b5N6qvOO4= X-Google-Smtp-Source: AGHT+IHGYzTyY+lKXC5ZzGLDdczt9gpNcywnA0gZTcpKffdelHOijTk9/frs1Lbi/Mfn8Otv+ayIPQ== X-Received: by 2002:a92:d64a:0:b0:35d:78a2:c3d3 with SMTP id x10-20020a92d64a000000b0035d78a2c3d3mr3691511ilp.79.1702005631038; Thu, 07 Dec 2023 19:20:31 -0800 (PST) Received: from nathanxps13 (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id m3-20020a92d703000000b0035aa2ec8bbdsm288055iln.57.2023.12.07.19.20.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Dec 2023 19:20:30 -0800 (PST) Date: Thu, 7 Dec 2023 21:20:28 -0600 From: Nathan Bossart To: Tom Lane Cc: Jeff Davis , Joe Conway , Andrew Dunstan , Davin Shearer , PostgreSQL-development Subject: Re: micro-optimizing json.c Message-ID: <20231208032028.GA3468226@nathanxps13> References: <20231207231251.GB3359478@nathanxps13> <8d5cce64d7413eff04c5595a0a80d2b5a809e2fe.camel@j-davis.com> <1340019.1701996030@sss.pgh.pa.us> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="azLHFNyN32YCQGCU" Content-Disposition: inline In-Reply-To: <1340019.1701996030@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --azLHFNyN32YCQGCU Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Thu, Dec 07, 2023 at 07:40:30PM -0500, Tom Lane wrote: > Hmm ... I think that might not be the way to think about it. What > I'm wondering is why we need a test as expensive as IsValidJsonNumber > in the first place, given that we know this is a numeric data type's > output. ISTM we only need to reject "Inf"/"-Inf" and "NaN", which > surely does not require a full parse. Skip over a sign, check for > "I"/"N", and you're done. > > ... and for that matter, why does quoting of Inf/NaN require > that we apply something as expensive as escape_json? Several other > paths in this switch have no hesitation about assuming that they > can just plaster double quotes around what was emitted. How is > that safe for timestamps but not Inf/NaN? I did both of these in v2, although I opted to test that the first character after the optional '-' was a digit instead of testing that it was _not_ an 'I' or 'N'. I think that should be similar performance-wise, and maybe it's a bit more future-proof in case someone invents some new notation for a numeric data type (/shrug). In any case, this seems to speed up my test by another half a second or so. I think there are some similar improvements that we can make for JSONTYPE_BOOL and JSONTYPE_CAST, but I haven't tested them yet. -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --azLHFNyN32YCQGCU Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0001-avoid-some-strlen-calls-in-json.c.patch" From dc5ec1a2204398fa25390f31771eaa57816a96a1 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Thu, 7 Dec 2023 16:45:45 -0600 Subject: [PATCH v2 1/1] avoid some strlen() calls in json.c --- src/backend/utils/adt/json.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index cb4311e75f..640d9123ed 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -218,13 +218,17 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result, outputstr = OidOutputFunctionCall(outfuncoid, val); /* - * Don't call escape_json for a non-key if it's a valid JSON - * number. + * Don't quote a non-key if it's a valid JSON number. Since we + * know this is a numeric data type's output, we simplify and + * open-code the validation for better performance. */ - if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr))) + if (!key_scalar && + ((*outputstr >= '0' && *outputstr <= '9') || + (*outputstr == '-' && + (outputstr[1] >= '0' && outputstr[1] <= '9')))) appendStringInfoString(result, outputstr); else - escape_json(result, outputstr); + appendStringInfo(result, "\"%s\"", outputstr); pfree(outputstr); break; case JSONTYPE_DATE: @@ -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 --azLHFNyN32YCQGCU--