Avg performance for int8/numeric

Started by Mark Kirkwoodabout 19 years ago28 messages
#1Mark Kirkwood
markir@paradise.net.nz
1 attachment(s)

Avg performance for these two datatypes can be improved by *not*
calculating the sum of squares in the shared accumulator
(do_numeric_accum). However there is a little subtlety as this function
is also the shared by variance and stddev!

This patch:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.
- Amends all the accumulators that call it to include the bool (set to
true).
- Adds new functions [int8|numeric]_avg_accum that call do_numeric_accum
with the bool set to false.
- Amends the the bootstrap entries for pg_aggregate to use the new
accumulators for avg(int8|numeric).
- Adds the new accumulators into the bootstrap entries for pg_proc.

Performance gain is approx 33% (it is still slower than doing sum/count
- possibly due to the construct/deconstruct overhead of the numeric
transition array).

Cheers

Mark

Attachments:

avg1.patchtext/x-patch; name=avg1.patchDownload
Index: src/backend/utils/adt/numeric.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/numeric.c,v
retrieving revision 1.96
diff -c -r1.96 numeric.c
*** src/backend/utils/adt/numeric.c	4 Oct 2006 00:29:59 -0000	1.96
--- src/backend/utils/adt/numeric.c	23 Nov 2006 08:51:44 -0000
***************
*** 2060,2066 ****
   */
  
  static ArrayType *
! do_numeric_accum(ArrayType *transarray, Numeric newval)
  {
  	Datum	   *transdatums;
  	int			ndatums;
--- 2060,2066 ----
   */
  
  static ArrayType *
! do_numeric_accum(ArrayType *transarray, Numeric newval, bool useSumX2)
  {
  	Datum	   *transdatums;
  	int			ndatums;
***************
*** 2082,2095 ****
  	N = DirectFunctionCall1(numeric_inc, N);
  	sumX = DirectFunctionCall2(numeric_add, sumX,
  							   NumericGetDatum(newval));
! 	sumX2 = DirectFunctionCall2(numeric_add, sumX2,
  								DirectFunctionCall2(numeric_mul,
  													NumericGetDatum(newval),
  													NumericGetDatum(newval)));
  
  	transdatums[0] = N;
  	transdatums[1] = sumX;
! 	transdatums[2] = sumX2;
  
  	result = construct_array(transdatums, 3,
  							 NUMERICOID, -1, false, 'i');
--- 2082,2097 ----
  	N = DirectFunctionCall1(numeric_inc, N);
  	sumX = DirectFunctionCall2(numeric_add, sumX,
  							   NumericGetDatum(newval));
! 	if (useSumX2)
! 		sumX2 = DirectFunctionCall2(numeric_add, sumX2,
  								DirectFunctionCall2(numeric_mul,
  													NumericGetDatum(newval),
  													NumericGetDatum(newval)));
  
  	transdatums[0] = N;
  	transdatums[1] = sumX;
! 	if (useSumX2)
! 		transdatums[2] = sumX2;
  
  	result = construct_array(transdatums, 3,
  							 NUMERICOID, -1, false, 'i');
***************
*** 2103,2109 ****
  	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
  	Numeric		newval = PG_GETARG_NUMERIC(1);
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  /*
--- 2105,2123 ----
  	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
  	Numeric		newval = PG_GETARG_NUMERIC(1);
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval, true));
! }
! 
! /*
!  * Optimized case for average of numeric.
!  */
! Datum
! numeric_avg_accum(PG_FUNCTION_ARGS)
! {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Numeric		newval = PG_GETARG_NUMERIC(1);
! 
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval, false));
  }
  
  /*
***************
*** 2124,2130 ****
  
  	newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
--- 2138,2144 ----
  
  	newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval, true));
  }
  
  Datum
***************
*** 2136,2142 ****
  
  	newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
--- 2150,2156 ----
  
  	newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval, true));
  }
  
  Datum
***************
*** 2148,2156 ****
  
  	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
--- 2162,2186 ----
  
  	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval, true));
! }
! 
! /*
!  * Optimized case for average of int8.
!  */
! Datum
! int8_avg_accum(PG_FUNCTION_ARGS)
! {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval8 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
! 
! 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
! 
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval, false));
  }
  
+ 
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
Index: src/include/catalog/pg_aggregate.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_aggregate.h,v
retrieving revision 1.58
diff -c -r1.58 pg_aggregate.h
*** src/include/catalog/pg_aggregate.h	4 Oct 2006 00:30:07 -0000	1.58
--- src/include/catalog/pg_aggregate.h	23 Nov 2006 08:51:49 -0000
***************
*** 80,89 ****
   */
  
  /* avg */
! DATA(insert ( 2100	int8_accum		numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_accum	numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
--- 80,89 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.427
diff -c -r1.427 pg_proc.h
*** src/include/catalog/pg_proc.h	4 Oct 2006 00:30:07 -0000	1.427
--- src/include/catalog/pg_proc.h	23 Nov 2006 08:52:37 -0000
***************
*** 2697,2708 ****
--- 2697,2710 ----
  DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 f f t f i 1 701 "1022" _null_ _null_ _null_ float8_stddev_samp - _null_ ));
  DESCR("STDDEV_SAMP aggregate final function");
  DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_accum - _null_ ));
+ DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_avg_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 21" _null_ _null_ _null_ int2_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 23" _null_ _null_ _null_ int4_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1836 (  int8_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_accum - _null_ ));
+ DATA(insert OID = 2857 (  int8_avg_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_avg_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1837 (  numeric_avg	   PGNSP PGUID 12 f f t f i 1 1700 "1231" _null_ _null_ _null_	numeric_avg - _null_ ));
  DESCR("AVG aggregate final function");
Index: src/include/utils/builtins.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.282
diff -c -r1.282 builtins.h
*** src/include/utils/builtins.h	18 Sep 2006 22:40:40 -0000	1.282
--- src/include/utils/builtins.h	23 Nov 2006 08:52:41 -0000
***************
*** 833,841 ****
--- 833,843 ----
  extern Datum text_numeric(PG_FUNCTION_ARGS);
  extern Datum numeric_text(PG_FUNCTION_ARGS);
  extern Datum numeric_accum(PG_FUNCTION_ARGS);
+ extern Datum numeric_avg_accum(PG_FUNCTION_ARGS);
  extern Datum int2_accum(PG_FUNCTION_ARGS);
  extern Datum int4_accum(PG_FUNCTION_ARGS);
  extern Datum int8_accum(PG_FUNCTION_ARGS);
+ extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
  extern Datum numeric_avg(PG_FUNCTION_ARGS);
  extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
  extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
#2Neil Conway
neilc@samurai.com
In reply to: Mark Kirkwood (#1)
Re: Avg performance for int8/numeric

On Fri, 2006-11-24 at 11:08 +1300, Mark Kirkwood wrote:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.

I think it would be clearer to reorganize this function slightly, and
have only a single branch on "useSumX2". On first glance it isn't
obviously that transdatums[2] is defined (but unchanged) when useSumX2
is false.

Performance gain is approx 33%

Nice.

(it is still slower than doing sum/count - possibly due to the
construct/deconstruct overhead of the numeric transition array).

This would indeed be worth profiling. If it turns out that array
overhead is significant, I wonder if we could use a composite type for
the transition variable instead. That might also make it easier to
represent the "N" value as an int8 rather than a numeric.

-Neil

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Neil Conway (#2)
Re: Avg performance for int8/numeric

Neil Conway <neilc@samurai.com> writes:

On Fri, 2006-11-24 at 11:08 +1300, Mark Kirkwood wrote:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.

I think it would be clearer to reorganize this function slightly, and
have only a single branch on "useSumX2".

It would be clearer to just have a separate function.

regards, tom lane

#4Bruce Momjian
bruce@momjian.us
In reply to: Mark Kirkwood (#1)
Re: Avg performance for int8/numeric

This has been saved for the 8.3 release:

http://momjian.postgresql.org/cgi-bin/pgpatches_hold

---------------------------------------------------------------------------

Mark Kirkwood wrote:

Avg performance for these two datatypes can be improved by *not*
calculating the sum of squares in the shared accumulator
(do_numeric_accum). However there is a little subtlety as this function
is also the shared by variance and stddev!

This patch:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.
- Amends all the accumulators that call it to include the bool (set to
true).
- Adds new functions [int8|numeric]_avg_accum that call do_numeric_accum
with the bool set to false.
- Amends the the bootstrap entries for pg_aggregate to use the new
accumulators for avg(int8|numeric).
- Adds the new accumulators into the bootstrap entries for pg_proc.

Performance gain is approx 33% (it is still slower than doing sum/count
- possibly due to the construct/deconstruct overhead of the numeric
transition array).

Cheers

Mark

---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to majordomo@postgresql.org so that your
message can get through to the mailing list cleanly

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#5Mark Kirkwood
markir@paradise.net.nz
In reply to: Neil Conway (#2)
1 attachment(s)
Re: Avg performance for int8/numeric

Neil Conway wrote:

On Fri, 2006-11-24 at 11:08 +1300, Mark Kirkwood wrote:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.

I think it would be clearer to reorganize this function slightly, and
have only a single branch on "useSumX2". On first glance it isn't
obviously that transdatums[2] is defined (but unchanged) when useSumX2
is false.

Right - new patch attached that adds a new function do_numeric_avg_accum
that only uses N and sum(X). This means I could amend the avg aggregates
for numeric, int8 to have a initvalues of {0,0}.

Cheers

Mark

Attachments:

avg2.patchtext/x-patch; name=avg2.patchDownload
? gmon.out
Index: src/backend/utils/adt/numeric.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/numeric.c,v
retrieving revision 1.96
diff -c -r1.96 numeric.c
*** src/backend/utils/adt/numeric.c	4 Oct 2006 00:29:59 -0000	1.96
--- src/backend/utils/adt/numeric.c	25 Nov 2006 00:00:58 -0000
***************
*** 2097,2102 ****
--- 2097,2136 ----
  	return result;
  }
  
+ /*
+  * Improve avg performance by not caclulating sum(X*X).
+  */
+ static ArrayType *
+ do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+ {
+ 	Datum	   *transdatums;
+ 	int			ndatums;
+ 	Datum		N,
+ 				sumX;
+ 	ArrayType  *result;
+ 
+ 	/* We assume the input is array of numeric */
+ 	deconstruct_array(transarray,
+ 					  NUMERICOID, -1, false, 'i',
+ 					  &transdatums, NULL, &ndatums);
+ 	if (ndatums != 2)
+ 		elog(ERROR, "expected 2-element numeric array");
+ 	N = transdatums[0];
+ 	sumX = transdatums[1];
+ 
+ 	N = DirectFunctionCall1(numeric_inc, N);
+ 	sumX = DirectFunctionCall2(numeric_add, sumX,
+ 							   NumericGetDatum(newval));
+ 
+ 	transdatums[0] = N;
+ 	transdatums[1] = sumX;
+ 
+ 	result = construct_array(transdatums, 2,
+ 							 NUMERICOID, -1, false, 'i');
+ 
+ 	return result;
+ }
+ 
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
***************
*** 2107,2112 ****
--- 2141,2158 ----
  }
  
  /*
+  * Optimized case for average of numeric.
+  */
+ Datum
+ numeric_avg_accum(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+ 	Numeric		newval = PG_GETARG_NUMERIC(1);
+ 
+ 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+ }
+ 
+ /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
   * is overkill for the N and sum(X) values, but definitely not overkill
***************
*** 2151,2156 ****
--- 2197,2218 ----
  	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
+ /*
+  * Optimized case for average of int8.
+  */
+ Datum
+ int8_avg_accum(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+ 	Datum		newval8 = PG_GETARG_DATUM(1);
+ 	Numeric		newval;
+ 
+ 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+ 
+ 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+ }
+ 
+ 
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
***************
*** 2164,2174 ****
  	deconstruct_array(transarray,
  					  NUMERICOID, -1, false, 'i',
  					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
- 	/* ignore sumX2 */
  
  	/* SQL92 defines AVG of no values to be NULL */
  	/* N is zero iff no digits (cf. numeric_uminus) */
--- 2226,2235 ----
  	deconstruct_array(transarray,
  					  NUMERICOID, -1, false, 'i',
  					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 2)
! 		elog(ERROR, "expected 2-element numeric array");
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
  
  	/* SQL92 defines AVG of no values to be NULL */
  	/* N is zero iff no digits (cf. numeric_uminus) */
Index: src/include/catalog/pg_aggregate.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_aggregate.h,v
retrieving revision 1.58
diff -c -r1.58 pg_aggregate.h
*** src/include/catalog/pg_aggregate.h	4 Oct 2006 00:30:07 -0000	1.58
--- src/include/catalog/pg_aggregate.h	25 Nov 2006 00:01:01 -0000
***************
*** 80,89 ****
   */
  
  /* avg */
! DATA(insert ( 2100	int8_accum		numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_accum	numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
--- 80,89 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	"{0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.428
diff -c -r1.428 pg_proc.h
*** src/include/catalog/pg_proc.h	24 Nov 2006 21:18:42 -0000	1.428
--- src/include/catalog/pg_proc.h	25 Nov 2006 00:01:20 -0000
***************
*** 2697,2708 ****
--- 2697,2710 ----
  DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 f f t f i 1 701 "1022" _null_ _null_ _null_ float8_stddev_samp - _null_ ));
  DESCR("STDDEV_SAMP aggregate final function");
  DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_accum - _null_ ));
+ DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_avg_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 21" _null_ _null_ _null_ int2_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 23" _null_ _null_ _null_ int4_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1836 (  int8_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_accum - _null_ ));
+ DATA(insert OID = 2857 (  int8_avg_accum	   PGNSP PGUID 12 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_avg_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1837 (  numeric_avg	   PGNSP PGUID 12 f f t f i 1 1700 "1231" _null_ _null_ _null_	numeric_avg - _null_ ));
  DESCR("AVG aggregate final function");
Index: src/include/utils/builtins.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.282
diff -c -r1.282 builtins.h
*** src/include/utils/builtins.h	18 Sep 2006 22:40:40 -0000	1.282
--- src/include/utils/builtins.h	25 Nov 2006 00:01:23 -0000
***************
*** 833,841 ****
--- 833,843 ----
  extern Datum text_numeric(PG_FUNCTION_ARGS);
  extern Datum numeric_text(PG_FUNCTION_ARGS);
  extern Datum numeric_accum(PG_FUNCTION_ARGS);
+ extern Datum numeric_avg_accum(PG_FUNCTION_ARGS);
  extern Datum int2_accum(PG_FUNCTION_ARGS);
  extern Datum int4_accum(PG_FUNCTION_ARGS);
  extern Datum int8_accum(PG_FUNCTION_ARGS);
+ extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
  extern Datum numeric_avg(PG_FUNCTION_ARGS);
  extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
  extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
#6Mark Kirkwood
markir@paradise.net.nz
In reply to: Neil Conway (#2)
1 attachment(s)
Re: Avg performance for int8/numeric

Neil Conway wrote:

(it is still slower than doing sum/count - possibly due to the
construct/deconstruct overhead of the numeric transition array).

This would indeed be worth profiling. If it turns out that array
overhead is significant, I wonder if we could use a composite type for
the transition variable instead. That might also make it easier to
represent the "N" value as an int8 rather than a numeric.

I've profiled the 2nd patch using the setup indicated below. The first
64 lines of the flat graph are attached. The complete profile is here:

http://homepages.paradise.net.nz/markir/download/postgres/postgres-avg.gprof.gz

Setup:

avg=# \d avgtest
Table "public.avgtest"
Column | Type | Modifiers
--------+---------------+-----------
id | integer |
val0 | bigint |
val1 | numeric(12,2) |
val2 | numeric(10,0) |

avg=# analyze verbose avgtest;
INFO: analyzing "public.avgtest"
INFO: "avgtest": scanned 3000 of 87689 pages, containing 342138 live
rows and 0 dead rows; 3000 rows in sample, 10000580 estimated total rows
ANALYZE
Time: 252.033 ms
avg=# select avg(val2) from avgtest;
avg
---------------------
714285.214285800000
(1 row)

Time: 35196.028 ms
avg=# \q

regards

Mark

Attachments:

postgres-avg.gprof.headtext/plain; name=postgres-avg.gprof.headDownload
#7Mark Kirkwood
markir@paradise.net.nz
In reply to: Neil Conway (#2)
1 attachment(s)
Re: [PATCHES] Avg performance for int8/numeric

(Blast - meant to send this to -hackers not -patches....)

Neil Conway wrote:

(it is still slower than doing sum/count - possibly due to the
construct/deconstruct overhead of the numeric transition array).

This would indeed be worth profiling. If it turns out that array
overhead is significant, I wonder if we could use a composite type for
the transition variable instead. That might also make it easier to
represent the "N" value as an int8 rather than a numeric.

I've profiled the 2nd patch using the setup indicated below. The first
64 lines of the flat graph are attached. The complete profile is here:

http://homepages.paradise.net.nz/markir/download/postgres/postgres-avg.gprof.gz

Setup:

avg=# \d avgtest
Table "public.avgtest"
Column | Type | Modifiers
--------+---------------+-----------
id | integer |
val0 | bigint |
val1 | numeric(12,2) |
val2 | numeric(10,0) |

avg=# analyze verbose avgtest;
INFO: analyzing "public.avgtest"
INFO: "avgtest": scanned 3000 of 87689 pages, containing 342138 live
rows and 0 dead rows; 3000 rows in sample, 10000580 estimated total rows
ANALYZE
Time: 252.033 ms
avg=# select avg(val2) from avgtest;
avg
---------------------
714285.214285800000
(1 row)

Time: 35196.028 ms
avg=# \q

regards

Mark

Attachments:

postgres-avg.gprof.headtext/plain; name=postgres-avg.gprof.headDownload
#8Luke Lonergan
llonergan@greenplum.com
In reply to: Mark Kirkwood (#6)
Re: Avg performance for int8/numeric

So, if I understand this correctly, we're calling Alloc and ContextAlloc 10
times for every row being summed?

There are approx 10M rows and the profile snippet below shows 100M calls to
each of those.

- Luke

On 11/24/06 4:46 PM, "Mark Kirkwood" <markir@paradise.net.nz> wrote:

Show quoted text

time seconds seconds calls s/call s/call name
14.42 2.16 2.16 100002977 0.00 0.00 AllocSetAlloc
9.08 3.52 1.36 20000000 0.00 0.00 add_abs
5.54 4.35 0.83 10000000 0.00 0.00 slot_deform_tuple
5.41 5.16 0.81 60001673 0.00 0.00 AllocSetFree
4.34 5.81 0.65 10000000 0.00 0.00 construct_md_array
4.21 6.44 0.63 20000003 0.00 0.00 make_result
3.54 6.97 0.53 10000000 0.00 0.00 numeric_add
3.27 7.46 0.49 30000003 0.00 0.00 set_var_from_num
3.00 7.91 0.45 100002652 0.00 0.00 MemoryContextAlloc

#9Luke Lonergan
llonergan@greenplum.com
In reply to: Mark Kirkwood (#7)
Re: [PATCHES] Avg performance for int8/numeric

So, if I understand this correctly, we're calling Alloc and ContextAlloc 10
times for every row being summed?

There are approx 10M rows and the profile snippet below shows 100M calls to
each of those.

- Luke

On 11/24/06 4:46 PM, "Mark Kirkwood" <markir@paradise.net.nz> wrote:

Show quoted text

time seconds seconds calls s/call s/call name
14.42 2.16 2.16 100002977 0.00 0.00 AllocSetAlloc
9.08 3.52 1.36 20000000 0.00 0.00 add_abs
5.54 4.35 0.83 10000000 0.00 0.00 slot_deform_tuple
5.41 5.16 0.81 60001673 0.00 0.00 AllocSetFree
4.34 5.81 0.65 10000000 0.00 0.00 construct_md_array
4.21 6.44 0.63 20000003 0.00 0.00 make_result
3.54 6.97 0.53 10000000 0.00 0.00 numeric_add
3.27 7.46 0.49 30000003 0.00 0.00 set_var_from_num
3.00 7.91 0.45 100002652 0.00 0.00 MemoryContextAlloc

#10Mark Kirkwood
markir@paradise.net.nz
In reply to: Luke Lonergan (#8)
Re: Avg performance for int8/numeric

Luke Lonergan wrote:

So, if I understand this correctly, we're calling Alloc and ContextAlloc 10
times for every row being summed?

There are approx 10M rows and the profile snippet below shows 100M calls to
each of those.

Unless I've accidentally run gprof on the profile output for a 100M row
case I had lying around :-( ... I'll check

#11Mark Kirkwood
markir@paradise.net.nz
In reply to: Mark Kirkwood (#10)
Re: Avg performance for int8/numeric

Mark Kirkwood wrote:

Luke Lonergan wrote:

So, if I understand this correctly, we're calling Alloc and
ContextAlloc 10
times for every row being summed?

There are approx 10M rows and the profile snippet below shows 100M
calls to
each of those.

Unless I've accidentally run gprof on the profile output for a 100M row
case I had lying around :-( ... I'll check

I haven't (so profile as attached is ok)...

#12Mark Kirkwood
markir@paradise.net.nz
In reply to: Mark Kirkwood (#7)
1 attachment(s)
Re: [PATCHES] Avg performance for int8/numeric

Mark Kirkwood wrote:

I've profiled the 2nd patch using the setup indicated below. The first
64 lines of the flat graph are attached.

By way of comparison, here is the first 63 lines for:

select sum(val2) from avgtest

Attachments:

postgres-sum.gprof.headtext/plain; name=postgres-sum.gprof.headDownload
#13Luke Lonergan
llonergan@greenplum.com
In reply to: Mark Kirkwood (#11)
Re: [PATCHES] Avg performance for int8/numeric

Mark,

On 11/24/06 6:03 PM, "Mark Kirkwood" <markir@paradise.net.nz> wrote:

Luke Lonergan wrote:

So, if I understand this correctly, we're calling Alloc and
ContextAlloc 10
times for every row being summed?

I haven't (so profile as attached is ok)...

OK - so, without having looked at the source recently, the last time I
profiled it within gdb it looked like the following is what happens in the
executor agg path:
temp heaptuple is allocated, page is pinned, tuple is copied into temp
heaptuple, page is unpinned, temp heaptuple is processed in agg path

This seems to fit the pattern we're seeing in your profile given that we've
got 4 attributes in this relation except that we're seeing it happen twice.
It seems like for each attribute a DATUM is alloc'ed, plus one more, and
this is done twice -> 10 alloc calls for each row being summed.

If this is the case, we can surely not alloc/free for every row and datum by
reusing the space...

- Luke

#14Luke Lonergan
llonergan@greenplum.com
In reply to: Mark Kirkwood (#12)
Re: [PATCHES] Avg performance for int8/numeric

Mark,

On 11/24/06 6:16 PM, "Mark Kirkwood" <markir@paradise.net.nz> wrote:

By way of comparison, here is the first 63 lines for:

select sum(val2) from avgtest

So, sum() is only alloc'ing 5 times for every row being processed, half of
what avg() is doing.

Seems like what we need to do is find a way to reuse the temp heaptuple
between calls.

- Luke

#15Mark Kirkwood
markir@paradise.net.nz
In reply to: Luke Lonergan (#14)
Re: [PATCHES] Avg performance for int8/numeric

Luke Lonergan wrote:

Mark,

On 11/24/06 6:16 PM, "Mark Kirkwood" <markir@paradise.net.nz> wrote:

By way of comparison, here is the first 63 lines for:

select sum(val2) from avgtest

So, sum() is only alloc'ing 5 times for every row being processed, half of
what avg() is doing.

Seems like what we need to do is find a way to reuse the temp heaptuple
between calls.

Yeah - and I'm *guessing* that its due to avg needing to
deconstruct/construct a 2 element numeric array every call (whereas sum
needs just a single numeric). So some delving into whether it is
construct_md_array that can re-use a heaptuple or whether we need to
look elsewhere...

Also Neil suggested investigating using a single composite type {int8,
numeric} for the {N,sum(X)} transition values. This could well be a
faster way to do this (not sure how to make it work yet... but it sounds
promising...).

Cheers

Mark

#16Simon Riggs
simon@2ndquadrant.com
In reply to: Mark Kirkwood (#15)
Re: [PATCHES] Avg performance for int8/numeric

On Sat, 2006-11-25 at 18:57 +1300, Mark Kirkwood wrote:

Also Neil suggested investigating using a single composite type
{int8,
numeric} for the {N,sum(X)} transition values. This could well be a
faster way to do this (not sure how to make it work yet... but it
sounds
promising...).

If that is true it implies that any fixed length array is more expensive
than using a composite type. Is there something to be gained by changing
the basic representation of arrays, rather than rewriting all uses of
them?

--
Simon Riggs
EnterpriseDB http://www.enterprisedb.com

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Simon Riggs (#16)
Re: [PATCHES] Avg performance for int8/numeric

"Simon Riggs" <simon@2ndquadrant.com> writes:

On Sat, 2006-11-25 at 18:57 +1300, Mark Kirkwood wrote:

Also Neil suggested investigating using a single composite type
{int8,
numeric} for the {N,sum(X)} transition values. This could well be a
faster way to do this (not sure how to make it work yet... but it
sounds
promising...).

If that is true it implies that any fixed length array is more expensive
than using a composite type.

Not sure how you derived that conclusion from this statement, but it
doesn't appear to me to follow at all. The reason for Neil's suggestion
was to avoid using numeric arithmetic to run a simple counter, and the
reason that this array stuff is expensive is that the array *components*
are variable-length, which is something that no amount of array
redesigning will eliminate.

regards, tom lane

#18Mark Kirkwood
markir@paradise.net.nz
In reply to: Tom Lane (#17)
Re: [PATCHES] Avg performance for int8/numeric

Tom Lane wrote:

"Simon Riggs" <simon@2ndquadrant.com> writes:

On Sat, 2006-11-25 at 18:57 +1300, Mark Kirkwood wrote:

Also Neil suggested investigating using a single composite type
{int8,
numeric} for the {N,sum(X)} transition values. This could well be a
faster way to do this (not sure how to make it work yet... but it
sounds
promising...).

If that is true it implies that any fixed length array is more expensive
than using a composite type.

Not sure how you derived that conclusion from this statement, but it
doesn't appear to me to follow at all. The reason for Neil's suggestion
was to avoid using numeric arithmetic to run a simple counter, and the
reason that this array stuff is expensive is that the array *components*
are variable-length, which is something that no amount of array
redesigning will eliminate.

Here is what I think the major contributors to the time spent in avg are:

1/ maintaining sum of squares in the transition array ~ 33%
2/ calling numeric_inc on a numeric counter ~ 10%
3/ deconstruct/construct array of 2 numerics ~ 16%

I derived these by constructing a (deliberately inefficient) version of
sum that used an array of numerics and calculated extra stuff in its
transaction array, and then started removing code a bit at a time to see
what happened (I'm sure there are smarter ways... but this worked ok...).

The current patch does 1/, and doing a composite type of {int8, numeric}
would let us use a an int8 counter instead of numeric, which would
pretty much sort out 2/.

The array cost is more tricky - as Tom mentioned the issue is related to
the variable length nature of the array components, so just changing to
a composite type may not in itself save any of the (so-called) 'array
cost'. Having said that - the profiles suggest that we are perhaps doing
a whole lot more alloc'ing (i.e copying? detoasting?) of memory for
numerics than perhaps we need... I'm not sure how deeply buried the
decision about alloc'ing is being made, so doing anything about this may
be hard.

It looks to me like trying out a composite type is the next obvious step
to do, and then (once I've figured out how so that) we can check its
performance again!

Cheers

Mark

#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Mark Kirkwood (#18)
Re: [PATCHES] Avg performance for int8/numeric

Mark Kirkwood <markir@paradise.net.nz> writes:

... Having said that - the profiles suggest that we are perhaps doing
a whole lot more alloc'ing (i.e copying? detoasting?) of memory for
numerics than perhaps we need...

Yeah. We've looked at this in the past and not figured out any
particularly desirable answer for variable-size accumulator state.
There is a hack in there for fixed-size pass-by-reference state (see
count(*)). It's possible that you could get some traction by only doing
a palloc when the state size has to increase --- with a custom state
type it'd be possible to keep track of the allocated size as well as the
actual current length of the numeric sum. Another idea to consider in
this context is avoiding repeated numeric pack/unpack overhead by
storing the running sum in the "calculation variable" format, but I'm
not sure how deep you'd need to burrow into numeric.c to allow that.

Most of this depends on being able to have a transition state value
that isn't any standard SQL datatype. We've discussed that recently
in I-forget-what-context, and didn't find a good answer yet. I think
you can find the thread by searching for a discussion of using type
"internal" as the state datatype --- which is an idea that doesn't work
by itself because of the security holes it'd open in the type system.

regards, tom lane

#20Mark Kirkwood
markir@paradise.net.nz
In reply to: Tom Lane (#19)
Re: [PATCHES] Avg performance for int8/numeric

Tom Lane wrote:

Mark Kirkwood <markir@paradise.net.nz> writes:

... Having said that - the profiles suggest that we are perhaps doing
a whole lot more alloc'ing (i.e copying? detoasting?) of memory for
numerics than perhaps we need...

Yeah. We've looked at this in the past and not figured out any
particularly desirable answer for variable-size accumulator state.
There is a hack in there for fixed-size pass-by-reference state (see
count(*)). It's possible that you could get some traction by only doing
a palloc when the state size has to increase --- with a custom state
type it'd be possible to keep track of the allocated size as well as the
actual current length of the numeric sum. Another idea to consider in
this context is avoiding repeated numeric pack/unpack overhead by
storing the running sum in the "calculation variable" format, but I'm
not sure how deep you'd need to burrow into numeric.c to allow that.

Right - I figured it would probably be hard :-(. It looks worth
investigating, but might be a more longer term project (for me anyway,
lots of stuff to read in there!).

Most of this depends on being able to have a transition state value
that isn't any standard SQL datatype. We've discussed that recently
in I-forget-what-context, and didn't find a good answer yet. I think
you can find the thread by searching for a discussion of using type
"internal" as the state datatype --- which is an idea that doesn't work
by itself because of the security holes it'd open in the type system.

Interesting, I didn't think of doing that - was considering creating a
suitable SQL composite type - a bit crass I guess, but I might just try
that out anyway and see what sort of improvement it gives (we can then
discuss how to do it properly in the advent that it's good....).

Cheers

Mark

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Mark Kirkwood (#20)
Re: [PATCHES] Avg performance for int8/numeric

Mark Kirkwood <markir@paradise.net.nz> writes:

Tom Lane wrote:

Most of this depends on being able to have a transition state value
that isn't any standard SQL datatype. We've discussed that recently
in I-forget-what-context, and didn't find a good answer yet.

Interesting, I didn't think of doing that - was considering creating a
suitable SQL composite type - a bit crass I guess, but I might just try
that out anyway and see what sort of improvement it gives (we can then
discuss how to do it properly in the advent that it's good....).

The thing is that (a) composite types have *at least* as much overhead
as arrays, probably rather more; and (b) a composite type in itself
doesn't allow non-SQL types as components, so still doesn't let you
tackle the idea of keeping the running sum in numeric.c's internal
calculation format. So I don't think this will prove much --- the only
gain you can get is the count-in-int8-instead-of-numeric bit, which is
interesting but there is much left on the table.

regards, tom lane

#22Mark Kirkwood
markir@paradise.net.nz
In reply to: Tom Lane (#21)
Re: [PATCHES] Avg performance for int8/numeric

Tom Lane wrote:

Mark Kirkwood <markir@paradise.net.nz> writes:

Tom Lane wrote:

Most of this depends on being able to have a transition state value
that isn't any standard SQL datatype. We've discussed that recently
in I-forget-what-context, and didn't find a good answer yet.

Interesting, I didn't think of doing that - was considering creating a
suitable SQL composite type - a bit crass I guess, but I might just try
that out anyway and see what sort of improvement it gives (we can then
discuss how to do it properly in the advent that it's good....).

The thing is that (a) composite types have *at least* as much overhead
as arrays, probably rather more; and (b) a composite type in itself
doesn't allow non-SQL types as components, so still doesn't let you
tackle the idea of keeping the running sum in numeric.c's internal
calculation format. So I don't think this will prove much --- the only
gain you can get is the count-in-int8-instead-of-numeric bit, which is
interesting but there is much left on the table.

Right - I spent this afternoon coming to pretty much the same conclusion...

So I guess the best way forward is to make do for the time being with
the savings gained by not calculating sumX2, and revisit avg (and
variance etc) when we know how to do non-SQL state types.

Cheers

Mark

#23Simon Riggs
simon@2ndquadrant.com
In reply to: Mark Kirkwood (#22)
Re: [PATCHES] Avg performance for int8/numeric

On Mon, 2006-11-27 at 18:22 +1300, Mark Kirkwood wrote:

Tom Lane wrote:

Mark Kirkwood <markir@paradise.net.nz> writes:

Tom Lane wrote:

Most of this depends on being able to have a transition state value
that isn't any standard SQL datatype. We've discussed that recently
in I-forget-what-context, and didn't find a good answer yet.

Interesting, I didn't think of doing that - was considering creating a
suitable SQL composite type - a bit crass I guess, but I might just try
that out anyway and see what sort of improvement it gives (we can then
discuss how to do it properly in the advent that it's good....).

The thing is that (a) composite types have *at least* as much overhead
as arrays, probably rather more; and (b) a composite type in itself
doesn't allow non-SQL types as components, so still doesn't let you
tackle the idea of keeping the running sum in numeric.c's internal
calculation format. So I don't think this will prove much --- the only
gain you can get is the count-in-int8-instead-of-numeric bit, which is
interesting but there is much left on the table.

Right - I spent this afternoon coming to pretty much the same conclusion...

So I guess the best way forward is to make do for the time being with
the savings gained by not calculating sumX2, and revisit avg (and
variance etc) when we know how to do non-SQL state types.

IIRC the calculation format for NUMERIC is simply less padded than the
on-disk version. It should be possible to create it as a normal type
that never gets used apart from this situation.

--
Simon Riggs
EnterpriseDB http://www.enterprisedb.com

#24Bruce Momjian
bruce@momjian.us
In reply to: Mark Kirkwood (#5)
Re: Avg performance for int8/numeric

Your patch has been added to the PostgreSQL unapplied patches list at:

http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

---------------------------------------------------------------------------

Mark Kirkwood wrote:

Neil Conway wrote:

On Fri, 2006-11-24 at 11:08 +1300, Mark Kirkwood wrote:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.

I think it would be clearer to reorganize this function slightly, and
have only a single branch on "useSumX2". On first glance it isn't
obviously that transdatums[2] is defined (but unchanged) when useSumX2
is false.

Right - new patch attached that adds a new function do_numeric_avg_accum
that only uses N and sum(X). This means I could amend the avg aggregates
for numeric, int8 to have a initvalues of {0,0}.

Cheers

Mark

---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#25Bruce Momjian
bruce@momjian.us
In reply to: Mark Kirkwood (#5)
2 attachment(s)
Re: Avg performance for int8/numeric

I have tested this patch but it generates regression failures.

There was some code drift, so I am attaching an updated version of the
patch, and the regression diffs. The 'four' column is an 'int4' so my
guess is that somehow the wrong aggregate is being called.

---------------------------------------------------------------------------

Mark Kirkwood wrote:

Neil Conway wrote:

On Fri, 2006-11-24 at 11:08 +1300, Mark Kirkwood wrote:

- Modifies do_numeric_accum to have an extra bool parameter and does not
calc sumX2 when it is false.

I think it would be clearer to reorganize this function slightly, and
have only a single branch on "useSumX2". On first glance it isn't
obviously that transdatums[2] is defined (but unchanged) when useSumX2
is false.

Right - new patch attached that adds a new function do_numeric_avg_accum
that only uses N and sum(X). This means I could amend the avg aggregates
for numeric, int8 to have a initvalues of {0,0}.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

Attachments:

/pg/test/regress/regression.diffstext/x-diffDownload
*** ./expected/aggregates.out	Fri Jul 28 14:33:04 2006
--- ./results/aggregates.out	Thu Feb 15 23:54:45 2007
***************
*** 238,248 ****
  
  -- user-defined aggregates
  SELECT newavg(four) AS avg_1 FROM onek;
!        avg_1        
! --------------------
!  1.5000000000000000
! (1 row)
! 
  SELECT newsum(four) AS sum_1500 FROM onek;
   sum_1500 
  ----------
--- 238,244 ----
  
  -- user-defined aggregates
  SELECT newavg(four) AS avg_1 FROM onek;
! ERROR:  expected 2-element numeric array
  SELECT newsum(four) AS sum_1500 FROM onek;
   sum_1500 
  ----------

======================================================================

/pgpatches/aggregatestext/x-diffDownload
Index: src/backend/utils/adt/numeric.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v
retrieving revision 1.99
diff -c -c -r1.99 numeric.c
*** src/backend/utils/adt/numeric.c	16 Jan 2007 21:41:13 -0000	1.99
--- src/backend/utils/adt/numeric.c	16 Feb 2007 04:30:27 -0000
***************
*** 2165,2170 ****
--- 2165,2204 ----
  	return result;
  }
  
+ /*
+  * Improve avg performance by not caclulating sum(X*X).
+  */
+ static ArrayType *
+ do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+ {
+ 	Datum	   *transdatums;
+ 	int			ndatums;
+ 	Datum		N,
+ 				sumX;
+ 	ArrayType  *result;
+ 
+ 	/* We assume the input is array of numeric */
+ 	deconstruct_array(transarray,
+ 					  NUMERICOID, -1, false, 'i',
+ 					  &transdatums, NULL, &ndatums);
+ 	if (ndatums != 2)
+ 		elog(ERROR, "expected 2-element numeric array");
+ 	N = transdatums[0];
+ 	sumX = transdatums[1];
+ 
+ 	N = DirectFunctionCall1(numeric_inc, N);
+ 	sumX = DirectFunctionCall2(numeric_add, sumX,
+ 							   NumericGetDatum(newval));
+ 
+ 	transdatums[0] = N;
+ 	transdatums[1] = sumX;
+ 
+ 	result = construct_array(transdatums, 2,
+ 							 NUMERICOID, -1, false, 'i');
+ 
+ 	return result;
+ }
+ 
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
***************
*** 2175,2180 ****
--- 2209,2226 ----
  }
  
  /*
+  * Optimized case for average of numeric.
+  */
+ Datum
+ numeric_avg_accum(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+ 	Numeric		newval = PG_GETARG_NUMERIC(1);
+ 
+ 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+ }
+ 
+ /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
   * is overkill for the N and sum(X) values, but definitely not overkill
***************
*** 2219,2224 ****
--- 2265,2286 ----
  	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
+ /*
+  * Optimized case for average of int8.
+  */
+ Datum
+ int8_avg_accum(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+ 	Datum		newval8 = PG_GETARG_DATUM(1);
+ 	Numeric		newval;
+ 
+ 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+ 
+ 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+ }
+ 
+ 
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
***************
*** 2232,2242 ****
  	deconstruct_array(transarray,
  					  NUMERICOID, -1, false, 'i',
  					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
- 	/* ignore sumX2 */
  
  	/* SQL92 defines AVG of no values to be NULL */
  	/* N is zero iff no digits (cf. numeric_uminus) */
--- 2294,2303 ----
  	deconstruct_array(transarray,
  					  NUMERICOID, -1, false, 'i',
  					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 2)
! 		elog(ERROR, "expected 2-element numeric array");
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
  
  	/* SQL92 defines AVG of no values to be NULL */
  	/* N is zero iff no digits (cf. numeric_uminus) */
Index: src/include/catalog/catversion.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/catversion.h,v
retrieving revision 1.384
diff -c -c -r1.384 catversion.h
*** src/include/catalog/catversion.h	14 Feb 2007 01:58:58 -0000	1.384
--- src/include/catalog/catversion.h	16 Feb 2007 04:30:28 -0000
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200702131
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200702151
  
  #endif
Index: src/include/catalog/pg_aggregate.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_aggregate.h,v
retrieving revision 1.60
diff -c -c -r1.60 pg_aggregate.h
*** src/include/catalog/pg_aggregate.h	20 Jan 2007 09:27:19 -0000	1.60
--- src/include/catalog/pg_aggregate.h	16 Feb 2007 04:30:28 -0000
***************
*** 80,89 ****
   */
  
  /* avg */
! DATA(insert ( 2100	int8_accum		numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_accum	numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
--- 80,89 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	"{0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.443
diff -c -c -r1.443 pg_proc.h
*** src/include/catalog/pg_proc.h	7 Feb 2007 23:11:30 -0000	1.443
--- src/include/catalog/pg_proc.h	16 Feb 2007 04:30:31 -0000
***************
*** 2744,2755 ****
--- 2744,2759 ----
  DESCR("STDDEV_SAMP aggregate final function");
  DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_accum - _null_ ));
  DESCR("aggregate transition function");
+ DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_avg_accum - _null_ ));
+ DESCR("aggregate transition function");
  DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 21" _null_ _null_ _null_ int2_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 23" _null_ _null_ _null_ int4_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1836 (  int8_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_accum - _null_ ));
  DESCR("aggregate transition function");
+ DATA(insert OID = 2746 (  int8_avg_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_avg_accum - _null_ ));
+ DESCR("aggregate transition function");
  DATA(insert OID = 1837 (  numeric_avg	   PGNSP PGUID 12 1 0 f f t f i 1 1700 "1231" _null_ _null_ _null_	numeric_avg - _null_ ));
  DESCR("AVG aggregate final function");
  DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 f f t f i 1 1700 "1231" _null_ _null_ _null_	numeric_var_pop - _null_ ));
Index: src/include/utils/builtins.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.287
diff -c -c -r1.287 builtins.h
*** src/include/utils/builtins.h	28 Jan 2007 16:16:54 -0000	1.287
--- src/include/utils/builtins.h	16 Feb 2007 04:30:32 -0000
***************
*** 841,849 ****
--- 841,851 ----
  extern Datum text_numeric(PG_FUNCTION_ARGS);
  extern Datum numeric_text(PG_FUNCTION_ARGS);
  extern Datum numeric_accum(PG_FUNCTION_ARGS);
+ extern Datum numeric_avg_accum(PG_FUNCTION_ARGS);
  extern Datum int2_accum(PG_FUNCTION_ARGS);
  extern Datum int4_accum(PG_FUNCTION_ARGS);
  extern Datum int8_accum(PG_FUNCTION_ARGS);
+ extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
  extern Datum numeric_avg(PG_FUNCTION_ARGS);
  extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
  extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
#26Mark Kirkwood
markir@paradise.net.nz
In reply to: Bruce Momjian (#25)
1 attachment(s)
Re: Avg performance for int8/numeric

Bruce Momjian wrote:

I have tested this patch but it generates regression failures.

There was some code drift, so I am attaching an updated version of the
patch, and the regression diffs. The 'four' column is an 'int4' so my
guess is that somehow the wrong aggregate is being called.

Good catch - I must have neglected to run the regression test after
amending the number of array arguments for the numeric avg :-(.

Hmmm - this changing the number of array args for avg means we can't mix
transition functions for variance with final functions for avg - which
is exactly what the regression suite does with the 'newavg' aggregate.

I've 'fixed' this by amending the definition of 'newavg' to use the
transition and final function that 'avg' does. However I found myself
asking if this lost us the point of that test - so I looked back at the
older postgres versions (e.g. 7.1.3) and saw that back *then* 'newavg'
and 'avg' were defined using the same functions...so I think making the
change as indicated is ok.

I've attached a new patch with this change.

Cheers

Mark

Attachments:

avg3.patchtext/x-patch; name=avg3.patchDownload
diff -Nacr pgsql.orig/src/backend/utils/adt/numeric.c pgsql/src/backend/utils/adt/numeric.c
*** pgsql.orig/src/backend/utils/adt/numeric.c	Sun Jan 21 11:36:20 2007
--- pgsql/src/backend/utils/adt/numeric.c	Fri Feb 16 18:09:24 2007
***************
*** 2165,2170 ****
--- 2165,2204 ----
  	return result;
  }
  
+ /*
+  * Improve avg performance by not caclulating sum(X*X).
+  */
+ static ArrayType *
+ do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+ {
+ 	Datum	   *transdatums;
+ 	int			ndatums;
+ 	Datum		N,
+ 				sumX;
+ 	ArrayType  *result;
+ 
+ 	/* We assume the input is array of numeric */
+ 	deconstruct_array(transarray,
+ 					  NUMERICOID, -1, false, 'i',
+ 					  &transdatums, NULL, &ndatums);
+ 	if (ndatums != 2)
+ 		elog(ERROR, "expected 2-element numeric array");
+ 	N = transdatums[0];
+ 	sumX = transdatums[1];
+ 
+ 	N = DirectFunctionCall1(numeric_inc, N);
+ 	sumX = DirectFunctionCall2(numeric_add, sumX,
+ 							   NumericGetDatum(newval));
+ 
+ 	transdatums[0] = N;
+ 	transdatums[1] = sumX;
+ 
+ 	result = construct_array(transdatums, 2,
+ 							 NUMERICOID, -1, false, 'i');
+ 
+ 	return result;
+ }
+ 
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
***************
*** 2175,2180 ****
--- 2209,2226 ----
  }
  
  /*
+  * Optimized case for average of numeric.
+  */
+ Datum
+ numeric_avg_accum(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+ 	Numeric		newval = PG_GETARG_NUMERIC(1);
+ 
+ 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+ }
+ 
+ /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
   * is overkill for the N and sum(X) values, but definitely not overkill
***************
*** 2219,2224 ****
--- 2265,2286 ----
  	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
+ /*
+  * Optimized case for average of int8.
+  */
+ Datum
+ int8_avg_accum(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+ 	Datum		newval8 = PG_GETARG_DATUM(1);
+ 	Numeric		newval;
+ 
+ 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+ 
+ 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+ }
+ 
+ 
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
***************
*** 2232,2242 ****
  	deconstruct_array(transarray,
  					  NUMERICOID, -1, false, 'i',
  					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
- 	/* ignore sumX2 */
  
  	/* SQL92 defines AVG of no values to be NULL */
  	/* N is zero iff no digits (cf. numeric_uminus) */
--- 2294,2303 ----
  	deconstruct_array(transarray,
  					  NUMERICOID, -1, false, 'i',
  					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 2)
! 		elog(ERROR, "expected 2-element numeric array");
  	N = DatumGetNumeric(transdatums[0]);
  	sumX = DatumGetNumeric(transdatums[1]);
  
  	/* SQL92 defines AVG of no values to be NULL */
  	/* N is zero iff no digits (cf. numeric_uminus) */
diff -Nacr pgsql.orig/src/include/catalog/catversion.h pgsql/src/include/catalog/catversion.h
*** pgsql.orig/src/include/catalog/catversion.h	Fri Feb 16 18:06:34 2007
--- pgsql/src/include/catalog/catversion.h	Fri Feb 16 18:09:24 2007
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200702131
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200702151
  
  #endif
diff -Nacr pgsql.orig/src/include/catalog/pg_aggregate.h pgsql/src/include/catalog/pg_aggregate.h
*** pgsql.orig/src/include/catalog/pg_aggregate.h	Sun Jan 21 11:36:22 2007
--- pgsql/src/include/catalog/pg_aggregate.h	Fri Feb 16 18:09:24 2007
***************
*** 80,89 ****
   */
  
  /* avg */
! DATA(insert ( 2100	int8_accum		numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_accum	numeric_avg		0	1231	"{0,0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
--- 80,89 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
  DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
  DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	"{0,0}" ));
  DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
  DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
diff -Nacr pgsql.orig/src/include/catalog/pg_proc.h pgsql/src/include/catalog/pg_proc.h
*** pgsql.orig/src/include/catalog/pg_proc.h	Fri Feb 16 18:06:37 2007
--- pgsql/src/include/catalog/pg_proc.h	Fri Feb 16 18:09:24 2007
***************
*** 2744,2754 ****
--- 2744,2758 ----
  DESCR("STDDEV_SAMP aggregate final function");
  DATA(insert OID = 1833 (  numeric_accum    PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_accum - _null_ ));
  DESCR("aggregate transition function");
+ DATA(insert OID = 2858 (  numeric_avg_accum    PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 1700" _null_ _null_ _null_ numeric_avg_accum - _null_ ));
+ DESCR("aggregate transition function");
  DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 21" _null_ _null_ _null_ int2_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 23" _null_ _null_ _null_ int4_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1836 (  int8_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_accum - _null_ ));
+ DESCR("aggregate transition function");
+ DATA(insert OID = 2746 (  int8_avg_accum	   PGNSP PGUID 12 1 0 f f t f i 2 1231 "1231 20" _null_ _null_ _null_ int8_avg_accum - _null_ ));
  DESCR("aggregate transition function");
  DATA(insert OID = 1837 (  numeric_avg	   PGNSP PGUID 12 1 0 f f t f i 1 1700 "1231" _null_ _null_ _null_	numeric_avg - _null_ ));
  DESCR("AVG aggregate final function");
diff -Nacr pgsql.orig/src/include/utils/builtins.h pgsql/src/include/utils/builtins.h
*** pgsql.orig/src/include/utils/builtins.h	Fri Feb 16 18:06:37 2007
--- pgsql/src/include/utils/builtins.h	Fri Feb 16 18:09:24 2007
***************
*** 841,849 ****
--- 841,851 ----
  extern Datum text_numeric(PG_FUNCTION_ARGS);
  extern Datum numeric_text(PG_FUNCTION_ARGS);
  extern Datum numeric_accum(PG_FUNCTION_ARGS);
+ extern Datum numeric_avg_accum(PG_FUNCTION_ARGS);
  extern Datum int2_accum(PG_FUNCTION_ARGS);
  extern Datum int4_accum(PG_FUNCTION_ARGS);
  extern Datum int8_accum(PG_FUNCTION_ARGS);
+ extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
  extern Datum numeric_avg(PG_FUNCTION_ARGS);
  extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
  extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
diff -Nacr pgsql.orig/src/test/regress/expected/create_aggregate.out pgsql/src/test/regress/expected/create_aggregate.out
*** pgsql.orig/src/test/regress/expected/create_aggregate.out	Sat Jul 29 13:45:35 2006
--- pgsql/src/test/regress/expected/create_aggregate.out	Sat Feb 17 12:05:39 2007
***************
*** 3,11 ****
  --
  -- all functions CREATEd
  CREATE AGGREGATE newavg (
!    sfunc = int4_accum, basetype = int4, stype = _numeric, 
!    finalfunc = numeric_avg,
!    initcond1 = '{0,0,0}'
  );
  -- test comments
  COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment';
--- 3,11 ----
  --
  -- all functions CREATEd
  CREATE AGGREGATE newavg (
!    sfunc = int4_avg_accum, basetype = int4, stype = _int8, 
!    finalfunc = int8_avg,
!    initcond1 = '{0,0}'
  );
  -- test comments
  COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment';
diff -Nacr pgsql.orig/src/test/regress/sql/create_aggregate.sql pgsql/src/test/regress/sql/create_aggregate.sql
*** pgsql.orig/src/test/regress/sql/create_aggregate.sql	Sat Jul 29 13:45:35 2006
--- pgsql/src/test/regress/sql/create_aggregate.sql	Sat Feb 17 12:00:37 2007
***************
*** 4,12 ****
  
  -- all functions CREATEd
  CREATE AGGREGATE newavg (
!    sfunc = int4_accum, basetype = int4, stype = _numeric, 
!    finalfunc = numeric_avg,
!    initcond1 = '{0,0,0}'
  );
  
  -- test comments
--- 4,12 ----
  
  -- all functions CREATEd
  CREATE AGGREGATE newavg (
!    sfunc = int4_avg_accum, basetype = int4, stype = _int8, 
!    finalfunc = int8_avg,
!    initcond1 = '{0,0}'
  );
  
  -- test comments
#27Bruce Momjian
bruce@momjian.us
In reply to: Mark Kirkwood (#26)
Re: Avg performance for int8/numeric

Mark Kirkwood wrote:

Bruce Momjian wrote:

I have tested this patch but it generates regression failures.

There was some code drift, so I am attaching an updated version of the
patch, and the regression diffs. The 'four' column is an 'int4' so my
guess is that somehow the wrong aggregate is being called.

Good catch - I must have neglected to run the regression test after
amending the number of array arguments for the numeric avg :-(.

Hmmm - this changing the number of array args for avg means we can't mix
transition functions for variance with final functions for avg - which
is exactly what the regression suite does with the 'newavg' aggregate.

Yea, I was just looking at this and came to same conclusion.

I've 'fixed' this by amending the definition of 'newavg' to use the
transition and final function that 'avg' does. However I found myself
asking if this lost us the point of that test - so I looked back at the
older postgres versions (e.g. 7.1.3) and saw that back *then* 'newavg'
and 'avg' were defined using the same functions...so I think making the
change as indicated is ok.

I've attached a new patch with this change.

OK, great, will apply.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#28Bruce Momjian
bruce@momjian.us
In reply to: Mark Kirkwood (#26)
Re: Avg performance for int8/numeric

Patch applied. Thanks.

---------------------------------------------------------------------------

Mark Kirkwood wrote:

Bruce Momjian wrote:

I have tested this patch but it generates regression failures.

There was some code drift, so I am attaching an updated version of the
patch, and the regression diffs. The 'four' column is an 'int4' so my
guess is that somehow the wrong aggregate is being called.

Good catch - I must have neglected to run the regression test after
amending the number of array arguments for the numeric avg :-(.

Hmmm - this changing the number of array args for avg means we can't mix
transition functions for variance with final functions for avg - which
is exactly what the regression suite does with the 'newavg' aggregate.

I've 'fixed' this by amending the definition of 'newavg' to use the
transition and final function that 'avg' does. However I found myself
asking if this lost us the point of that test - so I looked back at the
older postgres versions (e.g. 7.1.3) and saw that back *then* 'newavg'
and 'avg' were defined using the same functions...so I think making the
change as indicated is ok.

I've attached a new patch with this change.

Cheers

Mark

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +