On Wed, Jun 28, 2023 at 4:50 PM Joel Jacobson <j...@compiler.org> wrote:
>
> On Wed, Jun 28, 2023, at 08:26, jian he wrote:
>
> > Hi there.
> > I changed the function hashset_contains to strict.
>
> Changing hashset_contains to STRICT would cause it to return NULL
> if any of the operands are NULL, which I don't believe is correct, since:
>
> SELECT NULL = ANY('{}'::int4[]);
>  ?column?
> ----------
>  f
> (1 row)
>
> Hence, `hashset_contains('{}'::int4hashset, NULL)` should also return FALSE,
> to mimic the semantics of arrays and MULTISET's MEMBER OF predicate in 
> SQL:2023.
>
> Did you try running `make installcheck` after your change?
> You would then have seen one of the tests failing:
>
> test array-and-multiset-semantics ... FAILED       21 ms
>
> Check the content of `regression.diffs` to see why:
>
> % cat regression.diffs
> diff -U3 
> /Users/joel/src/hashset/test/expected/array-and-multiset-semantics.out 
> /Users/joel/src/hashset/results/array-and-multiset-semantics.out
> --- /Users/joel/src/hashset/test/expected/array-and-multiset-semantics.out    
>   2023-06-27 10:07:38
> +++ /Users/joel/src/hashset/results/array-and-multiset-semantics.out    
> 2023-06-28 10:13:27
> @@ -158,7 +158,7 @@
>          |      | {NULL}      | {NULL}       |                  |
>          |    1 | {1}         | {1}          |                  |
>          |    4 | {4}         | {4}          |                  |
> - {}     |      | {NULL}      | {NULL}       | f                | f
> + {}     |      | {NULL}      | {NULL}       |                  | f
>   {}     |    1 | {1}         | {1}          | f                | f
>   {}     |    4 | {4}         | {4}          | f                | f
>   {NULL} |      | {NULL}      | {NULL,NULL}  |                  |
> @@ -284,7 +284,8 @@
>      "= ANY(...)";
>   arg1 | arg2 | hashset_add | array_append | hashset_contains | = ANY(...)
>  ------+------+-------------+--------------+------------------+------------
> -(0 rows)
> + {}   |      | {NULL}      | {NULL}       |                  | f
> +(1 row)
>
>
> > also change the way to return an empty array.
>
> Nice.
> I agree the `Datum d` variable was unnecessary.
> I also removed the unused includes.
>
> > in benchmark.sql, would it be ok to use EXPLAIN to demonstrate that
> > int4hashset can speed distinct aggregate and distinct counts?
> > like the following:
> >
> > explain(analyze, costs off, timing off, buffers)
> > SELECT array_agg(DISTINCT i) FROM benchmark_input_100k \watch c=3
> >
> > explain(analyze, costs off, timing off, buffers)
> > SELECT hashset_agg(i) FROM benchmark_input_100k \watch c=3
>
> The 100k tables seems to be too small to give any meaningful results,
> when trying to measure individual queries:
>
> EXPLAIN(analyze, costs off, timing off, buffers)
> SELECT array_agg(DISTINCT i) FROM benchmark_input_100k;
>  Execution Time: 26.790 ms
>  Execution Time: 30.616 ms
>  Execution Time: 33.253 ms
>
> EXPLAIN(analyze, costs off, timing off, buffers)
> SELECT hashset_agg(i) FROM benchmark_input_100k;
>  Execution Time: 32.797 ms
>  Execution Time: 27.605 ms
>  Execution Time: 26.228 ms
>
> If we instead try the 10M tables, it looks like array_agg(DISTINCT ...)
> is actually faster for the `i` column where all input integers are unique:
>
> EXPLAIN(analyze, costs off, timing off, buffers)
> SELECT array_agg(DISTINCT i) FROM benchmark_input_10M;
>  Execution Time: 799.017 ms
>  Execution Time: 796.008 ms
>  Execution Time: 799.121 ms
>
> EXPLAIN(analyze, costs off, timing off, buffers)
> SELECT hashset_agg(i) FROM benchmark_input_10M;
>  Execution Time: 1204.873 ms
>  Execution Time: 1221.822 ms
>  Execution Time: 1216.340 ms
>
> For random integers, hashset is a win though:
>
> EXPLAIN(analyze, costs off, timing off, buffers)
> SELECT array_agg(DISTINCT rnd) FROM benchmark_input_10M;
>  Execution Time: 1874.722 ms
>  Execution Time: 1878.760 ms
>  Execution Time: 1861.640 ms
>
> EXPLAIN(analyze, costs off, timing off, buffers)
> SELECT hashset_agg(rnd) FROM benchmark_input_10M;
>  Execution Time: 1253.709 ms
>  Execution Time: 1222.651 ms
>  Execution Time: 1237.849 ms
>
> > explain(costs off,timing off, analyze,buffers)
> > select count(distinct rnd) from benchmark_input_100k \watch c=3
> >
> > explain(costs off,timing off, analyze,buffers)
> > SELECT hashset_cardinality(x) FROM (SELECT hashset_agg(rnd) FROM
> > benchmark_input_100k) sub(x) \watch c=3
>
> I tried these with 10M:
>
> EXPLAIN(costs off,timing off, analyze,buffers)
> SELECT COUNT(DISTINCT rnd) FROM benchmark_input_10M;
>  Execution Time: 1733.320 ms
>  Execution Time: 1725.214 ms
>  Execution Time: 1716.636 ms
>
> EXPLAIN(costs off,timing off, analyze,buffers)
> SELECT hashset_cardinality(x) FROM (SELECT hashset_agg(rnd) FROM 
> benchmark_input_10M) sub(x);
>  Execution Time: 1249.612 ms
>  Execution Time: 1240.558 ms
>  Execution Time: 1252.103 ms
>
> Not sure what I think of the current benchmark suite.
>
> I think it would be better to only include some realistic examples from
> real-life, such as the graph query which was the reason I personally started
> working on this. Otherwise there is a risk we optimise for some hypothetical
> scenario that is not relevant in practise.
>
> Would be good with more examples of typical work loads for when the hashset
> type would be useful.
>
> /Joel

