From 070bca416c0a78f4ce32a25c1225e820c820498a Mon Sep 17 00:00:00 2001
From: Joel Jakobsson <github@compiler.org>
Date: Mon, 1 Jul 2024 07:44:46 +0200
Subject: [PATCH] Optimize numeric multiplication for up to two base-NBASE
 digit operands

---
 src/backend/utils/adt/numeric.c | 52 +++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 5510a203b0..d6c9017a35 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -8747,6 +8747,58 @@ mul_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
 		return;
 	}
 
+	/*
+	 * If var1 and var2 are just one or two digits, their product will fit in
+	 * an int64 can be computed directly, which is significantly faster.
+	 */
+	if (var2ndigits <= 2)
+	{
+		int64		product;
+
+		switch (var1ndigits)
+		{
+			case 1:
+				product = var1digits[0];
+				break;
+			case 2:
+				product = var1digits[0] * NBASE + var1digits[1];
+				break;
+		}
+
+		switch (var2ndigits)
+		{
+			case 1:
+				product *= var2digits[0];
+				break;
+			case 2:
+				product *= var2digits[0] * NBASE + var2digits[1];
+				break;
+		}
+
+		alloc_var(result, res_ndigits);
+		res_digits = result->digits;
+		for (i = res_ndigits - 1; i >= 0; i--)
+		{
+			res_digits[i] = product % NBASE;
+			product /= NBASE;
+		}
+		Assert(product == 0);
+
+		/*
+		 * Finally, round the result to the requested precision.
+		 */
+		result->weight = res_weight;
+		result->sign = res_sign;
+
+		/* Round to target rscale (and set result->dscale) */
+		round_var(result, rscale);
+
+		/* Strip leading and trailing zeroes */
+		strip_var(result);
+
+		return;
+	}
+
 	/*
 	 * We do the arithmetic in an array "dig[]" of signed int's.  Since
 	 * INT_MAX is noticeably larger than NBASE*NBASE, this gives us headroom
-- 
2.45.1