> Did you try running `make installcheck` after your change?

First I use make installcheck
PG_CONFIG=/home/jian/postgres/2023_05_25_beta5421/bin/pg_config
I found out it uses another active cluster.
So I killed another active cluster.
later i found another database port so I took me sometime to found out
I need use following:
make installcheck
PG_CONFIG=/home/jian/postgres/2023_05_25_beta5421/bin/pg_config
PGPORT=5421

Anyway, this time, I added another macro,which seems to simplify the code.

#define SET_DATA_PTR(a) \
(((char *) (a->data)) + CEIL_DIV(a->capacity, 8))

it passed all the tests on my local machine.
I should have only made a patch, but when I was committed, I forgot to
mention one file, so I needed 2 commits.


> Not sure what I think of the current benchmark suite.
your result is so different from mine. I use the default  config. I
see a big difference. yech, I agree, the performance test should be
more careful.
From ece7e6bf34facb67c7a938b2862f3d3af06aefc0 Mon Sep 17 00:00:00 2001
From: pgaddict <jian.universal...@gmail.com>
Date: Thu, 29 Jun 2023 14:27:02 +0800
Subject: [PATCH 2/2] marco SET_DATA_PTR to quicly access hashset data region

---
 hashset-api.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/hashset-api.c b/hashset-api.c
index 28eb2387..2d856b47 100644
--- a/hashset-api.c
+++ b/hashset-api.c
@@ -194,7 +194,7 @@ int4hashset_out(PG_FUNCTION_ARGS)
 
 	/* Calculate the pointer to the bitmap and values array */
 	bitmap = set->data;
-	values = (int32 *) (set->data + CEIL_DIV(set->capacity, 8));
+	values = (int32 *) SET_DATA_PTR(set);
 
 	/* Initialize the StringInfo buffer */
 	initStringInfo(&str);
@@ -411,7 +411,7 @@ int4hashset_union(PG_FUNCTION_ARGS)
 	int4hashset_t  *seta = PG_GETARG_INT4HASHSET_COPY(0);
 	int4hashset_t  *setb = PG_GETARG_INT4HASHSET(1);
 	char		   *bitmap = setb->data;
-	int32		   *values = (int32 *) (bitmap + CEIL_DIV(setb->capacity, 8));
+	int32		   *values = (int32 *) SET_DATA_PTR(seta);
 
 	for (i = 0; i < setb->capacity; i++)
 	{
@@ -598,7 +598,7 @@ int4hashset_agg_add_set(PG_FUNCTION_ARGS)
 		value = PG_GETARG_INT4HASHSET(1);
 
 		bitmap = value->data;
-		values = (int32 *) (value->data + CEIL_DIV(value->capacity, 8));
+		values = (int32 *) SET_DATA_PTR(value);
 
 		for (i = 0; i < value->capacity; i++)
 		{
@@ -665,7 +665,7 @@ int4hashset_agg_combine(PG_FUNCTION_ARGS)
 	dst = (int4hashset_t *) PG_GETARG_POINTER(0);
 
 	bitmap = src->data;
-	values = (int32 *) (src->data + CEIL_DIV(src->capacity, 8));
+	values = (int32 *) SET_DATA_PTR(src);
 
 	for (i = 0; i < src->capacity; i++)
 	{
@@ -698,7 +698,7 @@ int4hashset_to_array(PG_FUNCTION_ARGS)
 		PG_RETURN_ARRAYTYPE_P(construct_empty_array(INT4OID));
 
 	sbitmap = set->data;
-	svalues = (int32 *) (set->data + CEIL_DIV(set->capacity, 8));
+	svalues = (int32 *) (int32 *) SET_DATA_PTR(set);
 
 	/* number of values to store in the array */
 	nvalues = set->nelements;
@@ -757,7 +757,7 @@ int4hashset_eq(PG_FUNCTION_ARGS)
 		PG_RETURN_BOOL(false);
 
 	bitmap_a = a->data;
-	values_a = (int32 *)(a->data + CEIL_DIV(a->capacity, 8));
+	values_a = (int32 *) SET_DATA_PTR(a);
 
 	/*
 	 * Check if every element in a is also in b
@@ -935,7 +935,8 @@ int4hashset_intersection(PG_FUNCTION_ARGS)
 	int4hashset_t  *seta = PG_GETARG_INT4HASHSET(0);
 	int4hashset_t  *setb = PG_GETARG_INT4HASHSET(1);
 	char		   *bitmap = setb->data;
-	int32		   *values = (int32 *)(bitmap + CEIL_DIV(setb->capacity, 8));
+	int32		   *values = (int32 *) SET_DATA_PTR(setb);
+	
 	int4hashset_t  *intersection;
 
 	intersection = int4hashset_allocate(
@@ -971,7 +972,7 @@ int4hashset_difference(PG_FUNCTION_ARGS)
 	int4hashset_t	*setb = PG_GETARG_INT4HASHSET(1);
 	int4hashset_t	*difference;
 	char			*bitmap = seta->data;
-	int32			*values = (int32 *)(bitmap + CEIL_DIV(seta->capacity, 8));
+	int32			*values = (int32 *) SET_DATA_PTR(seta);
 
 	difference = int4hashset_allocate(
 		seta->capacity,
@@ -1007,8 +1008,8 @@ int4hashset_symmetric_difference(PG_FUNCTION_ARGS)
 	int4hashset_t  *result;
 	char		   *bitmapa = seta->data;
 	char		   *bitmapb = setb->data;
-	int32		   *valuesa = (int32 *) (bitmapa + CEIL_DIV(seta->capacity, 8));
-	int32		   *valuesb = (int32 *) (bitmapb + CEIL_DIV(setb->capacity, 8));
+	int32		   *valuesa = (int32 *) SET_DATA_PTR(seta);
+	int32		   *valuesb = (int32 *) SET_DATA_PTR(setb);
 
 	result = int4hashset_allocate(
 		seta->nelements + setb->nelements,
-- 
2.34.1

From f94c1261f691c6473cd27de2a8d9465f77e73b40 Mon Sep 17 00:00:00 2001
From: pgaddict <jian.universal...@gmail.com>
Date: Thu, 29 Jun 2023 14:20:00 +0800
Subject: [PATCH 1/2]  marco SET_DATA_PTR to quicly access hashset data region

---
 hashset.c | 8 ++++----
 hashset.h | 3 +++
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/hashset.c b/hashset.c
index 91907abc..d786d379 100644
--- a/hashset.c
+++ b/hashset.c
@@ -82,7 +82,7 @@ int4hashset_resize(int4hashset_t * set)
 
 	/* Calculate the pointer to the bitmap and values array */
 	bitmap = set->data;
-	values = (int32 *) (set->data + CEIL_DIV(set->capacity, 8));
+	values = (int32 *) SET_DATA_PTR(set);
 
 	for (i = 0; i < set->capacity; i++)
 	{
@@ -132,7 +132,7 @@ int4hashset_add_element(int4hashset_t *set, int32 value)
     position = hash % set->capacity;
 
 	bitmap = set->data;
-	values = (int32 *) (set->data + CEIL_DIV(set->capacity, 8));
+	values = (int32 *) SET_DATA_PTR(set);
 
 	while (true)
 	{
@@ -204,7 +204,7 @@ int4hashset_contains_element(int4hashset_t *set, int32 value)
 	position = hash % set->capacity;
 
 	bitmap = set->data;
-	values = (int32 *) (set->data + CEIL_DIV(set->capacity, 8));
+	values = (int32 *) SET_DATA_PTR(set);
 
 	while (true)
 	{
@@ -238,7 +238,7 @@ int4hashset_extract_sorted_elements(int4hashset_t *set)
 
 	/* Access the data array */
 	char *bitmap = set->data;
-	int32 *values = (int32 *)(set->data + CEIL_DIV(set->capacity, 8));
+	int32 *values = (int32 *) SET_DATA_PTR(set);
 
 	/* Counter for the number of extracted elements */
 	int32 nextracted = 0;
diff --git a/hashset.h b/hashset.h
index 86f5d1b0..2513ab55 100644
--- a/hashset.h
+++ b/hashset.h
@@ -42,6 +42,9 @@ typedef struct int4hashset_t {
 	char		data[FLEXIBLE_ARRAY_MEMBER];
 } int4hashset_t;
 
+#define SET_DATA_PTR(a) \
+		(((char *) (a->data)) + CEIL_DIV(a->capacity, 8))
+				
 int4hashset_t *int4hashset_allocate(int capacity, float4 load_factor, float4 growth_factor, int hashfn_id);
 int4hashset_t *int4hashset_resize(int4hashset_t * set);
 int4hashset_t *int4hashset_add_element(int4hashset_t *set, int32 value);
-- 
2.34.1

Reply via email to