Add bump memory context type and use it for tuplesorts

Started by David Rowleyover 2 years ago58 messages
#1David Rowley
dgrowleyml@gmail.com
5 attachment(s)

Background:

The idea with 40af10b57 (Use Generation memory contexts to store
tuples in sorts) was to reduce the memory wastage in tuplesort.c
caused by aset.c's power-of-2 rounding up behaviour and allow more
tuples to be stored per work_mem in tuplesort.c

Later, in (v16's) c6e0fe1f2a (Improve performance of and reduce
overheads of memory management) that commit reduced the palloc chunk
header overhead down to 8 bytes. For generation.c contexts (as is now
used by non-bounded tuplesorts as of 40af10b57), the overhead was 24
bytes. So this allowed even more tuples to be stored in a work_mem by
reducing the chunk overheads for non-bounded tuplesorts by 2/3rds down
to 16 bytes.

1083f94da (Be smarter about freeing tuples during tuplesorts) removed
the useless pfree() calls from tuplesort.c which pfree'd the tuples
just before we reset the context. So, as of now, we never pfree()
memory allocated to store tuples in non-bounded tuplesorts.

My thoughts are, if we never pfree tuples in tuplesorts, then why
bother having a chunk header at all?

Proposal:

Because of all of what is mentioned above about the current state of
tuplesort, there does not really seem to be much need to have chunk
headers in memory we allocate for tuples at all. Not having these
saves us a further 8 bytes per tuple.

In the attached patch, I've added a bump memory allocator which
allocates chunks without and chunk header. This means the chunks
cannot be pfree'd or realloc'd. That seems fine for the use case for
storing tuples in tuplesort. I've coded bump.c in such a way that when
built with MEMORY_CONTEXT_CHECKING, we *do* have chunk headers. That
should allow us to pick up any bugs that are introduced by any code
which accidentally tries to pfree a bump.c chunk.

I'd expect a bump.c context only to be used for fairly short-lived and
memory that's only used by a small amount of code (e.g. only accessed
from a single .c file, like tuplesort.c). That should reduce the risk
of any code accessing the memory which might be tempted into calling
pfree or some other unsupported function.

Getting away from the complexity of freelists (aset.c) and tracking
allocations per block (generation.c) allows much better allocation
performance. All we need to do is check there's enough space then
bump the free pointer when performing an allocation. See the attached
time_to_allocate_10gbs_memory.png to see how bump.c compares to aset.c
and generation.c to allocate 10GBs of memory resetting the context
after 1MB. It's not far off twice as fast in raw allocation speed.
(See 3rd tab in the attached spreadsheet)

Performance:

In terms of the speed of palloc(), the performance tested on an AMD
3990x CPU on Linux for 8-byte chunks:

aset.c 9.19 seconds
generation.c 8.68 seconds
bump.c 4.95 seconds

These numbers were generated by calling:
select stype,chksz,pg_allocate_memory_test_reset(chksz,1024*1024,10::bigint*1024*1024*1024,stype)
from (values(8),(16),(32),(64),(128)) t1(chksz) cross join
(values('aset'),('generation'),('bump')) t2(stype) order by
stype,chksz;

This allocates a total of 10GBs of chunks but calls a context reset
after 1MB so as not to let the memory usage get out of hand. The
function is in the attached membench.patch.txt file.

In terms of performance of tuplesort, there's a small (~5-6%)
performance gain. Not as much as I'd hoped, but I'm also doing a bit
of other work on tuplesort to make it more efficient in terms of CPU,
so I suspect the cache efficiency improvements might be more
pronounced after those.
Please see the attached bump_context_tuplesort_2023-06-27.ods for my
complete benchmark.

One thing that might need more thought is that we're running a bit low
on MemoryContextMethodIDs. I had to use an empty slot that has a bit
pattern like glibc malloc'd chunks sized 128kB. Maybe it's worth
freeing up a bit from the block offset in MemoryChunk. This is
currently 30 bits allowing 1GB offset, but these offsets are always
MAXALIGNED, so we could free up a couple of bits since those 2
lowest-order bits will always be 0 anyway.

I've attached the bump allocator patch and also the script I used to
gather the performance results in the first 2 tabs in the attached
spreadsheet.

David

Attachments:

membench.patch.txttext/plain; charset=US-ASCII; name=membench.patch.txtDownload
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 92ca5b2f72..68e03b2edb 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -15,6 +15,8 @@
 
 #include "postgres.h"
 
+#include <time.h>
+
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "mb/pg_wchar.h"
@@ -193,3 +195,218 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(true);
 }
+
+typedef struct AllocateTestNext
+{
+	struct AllocateTestNext *next;		/* ptr to the next allocation */
+} AllocateTestNext;
+
+/* #define ALLOCATE_TEST_DEBUG */
+/*
+ * pg_allocate_memory_test
+ *		Used to test the performance of a memory context types
+ */
+Datum
+pg_allocate_memory_test(PG_FUNCTION_ARGS)
+{
+	int32	chunk_size = PG_GETARG_INT32(0);
+	int64	keep_memory = PG_GETARG_INT64(1);
+	int64	total_alloc = PG_GETARG_INT64(2);
+	text   *context_type_text = PG_GETARG_TEXT_PP(3);
+	char   *context_type;
+	int64	curr_memory_use = 0;
+	int64	remaining_alloc_bytes = total_alloc;
+	MemoryContext context;
+	MemoryContext oldContext;
+	AllocateTestNext	   *next_free_ptr = NULL;
+	AllocateTestNext	   *last_alloc = NULL;
+	clock_t	start, end;
+
+	if (chunk_size < sizeof(AllocateTestNext))
+		elog(ERROR, "chunk_size (%d) must be at least %ld bytes", chunk_size,
+			 sizeof(AllocateTestNext));
+	if (keep_memory > total_alloc)
+		elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")",
+			 keep_memory, total_alloc);
+
+	context_type = text_to_cstring(context_type_text);
+
+	start = clock();
+
+	if (strcmp(context_type, "generation") == 0)
+		context = GenerationContextCreate(CurrentMemoryContext,
+										  "pg_allocate_memory_test",
+										  ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "aset") == 0)
+		context = AllocSetContextCreate(CurrentMemoryContext,
+										"pg_allocate_memory_test",
+										ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "slab") == 0)
+		context = SlabContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_MAXSIZE,
+									chunk_size);
+	else
+		elog(ERROR, "context_type must be \"generation\", \"aset\" or \"slab\"");
+
+	oldContext = MemoryContextSwitchTo(context);
+
+	while (remaining_alloc_bytes > 0)
+	{
+		AllocateTestNext *curr_alloc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/* Allocate the memory and update the counters */
+		curr_alloc = (AllocateTestNext *) palloc(chunk_size);
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		/*
+		 * Point the last allocate to this one so that we can free allocations
+		 * starting with the oldest first.
+		 */
+		curr_alloc->next = NULL;
+		if (last_alloc != NULL)
+			last_alloc->next = curr_alloc;
+
+		if (next_free_ptr == NULL)
+		{
+			/*
+			 * Remember the first chunk to free. We will follow the ->next
+			 * pointers to find the next chunk to free when freeing memory
+			 */
+			next_free_ptr = curr_alloc;
+		}
+
+		/*
+		 * If the currently allocated memory has reached or exceeded the amount
+		 * of memory we want to keep allocated at once then we'd better free
+		 * some.  Since all allocations are the same size we only need to free
+		 * one allocation per loop.
+		 */
+		if (curr_memory_use >= keep_memory)
+		{
+			AllocateTestNext	 *next = next_free_ptr->next;
+
+			/* free the memory and update the current memory usage */
+			pfree(next_free_ptr);
+			curr_memory_use -= chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+			elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes);
+#endif
+			/* get the next chunk to free */
+			next_free_ptr = next;
+		}
+
+		if (curr_memory_use > 0)
+			last_alloc = curr_alloc;
+		else
+			last_alloc = NULL;
+	}
+
+	/* cleanup loop -- pfree remaining memory */
+	while (next_free_ptr != NULL)
+	{
+		AllocateTestNext	 *next = next_free_ptr->next;
+
+		/* free the memory and update the current memory usage */
+		pfree(next_free_ptr);
+		curr_memory_use -= chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		next_free_ptr = next;
+	}
+
+	MemoryContextSwitchTo(oldContext);
+
+	end = clock();
+
+	PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC);
+}
+
+Datum
+pg_allocate_memory_test_reset(PG_FUNCTION_ARGS)
+{
+	int32	chunk_size = PG_GETARG_INT32(0);
+	int64	keep_memory = PG_GETARG_INT64(1);
+	int64	total_alloc = PG_GETARG_INT64(2);
+	text   *context_type_text = PG_GETARG_TEXT_PP(3);
+	char   *context_type;
+	int64	curr_memory_use = 0;
+	int64	remaining_alloc_bytes = total_alloc;
+	MemoryContext context;
+	MemoryContext oldContext;
+	clock_t	start, end;
+
+	if (chunk_size < 1)
+		elog(ERROR, "size of chunk must be above 0");
+	if (keep_memory > total_alloc)
+		elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")",
+			 keep_memory, total_alloc);
+
+	context_type = text_to_cstring(context_type_text);
+
+	start = clock();
+
+	if (strcmp(context_type, "generation") == 0)
+		context = GenerationContextCreate(CurrentMemoryContext,
+										  "pg_allocate_memory_test",
+										  ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "aset") == 0)
+		context = AllocSetContextCreate(CurrentMemoryContext,
+										"pg_allocate_memory_test",
+										ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "slab") == 0)
+		context = SlabContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_MAXSIZE,
+									chunk_size);
+	else if (strcmp(context_type, "bump") == 0)
+		context = BumpContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_SIZES);
+	else
+		elog(ERROR, "context_type must be \"generation\", \"aset\" or \"slab\"");
+
+	oldContext = MemoryContextSwitchTo(context);
+
+	while (remaining_alloc_bytes > 0)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		/* Allocate the memory and update the counters */
+		(void) palloc(chunk_size);
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		/*
+		 * If the currently allocated memory has reached or exceeded the amount
+		 * of memory we want to keep allocated at once then reset the context.
+		 */
+		if (curr_memory_use >= keep_memory)
+		{
+			curr_memory_use = 0;
+			MemoryContextReset(context);
+		}
+	}
+
+	MemoryContextSwitchTo(oldContext);
+	MemoryContextDelete(context);
+
+	end = clock();
+
+	PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..894c649b0c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8223,6 +8223,18 @@
   prorettype => 'bool', proargtypes => 'int4',
   prosrc => 'pg_log_backend_memory_contexts' },
 
+# just for testing memory context allocation speed
+{ oid => '9319', descr => 'for testing performance of allocation and freeing',
+  proname => 'pg_allocate_memory_test', provolatile => 'v',
+  prorettype => 'float8', proargtypes => 'int4 int8 int8 text',
+  prosrc => 'pg_allocate_memory_test' },
+
+# just for testing memory context allocation speed
+{ oid => '9320', descr => 'for testing performance of allocation and resetting',
+  proname => 'pg_allocate_memory_test_reset', provolatile => 'v',
+  prorettype => 'float8', proargtypes => 'int4 int8 int8 text',
+  prosrc => 'pg_allocate_memory_test_reset' },
+
 # non-persistent series generator
 { oid => '1066', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
time_to_allocate_10gbs_memory.pngimage/png; name=time_to_allocate_10gbs_memory.pngDownload
bump_context_tuplesort_2023-06-27.odsapplication/vnd.oasis.opendocument.spreadsheet; name=bump_context_tuplesort_2023-06-27.odsDownload
PK�F�V�l9�..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK�F�VObjectReplacements/Object 3�\[h\E�7����M�1���5�������R/�fkC�$��
��c�M��>c���J[D|�+"� ���E�IDD�-�+B�����3g2'�3��m&9��s�����o���?g���<��/����o���'.����z���i�B���xTf;-KE�e� L����a9,�6*��?sO��\�y�:�7_���(��������m#X���3r����MX�|{7d�V3�E�^
��
�_��_������|Q�	IT>;;Ky8�?n����������{����'�w����3�v'�����B'kU-����1�:?��M��w�l`�#H<�*r��x��vu������:DSt���=!k�.;Hz����=!k�.;Hz�5Q�{B�]v����Z�	Yct�A����\�������7]������������tMOO�H����{}�t�&(��I�nyt�����K4���N�	�6��<�k���n��%�,�v�a�A��X�!Vn���e�h�'�{����-������N[�cn�fR��:������B`���P������p����������/�9��M��n[n����Z��J��kN@mt���[��y+����R`���P��������b���%�����kN@mt���[��<�����m��G���P������-`k�z�zf��{��ZXsj��P���9��zi�||N-�9��M��n�����E�|N-�9��M��n��|���%|N-�9��M��n���Ji.�sja�	��nB@]pw(����������5'�6�	u��-��C_(�v�����5'�6�	u�WV@*DS}�p_�����+����',���u,�0��l�>:<p��<�M�X��a;/�O�b^[�^���q��(�:�O�� _����r�����4
�� �����~^�\��S�]�t��aF�Z��
�0"]�t��Y�9#�2�g1����sy�����v��]yJWT�
��]jW�K�
���"��X���f�����#T.�����q��(�l>}��4*���o����Tw-j<�)����i�G�,��(�Z�l��P��U�v�S��JWf��{h�"���|��:D�F�(c4a$������N��������0��q�D~����(�Z�l�eL��������
��l��
�&�`jU�a�
K�J��|���7�y��q~n�3��@e�T��ce8l�aj�Q
Ka8
�V!����P�����i��h���L7�N[���p$�!��P���Kb8
�Va
T6jV7j.�-�x3H�KEM���3��
*Og�J���a��{L�B�~-���i���j��Q��(K0E��4{��+T�BjW@��T��������x������U��J#;����Q��05+k�0��=�����a/�9aMq���z�6�Q�	k�#�9K�3a�r�Z�k��L�W����8�Z��9���	k�#�m5	��Z�7:���,������8�z�QZ���>7�������r� �_)�by?;K���~�.�L%����x/��|Q���I�����4"C�+��J�WSS�.q�x|Y��y1�r��������4���\0g-���'�;�J;
�U]����	����_���]�y�����l%����	����	�qt��E��c4�J�m~���^9����5�76WmKd�<v�Zhc;j�Vi%�#������b��~�Mi�,��XT���KmtI�[~S�?u�&aa8��~��
��u}�C���#�j��=4�O��9`�>Q�*��PKe�F�QPK�F�VObjectReplacements/Object 2s��
qcd0d�Fid����10pz00��@!& ��#�%�������B��	�R���0�EA���(:�e�JN�j�����
d��-������A>P���l��g���9�u=Pg�h2��nPK��;��]PK�F�VObjectReplacements/Object 1s��
qcd0d�Fid����10��``���BL@���F KI�P�r�
�& K�b[��*F���T �*9
��n3C+��
��?c��@f`w�1HC�	0�|do ��@���p:�PK�]9�]PK�F�VObjectReplacements/Object 5�]}�TW���������|,B�+3�~Q�,��n��-�����|�.�l�����	b�M��?�&�*Q�&jcj�`Ec�Jb
��h��j�s���}��g�9�a�=�������w��w�}���a6D���[A�����%��-����bN��!����ga��q�f�kb���N�������TT.r���c z���6~1zx���'ak�"���o�r�hRy����NS.����4�~g�i$����B��L��\����#e�sc��|�����){��:z��u�n���������:������S�+9%��(���G�{	-C����;����}���U����A[�RP|���w��=���V��T"�N����G!��D69�$�-���:S�lr8I�[���u���p������Jd��I��r�+��Jd��I��r��L%���$Io9�su���p����Y:S�lr8I��8}�t�r��">8&-�J�C�X�����9�{��r����Aq��<������W���#��G��Bq*��{�~�U>�%����\�J�#V'D��-�m�Y�9h���B��"�sD%�k����,�r���*��g��� *�]�^������� ;G�3���a���A��tKw��*T�� 2;�ATr���Cgqc��<���8"�Adv���v
:�Ug��6��!;G�3���a���Am�Y,�l��P�8"�Adv���v
����o�
>��8"�Adv���v
z�Wg��>��Av2��g��� *�]���Y�|��SEB��"�sD%�k����,~����b���|��9���5�x��b��W ;G�3���a���A�WFt��`_�PqD>���Q�����:��E���8"�Adv���v
:���b�2�/�
G�3���a���A�Kt93XX&T�� 2;�ATr�]n�Y|���d'��|��9���5hk��b]���
���|��9���5(;�����W �l�Df�0�Jn���5�,^����J���|��9���5�{��b��7 ;G�3���a���A�����\�o@B2��g��� *�]�^����|��m�B��"�sD%�kP��E�L�� ;G�3���a���A�jt�j
�L*��g��� *�]��V�,NT�6G�3���a���A�r���
���M��"�sD%�k���:��[�
p��8"�Adv���v
z�@g1Rh08]�8"�Adv���v
������7x��qD>���Q��t%Z��$�����#�Df�0�J�mPc�v��8��#'��bD>���Q����1m��3_� �Df�0�Jn� ,��5h��@� �Df�0�Jn� ,�9��`w�.�A�3���a���AX�cpe�.�A�3���a���AX��N���t�"�Adv���v
������I�.�A�3���a���AXS����b],��g��� *�]��8��F��(��2�|��9���5�c�7X\��e�"�sD%�k���3x�T� �Df�0�Jn� ,��~���2],��g��� *�]��8���/��bD>���Q����1�?�E|�\� �Df�0�Jn� ,���
���bD>���Q���c��b}��s�X�� 2;�ATr�aq������2�|��9���5�c.,6x�R� �Df�0�Jn� ,�Y�lpi�.�A�3���a���AX����*u�"�Adv���v
���s
>Q��e�"�sD%�k��>h02M� �Df�0�Jn� ,���<�]�t�"�Adv���v
����sN���2�|��9���5�c��2��],��g��� *�]��8���
6��bD>���Q����1G��l�.�A�3���a���AXSSipx�.�A�3���a���AX�f��p�.�A�3���a���AX����w�t�"�Adv���v
���������2�|��9���5�c.��2W� �Df�0�J�Z���uebc_����=m�]}�m=�F(���7+��*���}��i  ��U~��U��;*�f���D�
���g?*����0�-{v�g!��3�\�aP"�^W[A������h����;gO���g��-�W�\�'���B12�|�P��B#S8F��/�4\bdZ#�"_&����F���}�P�9�F����|�P�gj�L�125�2�L��02�������2-�72������$e���e���B����B
�;MD�H��i,+yf�S����	>�g��7�J�\�L��>C�\r*vU2���U���N%�*��� C�|���d���	N2T��%N%�\�Lp~��J~�a����J&8�P%�"N%[\�Lp���JnY�T��U�'2���N�d�u���8$%�:�t����9I��=N%��8!�CR��^��ns��?�!)Y��T�m���8$%�mr*�6�	�s��W�:�t����9I�N%��8!�CR��.��ns��?�!)��^��ns��?�!)���Q2�:�	�s��_r*�6�	�s��:�T�m���8�_f�c�Um�������N���2�?��u�,�LR�����;)�, ���h[�:^$��i���P���(����5f����������P������G��=�g��xk������'?U��d0��-���R#��#���-���R#���Fl�[[����F�����^����wk�~������Q!Ff	�q^�)D�1�2���j�>'���T���r�d�Hm�]oRI�)�t��^g�xn��Q�q?>�����D����UUUjx{�_��=t2l��N����0�����y1����^�C���t�a���?����f�>�=
���6>����F���.���i�j��d{7��n�����������4����"��M��?��i�=�`����PG�e��g�OB�;[#�uHB�<c�0�w�pK�G���/
}!'z�2m�8��\^�������y`��K����C@����a]��2��@;#���R$����?�cDrArW{$��i�w���|o���"S��
�?��)R��T����Mcm���nm���]�G�!u1�����=�������XO�C�b����*�����h�10���8��������!g���3�Cg�kz�`]��������,��$[RY���&Oq���[�@���I
 �b��o.��H�wS�wS�wS�wS�g�}0�='��DG����2x�(W�'��r\��`,��f&�`��#�^�3Cbl�c�Q8�-����h8:�u����3������Z��UXO�u�"�Pt	��Cp���	�t�r�i��gRt_�r)���R7���`�N����pQ�^�_O���;m>%�wE�PK�(�QV
��PK�F�VObjectReplacements/Object 4�]{pTW?��@�y�@������&Ri!,��@HIJ"1)i���X[�G�`g��N�tlq�:��Ug������
�g�/juJu�Q����������~���Y������������~���'��n�l���6 ��~D�p>1M<pR��V!�� �ie@lt�1�*p�
���j���`��2A�����+p�����Y��OE���F��lMP��"p<�B=��a�g�X�i�"��<
���h�2]�G�s����c���s����~nL��.\������������k���-;:[����e][�<r��nS�������(���[��\���mqs
'�'�$r%���a���Ne��[N��}
��%GSXg�f��I��r\Y�3��&��$���[�3��&��$���*��Jd��I��r<?Eg*�M'Iz����3��&���[���O')~��ga�r�<��%I9���A��{t �?N�v��[�8'��t6/
|3W�8�q�*'�����U��s;��F�����n1b}B�h�����mC:�M�~a�PqD>���Q����Wg�o��*��g��� *�]�����xa��X��8"�Adv���v
����h�e�H�PqD>���Q����u�5��G�3���a���A����x����V��"�sD%�kP�V���mW	G�3���a���A������m�h�PqD>���Q����N���]��G�3���a���A�:���|�PqD>���Q����
:�?n4��2���|��9���5��6����5��B��"�sD%�kP�Z�Ex������#�Df�0�Jn��wV�,~1�R�PqD>���Q�����:�S��*��g��� *�]���Bg��n�}�&��|��9���5��r��/�~�R�8"�Adv���v
:���x4l��;��#�Df�0�Jn�����ba���UB��"�sD%�k��Ku���^-T�� 2;�ATr��\��8�������|��9���5h��E�B��fG�3���a���A������\�U�d�� 2;�ATr�}�Zg1Rcp�<���|��9���5hA��b�{�G�3���a���A?)�Y|��`?L�2��g��� *�]����,*3�>-e�� 2;�ATr�U�,J�
��H�8"�Adv���v
�A���[�G@=G�3���a���A�Su�y7��M��"�sD%�kP�d�E~����G�3���a������;q���O4�F�.�A�3���a���AX�f��ey�X�� 2;�ATr�aq������bD>���Q����1g��*��g��� *�]��8���_���2�|��9���5�c��3x�P� �Df�0�Jn� ,�yr���u�"�Adv���v
��������e�"�sD%�k������b],��g��� *�]��8���X-�)6�3<��9���5�cj7�^��e�"�sD%�k�����GKu�"�Adv���v
���c�
^*��2�|��9���5�c��R��e�"�sD%�k��t����X�� 2;�ATr��Z���H���3u�"�Adv���v
���9��>K� �Df�0�Jn� ,�y�.��+u�"�Adv���v
���C��R��e�"�sD%�k���7�W��e�"�sD%�k�|������X�� 2;�ATr�aq������2�|��9���5�cJ��l],��g��� *�]��8��<�����e�"�sD%�k���mp�<],��g��� *�]��8�������X�� 2;�ATr�aq�������2�|��9���5�c�f��P� �Df�0�Jn� ,��\b���t�"�Adv���v
�������b],��g��� *�]��8f[����t�"�Adv���v
����)�_��e�"�sD%O�Ar�*������������������%O���J�:�m��@����
�o��_u�g��uC��L��xT��8=�Q���'�����C%�������gX��[�V��W~ySN4��M�gK���g��-�U�\�'Sn��)#S��	e�+42�bd
�2�L�(22���T���2��bdj�eB�*�L����L(�+�F���}�P��#�����2�L����bdj�e�2�|p��I�^'je�j[
5��T4�"������;��sU2��q���eN%��J&xO?N�<t*rU2���q����J��*���`�*Y��T��U��T��+�J�]�Lp}1N���J����J&��J��8�\��d���q��#k�J6�*��Bf�*��6�d�u���8$%���T�m���8$%�w8�t[��5I���N%��8A�CR2��T�m���8$%���T�m���8$%������'��qHJ6�:�t[��5I�O�t*���	�k���w*���	�k����%C�k����!)���SI�5N�_�������nk����!�2{�$!���/�k����N���2�?�}=[��a���]�X3�rK2t�)�#�]c]��K���8b}�S#�[�Z�P�C���[��w���8b��b�F���Z��8b7�b�F�/gh��q��~bS�zG�n���a!Ff�a�X#D��e�#��<)����S�
��_�'�EjKm:�J�NM����su��O��Y"�8��g\BYp�Hc�V���\Mo�����|����U�S�8(F�.1����b�����B��������9�a�.=��O' r�����*������.[
��h��6f�+�VLL��S5�G2�����&xwe{vu{wE&zwM�~C��H�w�d�7��k�!8���'/���2o9���!�ak���A�a����n�(��y�/�DOW�-�3yao�4�Uhg��-h��8��]��(�k�6�x���0�������fw"�N���D���������5��+�[(��H�wW��zwE
���y��w�X���CNon���E�[�!u1�E?������!�A�K�
�:�������U)���&��cb���}
�7�-�N�0>_�v�5hAy�������a���y�]�-�,�uI�;�8Hw��-�M qf���&��t��"o.��H�wW�wW�wW�w����`�;.��Dg��%pQ�b���rD�0��R+��A�� ������	��y��'U�7E����.��g�yO��
�.�+�N�u�&zS�5�:��e�0O2$��m8�u��mR�E�m=��|*Kn�J�����*q�����B=�����?w�vR�o��PK����
��PK�F�VObjectReplacements/Object 6�\_L[U�����ED�f���c���t����hH����1�i����8c'>LHL6��/� >h�����b��1�������`4��Z���{���SN�o�z�s����{_���=�<8|$t�-��
������P�� �]h {
��-�9qT������u/y����B�k�`�<~,0������`�
h��2�#:Y�E����I��"|��0��5�H�z
��j�\��
}?<`Z��!��///S�N��;`h�+th0�Topx0������=}�g?��ak��Pv�d�C�v����`�o��f�_�E�-!H�2��\B�*;��6|�`_�����J)YmtXA���B/���6:� �M�L	���6:� �M��b)YmtXA����)YmtXA���K�#�d�!�tXAt��F��C����0m:X:�A���[�:tg���1{ ��o�,��F��M�]���_���L���Fr�G��[������=�e]h��M	\�h�/Pc=����L{�
�_X})��H�^����Q\�e��������'�2��T����N��5�}�X���RF�!�*��=\������Ke����'�2��T������R�>���/�>���u�
n�@��<��E�m���~a�	���C Up�u�'H�1���6��sNa�	���C Up{s�s
����sNa�	���C Up{s��������9��'�2��T��H�1kM;U�����H]�@���
$��T���2>�V�@��:R�W 1��p�i�^>�V�@��:R�W 1�\r���
>�V�@��:R��@4����B����@WO_h�P�@�$���p�C��m��X�8wE���x���q|�_��'.oT9���%C���7&p�Khw�����������G����e�%/�W��#�l��_�"��A�`"���]������Qx���5V7�r�S�r�]rW����ErW����c�]���M�*������pRU�������h��0�8���(G����oh�S�r:v��D�������l�������@��U&wm���RW�V�"����iO�	�
s��F1�)�
6R��bF�}g��O���9��P������������RW.cC�z1���[���b���R�sh?�X�+D"&����C�R��*��y[����	F��r��r��.P��6�Q�*��5����R-�y�|�9��&K��<�y7���Q�2���]�r���?����V��{��A�i�[��r�������o�t�������$��pZ��@���rW�R���]UR���`K���/8��j�����B[+l�9���u��>NSKM)�g�u�^�r�Z�hJ�@���Z�ij���-O��������8���4Yi�����GSG�&+M����8�:��I�
ip�� �y������q*�L�i�_�Sz�������1�������N�}�c0� �y��*�\�R�/|<Ja�_��'\JQ�.� ���8j?L���������"~��0AKK���_0���S��#o����qy~�_��2��/�=$�����I���Y'8,w���Vd�����|��uf0�N�g3h�cNN�[C�����F�)y���~����&���@�ZnD�M�s�M(�*v�VE8���7���~8N��T��Oa�+�~h�0f?����Su��4�'��=��� �'����� 3�G�,.��&v�?PK�����NPK�F�VObjectReplacements/Object 8�]kl��k�1��m���L!������b���
�
&<�l�@�(
��4@U�EAM?��j����I���R�U�H�5	US������R������;,3������;�f��sg�;�}w���}�M����WDP�% *�s	��b��9����-�	e@lt�Q�*p������Q#��V-le(����+p�����Y���������L1[��<}����G��3[,��4����;O��w2�F�L/�-��!�s6�t
�q���	?����2���Dl�l�^���a����]m�m_����C�^q���"%�d9e��h�7/�T]o��Sv��g!��+I��P�\��p*UE��J[�
��K��4�L%���$Im9~��Jd��I��r\Z�3��&��$���2Gg*�M'Ij���*��D69�$�-GV��T"�N�����@g*�M'Ij�Q��3��&��$������Jd��I�-��'�(>(�����P)x�K��r����"w�@�������)�	���:����
GT=#��Q�b$��{�~��U>�%����\nJ�#�$D��-�m��Y�=h�KS��#�Df�0�Jn����Y����W�
G�3���a���A���,�y��KyB��"�sD%�kP�.����
n��d�� 2;�ATr��y����^����#�Df�0�Jn��3�tO~��{���#�Df�0�Jn�����Y,�b�����|��9���5���t��1�Z�PqD>���Q���x�������d�� 2;�ATr������u������#�Df�0�Jn��_��Y�t���"���|��9���5�X����^���B��"�sD%�kP����nW���d�� 2;�ATr��d������@v2��g��� *�]������bpe�PqD>���Q��T�Lg��)�/@v2��g��� *�]�.6�,��l�7���#�Df�0�Jn��m�:�

����#�Df�0�Jn������|��t �Adv���v
��"��s�4x��qD>���Q���~��b�B���B��"�sD%�k��>���6��6HH��"�sD%�k��9:����	�8"�Adv���v
j��Y4�2�L�PqD>���Q���a����L�:��f��� *�]�NU�,�W,6G�3���a���A�R���2�o��#�Df�0�Jn��w�to��k]��"�sD%�k�#:��B�
�B��"�sD%�kP�T�EE���Av2��g��� *�]�����h��o�M��"�sD%�6�>r-�AXsv��Sy�X�� 2;�ATr�aqL�!�����2�|��9���5�c>�3x>_� �Df�0�Jn� ,�9����B],��g��� *�]��8&������X�� 2;�ATr�aq�;;
^+��2�|��9���5�c�n�{�t�"�Adv���v
�����W�bD>���Q����1on6�q�.�A�3���a���AXsx���b],��g��� *�]��8�t���Kt�"�Adv���v
���v�\��e�"�sD%�k�<����t],��g��� *�]��8F�������X�� 2;�ATr�aq��+��T� �Df�0�Jn��-:���?]��e�"�sD%�k����Y��X�� 2;�ATr�aq���7��bD>���Q����1�
^-��2�|��9���5�c�v`E�h����Df�0�Jn� ,�9��`A�.�A�3���a���AX�|���+u�"�Adv���v
�����7��],��g��� *�]��8fx��U�X�� 2;�ATr�aq�������2�|��9���5�c~^m0�.�A�3���a���AXsd���j],��g��� *�]��8���`w�.�A�3���a���AX�F���],��g��� *�]��8f�4�Wjt�"�Adv���v
����|�gku�"�Adv���v
����{��bD>���Q��k���`������{{gk{Gw����6#�\�����A�m����>���_�*���*'��
��j���T�x��G��������axP��<#���!r��T��/o�����>r��oiZ����l����
��+�dzm��)#S��	e*+42�cd
�2�L�F�%12-�eB��*52���T���2��425������2}q���1F�F_&���Z#SS�LM�L(��:#Ss�L��LR����yZ&�{P,�)�m)�@@�S�D��<����'8��*��{�*yu�S����	>����-!��aW%|�OS%��w*��U�gi��GMN%�]�Lp���Jv,s*���d���4U��{�J6�*��$M���8�lrU2�YJ�*�u�S�fW%��������!�9N�����,X�T�m���8$%�w:�t����9I�7��J��qB�������ns��?�!)y�~��ns��?�!)yi�SI�9N�����\��T�m���8$%Gv9�t����9I���:�t����9I����a�9N�����|�����'��qHJ���SI�9N����~����BT��_f/7�2;��z��$�d���B��T2I��.���I�f����D����"�M�]���F�%q�N��1��^����G��M��X��uF��8b'�9?
��[[�L���	}��R�'�qnmA����O��nmA������1b{�����-9b;���uk�~�6�wo�����bh��s��YB�5(������"n��A��>-�K����Sv�"�����Y"����D�q�������2.�$�c�dEE�����o���:�k@�^1(��n1����A1_�#�~!����{�����l���������g��Ga}�f����:��0�_�u��1�v�F�H�wS�w���l�����D��I�����n�����M��9{�����:T'��={�x��!���C�z� ������-�e�B���r��+����O����>�\��a=���U��=���{~�+����x�w�������I���@���1bl����H�w�����My�By7E���
�?��)R��4�������'9\<R�_�P�b��b��{���>��K��:�������U)��&��c`��A���������!{���3�Ag�kz�`���uX�B�	By���mLY���"Oq���[�6��%z�4�D��������"%�M���J������=�n���1y�%:��
�GL���R{H�)�������jf�F�>x��r���`[#��qoa��E���.��g�qO>��2��.�z�����pu��`~�dH0��p��I���8���z�K�P6��*y�K��j�q�����J���z�j�����q(�PK-�A\
��PK�F�VObjectReplacements/Object 7�]kl��k��1`�6cc�+`���SBl��8��iBMy�86���Tj�J��4J�(MU�����h�J���J�4Q_n�JU�R��J�'r��wwXf6g�{z{�~����w������}��#[��l�:�����-���N��.��t!�B(b��(�uY���n��'�he(���<+t��^�����_�^�s��2�)8��H'D�d������F�g�X�i�
�w��9�\4�D���	��!��l��3(�x�~����e,$��M��������s���m�������mSG��r���P�������(���G�ysZPu},nN���f���$�D�b����-�[Y0��[i���gr�q�Ig*�M7Iz���*��D69�$�-����D69�$�-���u���p�����:S�lr�I�[��\��D69�$�-GC��T"�nn9��=���(��I��R��#�$����{E�����:����W?FL���|k�`�l����g�<�PH��/�~u^�z]�	��7B����R����	��A^�A}C:�m���*��g��� *�]�2�Y���yB��"�sD%�k����,�v������#�Df�0�Jn���=:�����G�3���a���A}Zg�A������|��9���5��;u�?l�)�N��"�sD%�kP��:��}x��qD>���Q�����:��{
n�U��#�Df�0�Jn����u_�1�d'��|��9���5��Kg��>����#�Df�0�Jn��l�Y�e���EB��"�sD%�k�s:�/�k��X�8"�Adv���v
j���h�d�i�N��"�sD%�k���:�?D~�D�8"�Adv���v
z�������B��"�sD%�k�'��,V�m0�L�8"�Adv���v
��Fg��f�{ ;G�3���a���A�t_l4X	�8"�Adv���v
�wt���!!G�3���a���A�_����j�y���#�Df�0�Jn���+t��4x�d�� 2;�ATr��Z���Zf��B���|��9���5�W�uo��^)T�� 2;�ATr�}a��b���8�d�� 2;�ATr�-]������j���|��9���5���:�T|
���#�Df�0�Jn��c%:�GK
����#�Df�0�Jn��E�:��"��>!T�� 2;�ATr��$_g�����R��"�sD%�k��l���9'`A ��|��9���5�b��"/�`W�PqD>���Q��
j�\�c��0j�h�.�A�3���a���AX�����|],��g��� *�]��8�o��],��g��� *�]��8��������#�Df�0�Jn� ,��{�����X�� 2;�ATr�aq�{�
����2�|��9���5�c��2��<],��g��� *�]��8��!����X�� 2;�ATr�aq�w��P� �Df�0�Jn� ,�y��;�t�"�Adv���v
�����
v�bD>���Q����1��4�]��e�"�sD%�k���lp�D� �Df�0�Jn� ,�Y�n��R],��g��� *�]��8���?��qD>���Q��t�Eg��V���t�"�Adv���v
��������bD>���Q����1?��`�B� �Df�0�Jn� ,�9�d���X�� 2;�ATr�aq��z��t�"�Adv���v
����l^��e�"�sD%�k��i��;t�"�Adv���v
������.��2�|��9���5�c^_b�R�.�A�3���a���AXs���?�u�"�Adv���v
����J�kt�"�Adv���v
���+�
��D� �Df�0�Jn� ,��]fp�R],��g��� *�]��8ff���e�X�� 2;�ATr�aq����.��2�|��9���5�c��>�R� �Df�0�Jn� ,��6��h�.�A�3���a�<���+�����=��Z;:{��[���Pr{�oR2��l[G�uSH~����#���i7T��TOD�
�3������yZ�o=',�8S��z}�u�ytE��|��7�D�m��q��4m�o�r7I��[p��~2��129129�L(��#S8F�p ���B#S}�L��L(SA���!F��@&�i����#Sc ���B#SS�LM�L(Si��iM�Lk�P��#Ss�L��LR�%��j���u�V����P#!�NE�+�m��J~�[�:O%|`��J��v+�x*��3�U���V2��d���ST�5����T2���U����d���	.���G���l�T2���U�g���l�T2�%�UrI���O%\�LQ%?���d���	.d����v%�5��qHJ�������	�8$%Ov���Z�8����x�[I�5��qHJ�{�Jz�q�`�CR���Jz�q�`�CR�O;�Jz�q�`�CR�������	�8$%���V�k��k����V�k��k��-�%��k�p��!)���[I�5N8X�����a��^k�p��!�2{S���������_f'8�'�L����!��5��5xN�
�$��J�?>~'K�
�%����Z����re���6��F2|�&���Z�b7�;��w
�=�@�����N��S#��B��g;A�N����j��qzv8�������g�o��M�#���Y��Q!�����_���1�2���������J����[H&��-u'����2�q�jt���,�Y"�8��g\BYp�H������\Mo���w�~O@����S�cb��wK���X&pzP��|�����?�3�%�>�vB"�	Q
���_�����
�U����C��`���3���T���L��i�M����|�z��"3����?��)���4����&�s�l9��@��y���[��w������^9
s�������l�W�����L[&�fr`o�4{���g�_�~�:����_�}+�����z�30C|��"��N���#�����\��Y�M���������o���@��H��\��o�ls���d�������zR�qw��a�^?8��C0+��sH
�~8OVy��6vb����0���U���������
��Ag��=�}��n�:��A���r���%[RY���&Oq&����[��@���IM �y�M��\�M�"��b����R��2���`�;)�^���[��pE1<_���5�S3��%{��df�Ax��r�{�X^#�r��*��h$:�u�p}-��{��������z��0�6D�.��8�`���`N��,����IqE��,���,��*u�K��*�	8Z$/��uv;����������PK�b��&
�PK�F�VObjectReplacements/Object 9�[]lTE>w�����v�-����_E+�RZ�DB{Y��O�����h��T��@�PP����H|2�F�� ���1�$�%1����HL=gf�s����K�{�����{������������j���������jpVR������}CX��������6����-����&��Z:��5r��K�y<q{�HGy0�g�t���v`�fg��x�t+�wC�;�#U�+5'����aW���R�h��$:?{�,�Y�/�����s]G��M��{:��{Z�k�6���v{-ct��:�D���>����s��s��b�� �pC� �G�x���M�
C�o\-���ku
	Yct9A����*�H���	��t/�#!k�.'Hv��2�GB�]N�����t>���r�d7]�L�#!k�.'Hv��<�GB�]N���������1�� �M�����e�.'H��J�.�F[��N�.���d�����g�]��]"�k��.�N����0��H�'���{�>�&�����<������T���{����njgJ�B@���Z�|��� ��ZH��K��kN@mt��{[���x�?TK���T/�9��M��m�����z��o�H�^Xsj��P��^h�Q~�P��F���P���������eE���=�]Xsj��P����{X��0����P������Pd����n��]Xsj��P����E���jxF�����&����"���S��jyF�����&�����s��Pd�K���]Xsj��P����k��V%����P������Pd����=S�3����F7!�.��������.�9��M��mE�0&��Y<�kN@mt��O���8���;���=�-m;;����%�t���#x~7�2�����ww�Z�kI��L0Pal8���	r-8#������9x�F��������]dg�G"�o�
��7�NF���]�N���p�E�n���2N5�������e/Zt�kS$������i�Fr�K\8��9N�9
���6�RNym�S=Ni����sJ����6�<v%X��Q�����O����S���5�(�Q�at�Z�:�S��s5��Xa�a���Uq��$�F���M���5&��#���B���I�����a=38aMI�5�s',P�	kN",�$S��3N��$���s'���������8a+�[�I�t�%��v3���J�.��� ��M�R�h��X�%���k��-EQ����'�
"������v
{���B�OC|�R�hgN���0%l*AdZX�/��.����0��T/��6�(L	�J��b ��]4�Y8��-�#�E&�`/?]S;�@k��3>��-`"�y�yJW��UL�2�$�9�#+���vc��
���7��������$#���X�0��X���%,'��{/�V���]�jW��u��P�
���=��.p����`����s��E;�������H5[�h���2���S����3�S�����~��#��3�����y=}Xw�=3�U`8}X�@�������0v`i@�?�<�+*�Kg�Z\'?b�����!�hVOx��b��%o�B�1ez�x�����.����v�]%j�t�k�:B���]���.;�v��]a��\��PG�ve��t������;��m�a�����ng�<������m	1Xk����f�
�jk�f��I�~�M�
�b�ED<�C�c,��T�����
�a\��X��
[���/,������e,���������)�������g��X�D�� !���(Ey/ai���,�+��C��E8�cv�{�Y���cWn����1�=�k�G{��U��P�������U��R����V���v���P����5[���v�U���]���z�k���x�"�PK��6�lSPK�F�VObjectReplacements/Object 10�\klTE>�*}��}lK�����!YJ�H,��<
[m�MyH��h�����1�bbB"��D�1Cbb���K�!&��`b�G=gf�s��Sg�N�n����{��������s��-[�6w��@����o����=sN{�X���^����4/���+[D���E�e��Q+��9X{�)����%���Q�0�!�I�m[��8��;��I��"|{7d��02E�7���$�I{/��"-��vNH��W�\�:���t�wmHtm��`�7���&��c���aw��jF'��K��M]Z�����|F�p���'0��#H�cU��=~}�
���`����V�1E����'d��e�o����=!k�.;H~�u�����1�� �M��j�������7]oyO�����t�.�=!k�.;H~�u�����1�� �M��i�'d��e�o�N�S7�~�t�A������$�1H�$�t%��,��%�9%k�X
��A���;]:t�zt��n]��0����o�y�WK���V/���y�O1��cc����c�2A[(��o#B%������N���6}3)��J����n������&�w���a��Fia�kN@mt���[��ee��^D�za�	��nB@]pwxs!�����n�������&������c(2������]Xsj��P����LH�Eu<�kN@mt���[@��WG�}��gta�	��nB@]pw(2x`���M<�kN@mt���[@����W�#���5'�6�	u�s-����1�B���yF�����&�w��"����v����5'�6�	u��-�������4��.�9��M��nE�g��_4��.�9��M��nE�2&������P�����+ 5������xOWog{��������$��c ��������}�����]}�q)}K&(�fl�g���8��k�~���5e�&��<�d�h�:�[99}�W��<���30��I����P�M��87v|Z�	�&1�S^��d��M����<J�����F�i,s���8��tm���
�;p�k��qJ�����S:%�5�@���`5���?n-��5�i���#���F�����<5�j�����s&,��-K#,��35�������R ����N��4��H�S���JNXka���	+��������� ����"����	+*.�+��0��*��U�F?����W�\�{��/W������V�������Y�&���K�}��Vi�^��zU{�|U;�����M�"��}e{(� r����va��D��~U�{!,��v���(
S�fD��u�R�.�������^K���9a3�������/[�����F���H�������
���tL)�%Z��_�R�l���)]����e�����=�?[����r��7��>�����y�x��X�1�kX�x�X�Qk���ZI��v��"�k��U�v�(]�{��U�L��;�0��O�}��E+��>��-�T�5�V�L+�<����PG���ov��oW�~xgu��2��g7�-:�.�_-���"D���"F�n���1���}��Sg�&�'�a����k,I\h��|�ep:��r���	!��r=b���X�JW��e��]���r��B��K��e��J��.�J��V�j����U��P�����M��i;^d�����C��d�.�������-	��d=���~�X�`3;��Z����bA���~�&tF1�����?q���z��?c��y�	�����8���|��c89�q���!��8�c8R�8$_����p������_���H�F�(�
,�������||�x L�w���z����]X9�xy�n
������S�f�#T��z�+�v5���.�Q�jR�"j�Luj�U�f�]���9j��jW��5W��$(���PK7�R��UPK�F�VObjectReplacements/Object 11�\[l�E>{i�R�n�t{a"�n�h"��,P�Rh�MLlJK�������Z�1���(�����W�1!>C������C=gfO���:��t/������3�g��������
��� �pA#8��������
LL��g���8��="��l.y�[��A�\����Sx��9O��������Y������8�p�rg)���0�`��;�a�;#U�����._���q����m���%B��_�t��\�/ja���o}o���OD{�[WG�uo�gn����Z�I(gc�dc��m����Y����d��{�����*�w�L�?��SEZpS��������L��	Yct9A���w�L���	��t���3!k�.'H~�u�Z���1�� �M����	Yct9A���~9���r��7]���3!k�.'H~���9���r��7]����^�t9A���q����� ]N�l�5>>�&]������M�AA�T������K��lg�����	��/1��2��������l������.8�\�b��x\�
zr���y��v�P�Rg���S�^��Q^��m����9��M�����(5��f=�~���F7!�.xn�/����v�D?[sj��P<���XFyc�m�����P����������D@��~����*{�]����nB@]���+�7���U%+:[sj��P<��
���������9��M���r�����7��������&��m������4��������&�������$�+��
���eEgkN@mt�����\�?��m���l�	��nB@]���+�`��eEgkN@mt�����\�+���YVt���F7!�.xn����mw���������&��^ipM�E�{��7�������D�q��������9=,������!����%c|5��MxA�#��b|M��R�G�����r��s%+�0:�"�VLFN���b�G7[N�]���u����p���="lA
	����_$eo��J��H��R�
���M6�hO���-r���H���r�H���-r��)��gK$���0���W�j��z��9n-��U��S�:Y���d�j����:��M��6��j�y���	;���ms�E=�K��b���4�y��<
�Nsa�yZQ���:O��<����������i��*l�8O�+e��'���b�f�v�S_�����i$u��0���r���$y)�i�h�<--�y��y��[��&���["��#I�v�4S�s�z�2O;!>O��9s������p~F{������������>�~�~�=&���4�����/�D���H�����'�4��V�Y��~gN���0%l*Ad[���G9�=V+g���9�����	�R��M%����*�]4���O+�������_�������@k�����������(]�{U1���)sgF��Y��m*������������<��?����-�w���\)�N|�l+iy����T���v��]>���s�w�S�r�pW��]���v�=�k�HH�����0���x��rg>@=�v��1v�����pWu@e^F�v�[����~u�vl!�
lb�8�l��OlO��A�tF�5�:�
�F\�c��fM9�����X�x�O�����������t��]V��5K���]j�}��.+�vU�o�vYUjW��U�v��]u���<���������Mv����Bt�K�a���y�>�o�%��
��j<�E6��mb�0<+6�'���/B�(&�]���B$��\���K�Wl�����]�vW�R\/��=���W������o��c��0%_��3��0�|�Q?`�����<�	Q��^���{���.���
��a���0��Y���cq����1�=��G�^����P���������R��&��Y�jQ����P����5W���v�W��W�Z��JW�� ���?PKo�$�*WPK�F�VObject 1/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 1/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 1/content.xml�XK��6��WJr���>��\zJzhS����(�XJHjm��wH�4�����6���7��h���C�g/T*&��d9_$3�d"gM�����g��|���E�2��"�j�h��F��3�nT��OI'�T�T����Tg�hi����Z]=E�����M161������g�.�TM�Q!���%���@����9��������\>>>b���<������SN�2���%���j2�>��MR��'���ciMz�������W2p�%m��!���j2X(�XY.��t�u>�K99@/���g��Ce��8���,Yp,�t�����M^%�e�_��i*#xv��p1����U��B�Z���Xa�D�z�?b
O�-�0�
�5$�^����R��p+�N�!�_�b��|�t�/O��P.�am?�bkF�o�`��1B�Z!Z�6����j���<�o��_�����������6����p=p�;Ls���s���E}�T���@_�\yW�E�����U�&'0�1�S�4�wZ)�|��@n��ms�0��:B�����+6<dF<L���%�>��R�N�~ �P:,Cv,���
�~�9���,�����Er��O5kl�J��Y�4��e��<rf8BA���H�%�lN��#%0h����G�h'����`���I���������4F�T�-&�G��aK��/~�D^�������h��LV-��P����!�L{�R�����0TjF���=8J�g8�
�J#
������O�2��5�@�PL���i�o2��
��+,\@r?���s:�<>���.��x_0���4[.�#P@�����d������-�A9��B���o�<����m8=\|{���~W�X��.��byN�Q����$�r�c�VL���h���?�S���-��{��p���,���9S-'G����3O�(	,aU��3��f�Rx�f.=���p��h^?��`n����a	�4*�T�\)\YP�����dv����`��90���n]������f��0��b�3��|���<+���2l�}'�>����j�^�������������_�����L����&u�$%8��^J���;��o���p�_\�z�N���`o7.���ft�rs��Y�oon�:������j[>������V���s���3�E�q�T�d�#���F3k����N����!�Ixr�@,�mG��
E/���-�����L��B#]7
\{y�[��PE��
����N��nc�������]�����{�&�!+V[��UQ����T�M��y�1����������s�MO�{�#�Lz_���p��A{�RHS���+ 	wy.��.@Y�������|���������9��s{
��p>��;a
y�GF��n����w�	D�&�(�;��]��!����I(<�C��(g��$���R{����+�1�FU��C�����f��{��oPKU3�\�0PK�F�VObject 3/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 3/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 3/content.xml�Z�o���_a�pE� ���%�I�[����v���%Jf#�E����R���v���HV�!g�����>�t���+����h��E�cV���}��,z��O�u��]NiD���w��j]K�;Q�9�X�.HN������E;k��^k[�����������Z��"�l�\�~n�>��������yI$�d���/���~o�m�ahii�G�^����#�fT�,lb����$S�S�}�*y�&�����%=����n�(��x��%��C��TKJ��o,N<}� zM'��^}�%b2TZy�3�L��V��.v�����'��	4���j���T�������N9�^����US�E^�+'0a[�x����-��E\<��H;&J�����g����)����`�a��U/0��N����������j�����{+��M����=��F^Z�r���_.'r{a/��B������|jj(�A�F�����l�;L�:L0x��w?r(���T��Z��*^��^������?�-��I����U��3-q|6�`�c�i<`�2������r��_�a)��(�0�{������^S�,����$��a��i�zz�	���ztQS�
����h9RZ4O9+t�R���I�����3kN0�m|�TL#���(3Q0�,�N\��p�v��k�/����,;�!v;%���%�,��j����(xA����k�����S�X}���%�����b���p�JX����8�p����sZX%j���[UL���K����~\`�~\�		��F��O���4\�_�=�J��r�6��H������Vzi�j/2z�8���f������"�v15�,�iaD4� _�U��!��j���RP"
��������������w�j~��4�|u�1��b�FF649��#'��������P]��7J|'�(e<%�Nms�$�*����F5��\w�p����{���3]�#l���)�	�U;2��H��qY1�f��N
�(F:?��(����~���O�V�W�����=�U�L��(_�Em
��T�H����w3�n�\�6��R��;���_�$'���Iw����@��U���*a�UHI��.����h��jx>��U�V�����l|���Qp��}�\}�'���x�{5��%��`_(��
%��x8�fA�bT����nF�����7�+/\��f��w�F����{����D��uuw���^[�[��-�������[A���i���XR���(�A���j������U]�[O2������#jx�=RI������k:���f.=���_�la���
�ZPf�)84�*�=�~C�nV�n�
���=�������r�>^�d����?9�����p�j������%l��kU���%U����
��d:����L���6����70����.-81+����)��aob�&���@���	�q�\�]w�:V�v�5��`�L������0���H�LT��E2�����3=����~�!;��k���|�J+�`5Vo�2+�BU�m2���)�jZ8!{��y���qXuX���gU�Q��f��K��h�L�.=��V�wb�y�����^�&�����}�2[�t��n����o�����\;���0���[��e��	�=���G:q]��������3xGs����I=#�\���Z����
�,�����Aeh�����?��F�8a�U������G��i�����l4�}7���9�����������Oy�����+�����V�������������r�X�����&���MZo2�\��M�)�33hy 6������5��eF�����J��"q��m�@����%7*�����m!7\!������������%@���^�{� ���!rPh��d�Pr/��k�!^�.��$J���wj���t|/D��+�b0��:v����k�7��|�\�����w<x��+g�@p7���������� �C�o�":��<�
�pe�A��OC�������� �(��J~��44���>8���m�+�	�����^�W>�<���A������^�R��3��s�p���a��Xr7�Pm~����Zx` ���H5����[��U���~��>��w���PK_ry���/PK�F�VObject 2/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 2/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 2/content.xml�ZIs�8���p1�G�x��J���9u�a���� @�(I�����$@�
�dzz�������[���w����Iy�����H�����������n��=�c�]��cFr�B�+�=�\����(����]�3"w*����5��M�3����T�b�y���p�H���/������TMg�P������XG������=�N�iaT���3�5iZ��(���B�0��I/p�����S���m�����0�6�"g5�Y��yE�F"��)�P�OS5iZH���H,������S2��d �a��d�qg|�%C���������c�o
�r������y8JbZ�xz�Ms[M1��+'P1�����E��������Hl'��1��{U��sA�[��]GB7�i����4��1U�z�&e��V��%�mmL	����+�s�ID�C^�Z�mqV��/�#l���M��������
-&\I�q�����G���WE<H�������6�2��������d�!���yR��9�E��|N�������"�\�>�?~���-��t��r�9�
��P�p�����C����$��eC��Z�:+[������\�����&N	�E4�
�G�x�{�G��P�>5 �vj���'�Ar�B�)q���w��8�E��G�X���������x�h3�U
���F�������/�n���}��-2a8�B�3�.zIG�fI.R���!BQ"g���(�A@�sb��4�����3wme�	��
.�2Yg�����r)���,� �8��h�?�`
x[j�w5i�����$���og�po�C������b�,K�	��BYPj���������!f=�0rd��F���xU�h�cDPJ���($�A=��I�p��[%X�e%V�d������O��_�����j�4W�'���g��,� ��]�M�G�a�(��7��r�[:h����!b<�0��
�v��@�$,���p�/�G�^���Y�@����THu�T{���D�#��#)�������#j��#��"��!>S����[�lM>Z�0�Wu�N4�S����&���V]����L*x���~��v%Gpe�\	��-��c��_�����5��T�i�����14=r����0�x#�D���UM�������}��4��I�e=����58V�w���K����6���f/G�:�Q�����K�����[�r��a��M�e��:C����3�:l~^d�+��e�j��W5���G{c����0Ws�T��r�����XI	MR8���\z�|M�K���U���'�����rsw��^o��~QQ[�HB�h��iDpWZI���fY�P�q���7�T��\��|�������r�`�f�z	5YH�~P�e9q���Rk��nV-}w��Gu�b�n/,�kw}�Ym�8��[A��V�����c�����X�{t��4�u]$v�����s�r�A!(�2t�GrY-s��y�L
��3�*]�w�Q
��e0�}�W ��s��z���T�1����3����W��{-�K@1�BW�-�~���3/�Q$�NX�C�@�������N_���
X��]����;�n� �h�v���f���H$�3}��j�&wsv�N�s���n���Xp���7+�	U��0����b�
$�I9s����h%���B)��J��^����Cz�:?]���?�]����r����3�W���h�N+�{xy����g��?PK��N�� PK�F�VObject 5/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 5/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 5/content.xml�\K�����W�����@	OPff�v*�u��*�G�$����<���A����%{�t%�)���h���I�7?=m��CRVi��N��L'I�,�4_�N���_=���w7�j�.�E\,��$��e���{��j����}�/��J�Em�jQ/�.��QWz��:�)�����w���h%fE��3B�����W�����,X���.���)}���S����q����3o�N4^�r�}�5R�r�d�UV����;�mRG�����K����bawt�<������rW�������(���2z�T��gr��%�/�cD���b����~�����j�����Vj����~{��o?����py</�eZ'�#�<+���e/��<�aM���UQn�S�*����������b���{��>��}$A�:,6y�%ej�(k�-�������dne�=n�m�z��h'�_��CbtfpW�J���~�'�)�`[)d���-���t�����=�`������[n/u
+��vY�����F[���.�:�x5��`Aq�}�U����<������_��������$��(�~^�W�Q~<��e�t-��p�@����>"����F��������(�}�����@�S��]WD���I�A�����6��Z{w��,k���_�t 49\m�������:�!����f~b���v��
�:=�XE�4{���N�!�2�m`�;p������������L�y��u4�S���k�\e�	b�vE��
�u�o�^����*��7���
8U~���>Nl���uU,:Q��n/�`�<������XA�t{}W�Qv�]����(��=)G�b=��1n�u�k����%O���f�+����~i���ql�8N�`�d��*�*0��&v��,�v]&Q$�]�������W�]��H����f�<��NH��l�V�,z��>�KN���mT~�����B8�
b��P��_e�:�Je�M��Ll�I +���%��7�+��Ip�}����e]>�F�5d�9�UZV�P��Q�e�����vZ�`�{	�{1(��8k�b_.��������w@0�?�P�b>xLc{B��z���P���~_����?v�f��0OY�M��>�|�����Xjj
��(�z)l���V���XG�.v�$�EF{=U���{���cE��U�{�:k}u#q]�+�}���[���6�mIP�!��+q���9�Yc�))���K"���&���v�mL�"K�S�����j!�i�/���c��Wh��"~�/�w7�#4?��>�<3n�.�7I���!M�Lj�6/�2Y�Ng���~�%���f���eU�a��������j�wtK��u����I�p�.	�����[#p�g�b�uC�ap��������qZ?�t��.+�T���� t��p��M�Dq\&v���`���ow����XM������v����'������1��B�����xm�4�DU[5������������
�~�1S3���!p(�n�EQ�i�IP&k`���f\R�A���Nx��eE{�ma�������z�v�r�+S(�������)���[g�I��L��6$����������^�$M]��;<�sAD��y5I��)n�������H\�
�7r!~�����6?R~�����?��'<M2�	�%���6���DI��>)�'5,g�2K�dY�qu��u��''��l��I��Q%������n���I|��_g����>�i������{�^��kMV��K���.��!O3�������m��p����Wfzod?FYv�jzL���i)w�����mrY�����Y����}����M�I�'���V�u#N�����t9G��}v����xpg���Ka/�n������u���-V����pU58�V<���C��v��u�E�O!/$O+x1x��l��5������Z�y�>P�vzn���7"�YC����*��G�YE���>�\J��E$�
��AsI��$��y�k�~���	���/���AOn��:$<��P��M�����������Uh��LiD�������/����v���)�--aW�pD3NA�@�V@����0T4��CgW����(����B�
����!o+`D�����
04�!Z�����kE

�C�V���Rrn�P����h�p�H?4����J����f�`oF('�C��!<.*�f�Eg�dFrd����PH�)�X��hFp\��/��S���*lY!��`l�!J(�d"3`h438���V����PSbBC2B��D���aJh��Cd4#����7�3���pBB$ ��~2i$Q�RM�~��fJ�Eo4���&�J+|<#�_JT�!5
n�������yo�
(U��-Gf��?3@9,�-�k`�n�����	!�6-�2����p��I�E�3�[;z*�la����#����9W:������<u���P
N�fM�BUF��'�l-H(P������V���gF5W�1�B#Q��of`P��_B#3 d<3���\dLq��*eB��#�R"t��0�N4��2��<�-3C��P��p.6B��A
c�R��4n12����A��+�
�28)!��~B��:�u�Ff��xfp�Go�"��d�>-P�c�-��P������������f�S�i�P������UC��8\d<38)�yK
�H8���L�.
�W"��e�v2|Dcd<3������9�@h���F�x3���E`�P�"B�3�S���T������K�*%�x3���5��B����g'D��.(&�2'!.���d�r�Yc���t�3�������B)*���x<� ���c�2jp.9�xfpHa�h���1���Z���o�����X*���o0o4�2��VjBDB���H���p0��xFp�g���pFs�
���e#��:4&p�Ke��g'A0o)AHJ%�m45���w2��N ��h�������Y@W��H�5z�4@��Dm�C���!>2��Eqo��0�(c�Q�)���SP�E��%%��ggQ��I�:G���~����h_)pJ���:�F���;�u�}���I�������C�������/PKdo��?oXPK�F�VObject 4/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 4/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 4/content.xml�\[s��~���hg;�)�/���$�>m��ng���EJbC�*I�����"u�����v�%����������b�%��������L'�f^��f�n������w��-�|����|��6M4/7
�=�����C�Mw���L����$���i�7�6���np��V���,�MWM�������G\V�#D������<W�c]D���&M~_��>��� ���!~��Hj���h�5�����h{��YVdNX=�1��}�Y���>�/�n���il;��M���;���c�msBrVe��j>���\I�/�V<=8OG}Y�M��������l���#������v��7��}V����I�)�����<Ty�U���d�yR��������oZ���>�N ��:x��i������1X�������n$A��/6{�fU���h���f�a�pr��\�a��f]�1�w-�W�v�x��<+z�zt����u�C����
��['������'��O?�n�>�4\�����|{��u�G[����:��t���[PZ�_0U:s3d_\\�^�/�����>_e���9�zgX^�$��������i�8>���@�%��<��ysl�O�9,r!~6�x_����t��%|z�g��)�t�y������h����N����n�W2�:M�O�|��i	��|�7?�tvw;;2��m7�s����:/�\��I?dY%��x�UM����0UU~�	6�&k�"	������',�;As�����e��v����5-��x�]Q���]j��abgx�Ey�w������u�G��PB���H��~���<)�u�3,��m3�r���'�a���}i�;�������Np������o��.��U���&�gE��H����1�����*K�([o��~�]�E?C��dG�����m�h�!�<��4��E��}VxK����uR}����e.�q	���g��]��SQ.�TV�|�!U��OQ����{�M\!7��{������M�M�Q ��yU7�P��(��d�l��w�:���H�{����s�+w���_����k����h����C�����!O�	MbB�|=�P�����������������~��l�.ru���S������&�_�"���ty���v�}�e��}�J�KD��C��t�-V��}E��U��`uR��B�2'��������_Kl�Z���
��\��Fu�N*��TI����,��_%s�,����.�<=V�.�?-�+��Y<
�����{��L���V��mg�g��{��"�V�>����
ijb!�km_&���l�n��s���\�������"���>o���o����;���2����B5<uK+�$�\�k.M�4k�[Z����m���}V��������E	�$.G;B��]���6jK�(I�*s��}+���n���O&�b�PV���������<�����O�^����Cr��k���*�������j���ZhD
P����1��}�aJh,�,�4�$MU�X�%00>� �I`D<�!c�]��r��
�0T����w�o�R��������,�./����a3M�8��S��ET����slz	��y��n�����7�$�tFq;�w��!��@����S��	�KL�}�*������8�O��Q�I&���Lb�E�c�����e�����L Py���MZ�fxY�������2�6��:���il?
TK����Q�.S��C��|Z��|Q�� }���g��J�j��<�f`�)�i����=�kJ�����������xa�F�CR����P�( ���gIp�6�������(�yRD�;�Q�h�%iVE��9��F'��G��t/]��Y���:�^zU>x�xb�H��l��Z�wS���g�X�fy����n�������!��/Y��3��l=�y����g�gHd��.!m���f��.h��D��s;�E����d�u	9p8�O

F����\�g�<���u�s���� 8%�u�C����bz �[8�<�zK=�IH����C�x��6\�����0eg�2!1c�m(�<ax����7�-��7����`�M(��#=xH �B��)�O��v�!'��T�$	��Xr���K-����23j���3-6�1r5%���D %P��&����}$���a���b��WS�����z�9�FYN%��<$�����+��Xr5% NB��Y�4�1
&���CB����h/�T������)�k�@$)�DSI��Y����/be �NW�#WS:�NJ�'A�X08�l@>J>�?�je���p�!WS
��c��IP"�R[�9_���!�����(����C��JP�F��M%�]	��1J��� �Z��6�`���)aT����PZ!c���#���X)������#�S:*i���b��c)W
G�1J����Rb�1�T���)_"J9Q1�l2Air$���m��Tp|�#�S2L������BM`��1H>��VY(��X��\O
�B+P������R����0FB��1��
a��;��\O
(N�@�3��
���(M�4xH8��0n Sz,#�SZ
T;����~�r�>�7xH ���\p0z�%�<�zj����Q�X�aL�0D|DzH(�:f�i�%3L),�\O
(������iP&f\R�c(9���
���Tq���1r50T;�@�3�"v�
,��T��xH ���\	��%p
c�r=5��P6��d�Z%�GF�w�S9w/��#o����_4�

��vL !��,�CB�W�r�s��r=5�^���R���Rj���]��@��N�LIf�"T!�����_��A@e`(�r!�}$�|S���T���G����)P�.��5��H9~��#��sK����\������oU��T�5U�3�gL�	%_CZ��a�i#-�?F��t`�@G�p(T	�\���BI��M3���z$~�\O	�W/�h�<��@3�)�f��@����R�	�T������
T?��W�� \��K>J
"��Xri�Z���>r55pTC�@54��1�R!��`5xH �*u
��"D��7����0z���5`p�^���S�1�@���8������_��A�M\������`���P��sH
0}��?*�o��Mw�����u�� �=v_��������PK�����QXPK�F�VObject 6/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 6/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 6/content.xml�ZK�����W^l�����t�bf��e'�d��%3-�E����)RSj�-���
d��=bU���z�h���"_�^SV>.�i/��YB��q��o?����?<�4�1Y',��F�J��.�uC}\�x�f�������"^�����Z�^+]�
c�������@�a2�Y�m{V��q�l��C�)c�
��J9-�{������J%���R��5�{�j�s�����TV[�DV�[���'yu�jq�'���uiAb����ey%�D��8�=��j���L�����tq7@��M�%��x��d��g��%��K��bC�d����
���Z/{N�{|�=�y�3���lr�jJ/�s�*�!�^�����������Y��R�+$�^���PN%	�Jl=�a��x�
d[���q+��r����5g���m����)%yW6��g�a�(j�B���Zk��v����W ���?��./����m�i5�������a�� `�${?2(a��TE���������N.r���O�����'f�63�W\����'���9������B����"���$����4C�xk����u��\��jC��|�&�R���jK��C������.��%�����1-����*N�%4��ZZO��='(B��*!1-pnT9�	�q����3Ll�ymZ�E�v!���rI���dW[���%\PR/d���8{�
JV�V�pM�s]�z��5���Q��
x�wJ�������8�s�wI$_��e����l��",_�/
co������K)��<jt�~�������!�=('n�.{�.�:�%a9^��pQ���
f��}W�h���[�$�4b����)�k����,}Yp��A�J�w51~��:�#��G���o���������*�h�xC���4~6
��kx�/�P����X�bb;��)g�qj[�xD�D�H}��������.��x��zs�~Wq=c#��c��#s��L�1�5��n@>{6��t2~l��Q���z���_#B�%�������zcO9F�����X������|M*w[�����v����Pc�	$/+���_�5�9��5���&�_��\^h-#�w���E��kt���jx>���>x��'�y1��5����������L<�w='�S�jR]K����e��>6p{��D��=���Q�r�����o�!��W^��
�Z��-a��c��?=4���Uu���~W�[B�-���\T_������K�����"r��Q��8�u����`����*���%9���d�?h���d 3BQg"@���2���%�
��#[p��zG�����1��YF��5������}'	'��?b0�/�]Q-��}^�t�g��?)���u�F�}hx�O�z���3��UU������7Ll{`l�E���m�h�=w�E&��q|C��<X���1��bp��=�&Bh�����
��"4V����������������y� _q
���8����.3�����D��z_��}��<���B���}�C
T��7R��}x���{���qY���wN�|j��	��(��|<����2�49���^
�L�	?�D�m�n��w�6~!��ga�9^���w��U����-�CLU����_*��<.
��	0G����,���
�/�z��`��<?���^���P�s!m�����z�zo�I#�����on.cKpB�vEYi��T4O��bgO��z�h��R����f�v��#���B�t����
m���6"��=����SK/p
�������������28R�Ow,{��:-V��F�)������w;'��T'�Wp.U�&������<���B��m��{��2#��`�GBIQ?Fah{6r��9'���������]yA�� X9�j��r�r�y4��\n\�[��F��!r�U�����]=C������<��v8(��)W��;
a���c������g��d��r7�nd���@��0�P>B�{�x~�Q����:C���;v��Q�����c�o��vt�W3���E!�}������r/���������=]�����g'L����5���U�z���c����"�[9+�hE��!��`���}��M�l?r#�S���c����Ph�pBd�P��f�a�R���w��x�u�����{����PKI��f��/PK�F�Vmanifest.rdf���n�0��<�e��@/r(��j��5�X/������VQ�������F3�����a�����T4c)%�Hh��+:�.���:���+��j���*�wn*9_��-7l���(x��<O�"��8qH���	�Bi��|9��	fWQt���y� =��:���
a�R��� ��@�	L��t��NK�3��Q9�����`����<`�+�������^����\��|�hz�czu����#�`�2�O��;y���.�����vDl@��g�����UG�PK��h��PK�F�VConfigurations2/toolbar/PK�F�VConfigurations2/popupmenu/PK�F�VConfigurations2/progressbar/PK�F�VConfigurations2/images/Bitmaps/PK�F�VConfigurations2/statusbar/PK�F�VConfigurations2/floater/PK�F�VConfigurations2/menubar/PK�F�VConfigurations2/toolpanel/PK�F�VConfigurations2/accelerator/PK�F�VObject 8/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 8/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 8/content.xml�\K�����W�����@	���'e;�\�9$���R��S�BR��_�)Rh
G#y��d��l�h������7|�f�����"�0�32�$����|�a�����osS�V�2Y��r�M�:Xy
N`t^-Z�����ET��"��I����b�������hl������w���h5fE��3B�y{�i��K-=VY�*`��]T�w2������������7&�1f�H;�x����e�h��y�%�X5�3:�t�I]�>��.�����al���u�X_:���c�]}�1/�]Q�=e�p�%����K_>��������_�~�����j�����,5���|��K�����3�!\���C��I��/��/�l�+�.cX��iU��j(����b����7�F�g�xt�r�g�U��$�^��&���L�(��a4�����*(�[�~��z���1��S��oX�!1:3��]�I��M���i�"�VA
�,vg�;�6�7/�E��������1���K]����]�����k���a�����"^���dAq�}�U���������^�/j���_n�mtTN_W��Uu���u�����N��}�<|�GDU�zh���[Y`S�e��|�Q�N�:E��v@��p�t4i���i�!��w'm��}���LO�&��m�7<�a\������oo�s����>7������*�����ew�
Y��n+��C$e�&���5LU_a������c��9v�\s�kO�\�+��nP��}c��%��Vi�=�]V���]l��8��w�U��Tid?�^Z�~y��	%�����b/����L��x�J��Q��{P���z��c��I_l��,y|qx/o&��?�M���2��I�I�8��e�e���(����)vE-X���L�:H�����q_%�O����Q8����&y4
��<��8�vY�d�]��,9]~
�Q����%�pn��3�b_?S��u��f�.O$eb�OY�L�-�yS�Bm�w���~{\��l4ZCF��3X�eU��;
����uS��V)�z#��^
�����W���	�cZ�Q�l�����g�j�C�ilOh2#T/�SW�E�������4���]�[�>�SuS��@� ���?�����>��^
[����eXt
�h��.���h/�*|:���~���|�*�k�:K�����.���\�"����������!�b�u�s�p���QI�/�$
9�H����n����x�3]5��Z�+��E8blv�������������M��w���������&I�8����!�w��	�M��>Lg���~�%���4ug�2������]Tv�]O��;��d�:���{��j�j�^�����[#`�gR�����08y�Ey��g���,��`�P*@�2��v����A�rQ����o#X�����$���I��<��m�����?M�1����}dtq��gvo��	�`Um����z�cCg*T4��S��c�f��0p����(�4��$(�5��Y`3.(� �,�/3��j�-��Z:����n�-��2���)xf���(��.l��&�~3uZ��<@�g"�f!m�ghz�4u���������o�j���S��*7s�`%��.J���B��(�Sm~��<�O��?
�#�A&�y	%3�f��I������iR�r&�(��J�EW�^�i<8�>�FM��UROH<����ni�T�	�t���9�}����6�W%�O����;����+�����]C�f�-�`M�����:�?��������~��ljzL�����;sHB�6���>��vDV,�,h�y"�`�DqR���e�Mk���7���/]���vG�Nv�^'�sb��Ka��ne'w�wS��g�Xi�>��mT�p��n[����Y���k�����<�6�l��1�T�k([!��I����}������;��n��zkU@
������}}�����H
����$�����9�s���_�!�G@������{:��&�LQ��P�0�B8��M?��'?�p����I�������o����v���)�=�0�*�9cTSC]"���}mBI�&ZC�vp":��4����7$��R��
)G0�����
N)2Jn�
�h4"�dz�A���jC �`"���
8���PH.B��
�h4"��(�tFHg��.w<���H�4���R�
KF#A�$��`+ft�g�$�G�dBAI@
��F���}�-4pM���.�Z!��
Fj��f!�YOD���djO������s*	�$�����HBm�	��$���dO)��%#��BBq, ������Dj����T2	�8�"�`�v��U�L
D�xL�qT�\Sr��x4��3<�O6�J�#@\S�h@)1T����8.�d<\P��@�S
�!��T��$��9��#+hM(�I���i���.�B�)���4 �7T�����]G4 �x4��x������`8�K���RkM�"!5(�d<���[�� �p)�`����%�h��
I5�'�SD��G��BSo=4��>2��UFU;��;�'�$B�M�`<���[��aLQbBJL��K��JEg��NC��hp�����a�O�PJ�o4(����`@��hp�+o=4�\*�:�J`��
FB��Xp�����y�������5'�'�3�x�O%������A��d<�d�|�S���&���X��g��@���<p%��������<�	b�)F��X���0$�(�W8#���Hp�y�����@K�S�T�o$(
���4j��d<����2!p����1
������&�G$J��L�d<�d����Z3BQa`����o��R\I����g�X2
�o�r@���4O*����,�F���v����G��A3otH��Z2� �NX��@Pr�6�r��I�����_y��B�!��
t��$��LH#�}��qc�x48����)lBa�Z�����-l!L��G���N���u�����3-�I��%��!��mS��q�$���t1�<c�>���$va�o4p�#���/��HD��hp�g��{!�n���:,��9�����'	��\I���
���~���3����\�?.�]��G���PKa���RiXPK�F�VObject 7/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 7/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 7/content.xml�\Ks����W�����@o����I%���MUn)Z�$f(Q!)?���A���4c�[��k<������MYs�����<dU����)Ax:���2�������SO���M�\��l����&�6���6��fo�y'������L���o�MV�����e�~������#ey;]7�n>��	�*���b�g��^{Y^j��.�e	����&�/�OE��2�}||D��5I���V����Ao���V+]��"3��Ad��n�&�tF��R�<��*������t����V��bVe��j<���RKF��6�V,�|:K�V��z���:�.��UvpN�/g�U�go�������I�� ���l�<Vy�U�����")����0�=�!��e��O����N� x>���U�-��Z
�hY��d��f��]V�F�����������]<3:����x=ci�Z�_��Cb�V�w�����a�'�)�hSG9d�������r��Y�r�x�����?�n���5����*�]p��stX�������L����
���W%3�B�`���{������?�m��6�Q9[�W7��x���4=�Z0�����<=���wCD�5kN����3#�L��9�-_o����VQ��w}���\'_DmZ��n��v�k���KY����_��S�^m�m��
���*o ����fvb���n��k2=,�M^<�!s�~��Jvk��"��<�'&�a���l�m���,�3G��k_���� ��:oZT�j��b���2/�p�%�*���|6^`��,��*I�W�Nq��~B0�~j&,�����~���8����=�w��Q;�XO��Kn����&�����^�>�����}�vQ��,Z�i�m�EV��������)�EX�pSeIe�]�������g�]����>�a�M����g����H��"��
o���K�I�/5|�WYAoMA� �B��7/��r�@����ORe&�d��l��y���+��Yt�}���������
2
���2���7jN%U�lWm�~;�s��f�������a��W���)���yq~KD��A5�!���<574F��������������]�[�>�S�M[�E�@�����?�����!)"�^JS�<��aX�
�d��.����^OU���k�����
������,����uAp���5;���>�*l�lI�v��X,�Y�:�g��6*	�e#�%�1��D�+���1��"OOu����Uy����cC�����@� �/���Ek���s��{���g1-��_g�j
�4�����o&��U���"��n�w�i>M�]�(��>�s~�T�����yG��"[e�tb��R5���^�0b��{,c$m_��&gO�d�=�Y�1N���N�+J(�p9��v����Q�rEI�V�9�o�����������\N���?7�f���'Lk����B��P�������6L�uRwU#�/���
A�+�J)��1UH�`X�F�(�*��I�EU�{\�9�)�6������l������z�O�a��r(�������pY�]]�9�IB�a��1!y��!"�'���Z^�"m]��;<�� "��m=���S��*73��%��*+���B����J�#���>
��i|�i�1��� c$�rju����z�4��	$�"��E�M����<=�p�e��Mz�:��7������ni���7��:?s@���5�u��J>���1����YiWB/9���>�<M-�;t/������u`z7�?]�������i��1/H������K��e��C^�f�")��=OG+ZgI�U�!{��n��n���~?��.��Vw�7;X��GO#�Yp�>��X��y#�{S��w�X�vu����n�������1�����c�7���J�B����g���*]A�
i=���]���no�f��~'p�=4{�X�j�p �5�����`�^D���=4���)� 9%���Fb�_ &G@�����t����aA1a��3v<�/y�/M�C#�� L)*����} ��`���y�h�GX��^��B%�X��
�9�����O(�1�T�Xqj��/9k?$	Vx�@ 0����
�u�-\A( HK�	0�c�lp%�Q��{��@�1��b�n�����"A#���2�R��&���F���DA����j�%��P!
W�_gLq;]�h$�e($�����X*bG�'	�I���p�klgCO2	��-)�T1���%����IB9E"V�]	Fl��d4�y>'~=
�J��
��9���r�b�����'�+:CU'1AX����`j��IB�����1�Z4��#O2	[���`�A#E����7�+��k��)�I�$��`�N��%��1���\�,��P�(�k�������d<�M�P(B��T)l7��$�P��� ��zT;n�J����@]3%)��TBw�<:�%�h��b%��.c;�=�x4�-L����p��<�L�4��P��P,1�X�XrnG�+���
��'��rb�I�$�`�%VJ
!�P����hp��P4�Gf��������
B���&\a!$��yO2	�u�N�T!��X3���K_���h-0��g��J���i�B���K���`��I�$`,
V�k����d<��[�.H&PL$��\�T[��/	E�@��);7�'����!
1�JRF���n}I(b$4S����C���h�A	'���N�+O�
�`��U$�S�y��hp2e(b$�$<&Z@��ip%�hP��p&d,��<z��hp2u A�&�V�~��	B9�y/*!�'v��K�#��T(�W���~��KB���yw�aV�Pp�� lH� ��*���w�'	E��� L����+��5B����a�K�����EW*)�TiNcN���fy��h�A	���
5���w�w%�h���O��v:hO2
v���V���Bb��S{�P4���4Un '�=�x4�!��VJR-��s�xO*9���c2V�m���F�:h��V�����`~��.
<I���%�$�h�%}�x4X)"����� S���^��$��	����N��I������x��
4��h��I���I�E��P�j%�Q�n4��+i�Gjw������3���������W�|�PK�V��oYXPK�F�VObject 9/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 9/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 9/content.xml�[K�����W^l����g�^L�`s��!���%3-�I����)R/���R��i$��M��U���Eif��9�'��qB���c��	N}�4z�����������4��7��	N���T��	p�|��>M�,�P�	��(�|#�
�pZrmt���U�P�4=�m,K2�&e�5�m���%uH�J:��)(�dH�}�z�I�R�=�N��U"��zm���4�+���bE����-�t��6���O��*qq�{���un���/���yY&:<0��(��J��L����Av7h@���5��z��Xo�qg���%E�s��d�Yo��@7��t9u�����F�w��(�+���N�*�B���N bf��
��~m)��c�b�,�*QH�)���P�3��\B�b�4vh�!:uj�����l<�$�_1�jI�wh[Fm]����L�J��m(5n���f�[�.A�p�������_������!iI�3��N���a:�w�g:04���R(���Pu,�~�u��;�R���/��8A51y������� h
-�s-8m���~�2�sW�q��wK���[���V�+����Wk
�����QP��,��6/hE]�g'y��z����^M�QBR������Z�����n�oz+�?8�b"D	�/rJZR�De�8���L�'2�a+F_`���X��$t��u�S#]X~�H���r"����*�S!���	8�b
���Z~��e���tS�:H~J[r�J=���c�?N%C��Q��������9v&*Pjjr���.���{������.{��6�?�]�#�lH���qC��(����.��r��i�0N2q)w<rl��k�E^�E��*�OCK�Nc��]��q|�2�_�����R8�
M�"z7D1�t*���W+����*0��`��q��{������K�.`(���@�i��qq-TZd �P���i�	�u���"��������G�_��	����~m-�;�z�"��	�	m�������Tf�j/?���V?���>X�.�aT������;�]�/��J�j,_Ql@�Be��J����$�PV��3���~�j�N�k������j��Xu:`5�"1,	�K�����������+I����V�����`�t��s�cl�0�\w�!��s��(����o��{��@�yp
�1	�������W�^8���b������=
.�@	�m�@P��0V��Z��jy�$:@{�6W�BN��0�����4���dX�����&������=b����AQ�I�#�}��q0�5�A���lY�Pz��8j�P)��9Ci���V���I4��,��cA��J��������(�F������W��|(�p�����AO��d�'w3����T>OY�������Tj|Lw��c��zQ�K7;��u���2���UtP��"�
�#�g%b�\4D�������E,��Z�����.p��=v.-����~�b����������R���A���\�9�6��_�����m�9�������i���E`���U�o-MX�"�Q&s�a!�
�O+U6j������ih��mzsGy	E�.��d�}��xchp��<!����<�#�0�=b$h��*P��y�Y}f6�Y���������}�<�����#4��{��I���B_���UF�~^]�3����<����p�?���1�<���s������b�e��;�'(m��k-I��N�j;W��Pu������9������oP��d�yLR�\�'��a���>�+������-zs�B:�����qR*7[�kW3��V�E��|������QMz]<q��n���)kk��d���)-f�;�����I�0��QL��f�Dv[
�&A��h�
�
����\����n�0����>2&�
�;��+��K�����/�=�6X
������
����a5|\)����<.��E
���nT��q�:�0G��z�`��8�f�l�A�����t�;�l��f��i�|�pq�i�p,I���%���9q����7����������\�q$��b�IR�o�Bg��f��4Lv��$��
I0K��y�$t��oNUw���G���j��()G���f�PKC4$Z�3PK�F�VObject 10/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 10/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 10/content.xml�[���6���"�����
23������=����U�2(c[.I���Z�e��a�@v����j�����P��}�iW��8���8p��W)�H�z����;�����<�<')^d4���NJ+�G���=�8��jA'|Q���H���Y���JVC��q��^x�\�9\�V^������;��J����)([�H�e��+H����n��6R"��|��Y���-_�a���RX
�^���-�@��'ym���g���������%������&�5e�������$/�-,cQv��(�A��:�����k���J1�pF���������\bv��H�W��t����-#3�==���"m����t
�l�)+�P:�����=O'��SL��@�.����D9�TT�FY��1#r
j���C?�����I����(��C���^�mS�lms��6����P���!P���j{���[�L��/?wU�������%m�H}v�i������<�a�C�����2Z	���;�Y�[���(�����t�K�1���A=.P��i+�e���"N��������<C+~��'�Y��^�{+��x��k5���th#(�IU����.hM]���.Y�A�����Q3*I����uY�#{O���Oz���u0n9*I��$i�Y�b�^��5f�`>�i
[1�T��J�%�����85���D"����Pl��^��&��(^��+(�*�es��6�Q`����a
��[4c����(����rA��cl��*:2'��z�_�����\GU�����������.����v^mp�����G���d�Y�,������x�Q���]lOi�l�`	����7;�A����x��������c��Ic3����-qq�2I���g��R)\�����+&���
�B���K��0,����p��l�U�
�9v��sK���`{0���@����qq(TZ� �P�R����p�V���
��Nz�nXz�?�#���W�[��h�~2��gmr���L����IZ��)���R~�Jv_��	�}r�b����P�$��7�O�?�?+M��|A���}������B���`���x���N�j�;����k��[�:������eI����%�o]>�jl�����q�L��dP�
����}����FqM�%rG����|�L��so<8e\ChA��;}�~.R��+�Y8w��b������%���@	~z����Ual;$7�ML�\c�ZC{3w�`*��-�b�p�8v]3��[���ib�q���X"f�v��EF��p����u��Hk$� r�pfT(c75V�4���F�~`�+���M=Qz,������|(�������ai�4��CQ�T��#��|�)(��=i���a��l��pf��I�r�Y#��nPaI��C��f6@���fl��(:tt�F��m|P�2R!��WR�V�&JD4$"�^������r-���m�%n|�3k��@G�wv�~�2�T��:$e%jmD����O�8
-�w�(��v���VP?��e�4��������5��%�E�[Q&��vA]DB��t��v�~��a���0�n<	,�gPH��<�`�8�Up�Q�8�&�G�_>��H�k�O��b$�)u�<��;7����6p��`��~�~�������)%�ie�G�AP����=��z���;z��������O��������7������%���8������0�V��psE��m'�T7��9z�
im��7�=.g�Q���97e��s�(]�b�h���F��J[��f�tF�@9�b�W[��J��U�{�Rp!��Y
��;�T���������(�p0�<�����N3���l���������of�U%�\��W�=����pNr�%Y�A�@�������:�[����s�:%����
�������'�4I�����r�JpE�uw�cw>�F~�n(7,W�=���%~0�P	��������;���$C����M�I�;y�r/y�l>������{:5
-�����`O#+|����AEv�6�{�a�db��������A�;���&�8�OGC���Y0��=y�r/y�0����P��[��:�ap/��(��*e(��M����"�MZ��4��zH�}R{#o���5C�z���K���PK�,��4PK�F�VObject 11/meta.xml���n�0��}
���O�r��R/��[��q62N��H�hU=���f��l{nj����Z��w=���ZHU����q��mq�����������-s���e���QT�NvT�;j9�-�YB�4���7Z��`mKF��p��`�y!\�3}�������w�`b�4Ma�����\{4�D	X�����af�>��0�����i����,[�$��������=� �\l����>�(E6�P�aV��I�
>Ol��
����Z���=�wq�,�]k�ri��,���y�>e\l6��"&>"���8)�~�e�#
��+�PK�v�7iPK�F�VObject 11/styles.xml�U��� }�WD��C�n�%�������*f,���}�������s83����:�b70�9�sV���I
��9������^/_NP��2\�[&M��o������s���Jb �k,I�46C��B�1{���sv3��9����j����
����T�I����B���b%:	.�q�c�%����|t��bSE=8�hP�z%�FE��4*�Z��M&U�ac���.~G�u�L�J�1U�a�VE��J��U��C����yr}�FTr�<8frM�Q���o�L%�O�������G�
S�n�)4�F��<r��;r��9'X������Z�=�G�A!/��_�&l�zi�d7�]�MS����4�:amD2nfQ������V<{]�>��}�E'��6����n�����|�l��gb��P�Ca���9��6�����=�x�<73z�nh��_ ����v�jH�w;����EOQ�]�wg~n���?D�?PK�E4��PK�F�VObject 11/content.xml�[Y���~��0�� y�AI>�t�b$/;y��y�%�fZ�n���S$uPn�m{��F2m��*YU_,R3?��|�B���|#��H�������o�����z���2��E��MAJ�$���{�K�0���
/*%.�X�d�*R6�6�B��)�=��RV�S����|?��s���s%�D�d�-*,�2�	���|n�n�[wj�(�cO�6�i��U�k�4�HN�0�!y
oA$>W?�k�$�>?F�l��d'���x����'<0�8��-o���x!�la)����i��������>Yc~6T���3^��%�l�.7�������r4���d�l9��[��I��I������S�fS�x!��	D��!x:�cO3������elSHP�je��"��!��i��
�8���Z �S<��kY��+�mXsv��ua�V���(���i\�1��
�Y��f��X���2�������sWuyqnh(�^�&�Vg'�������3&8,��?P(e��PE�Z������]�G�����_�5)p�L�f���e���x���=�m�8/�lh3B�P���_�sT��z��X�L�Q
�������� th���$�LA�����L�Rz��0������O+����P?��{z��|z0������&d���^��%����4�  ����JkX��gX�d%�b-	�����'[��ArN��U�7Z�*RF��6�����C�O���{��-V�����0���?����8V2h�������#�;���J��t��:.�9������������No���/���L�MJ�5MSR:	�s���q�����&KN�tHQ�}��F�w�]�X
[Tu��:VJ�46���������*���)0���!�K����x��6�S�V:�uA��NT�!P8I���n\�7'��{n�f��|��T�9��r!�*��9.W�}
n]�L���A��X�)��
O��wT�Y��~k��O&=��u�;[���w}4O��=�d�|�>g%����}r�b]��������w�O�?�+M��|���S}�����a��&w����x���N�k�;����kU��X�t�����eI����%��.�o56oIz��(�O��dP�
����c�D��F��('��#����\�\&����7nN����t�L����y��{��UB_�w�*�X�t�>h�O&��.�m���g��Z�	]�����9�*�~�Xs�=�]�y����S��R��$9�6s�������.�Mr�"e:��=��F*B7
f��e�"���F��Lv.�����(4�@�3����d��7�R��gU�)'��?c��o��%zw��
��P�7z����"��l��h��o�V�����4�����u���l�|����q4rgax������`�����8������x�z"�!(�E��i��k�W}m{�(H��]c��@�)t�{g7���&3�L[mBRU��I��K�������������'��>TO�s�*M��,����>x���g	kQ�V����]�"��O�t��a��!���������0�:�IX�\lT#N�����-G�����dM�i�W�����EJ;��#����2q��>7D8��/����4��4����/�L0�h�o�%�bpNo��������n�>v���O�������O�wts�?_���[������N�o��
�n�(Y��0Wh��v2PN�v������P���y���r�����sS��?f�J�e3��>��b��tX��\�����`(b!R�
���f����yU�^�B�{?�!Dv��n�W���v��<}���F���(p#���&\�q3�*v��q�)��YwUI9�:�U|%�`*��C`A���?AP#�,w��o��U�-�b����aX7�&�M��
�yG�igxC�a%����
��1���m����H\��\_$n���
d�-��0��(�$5�{�C~8�g�<C��<�N"���pR�=�V��2:����g���K
P"[���G���fQd��P��[�VM��;�����b�lC��<4��P��{Ic4���8C�fNEv'��&���O����)w�Lf��'OS�%��0@�<C�����'�'���K�������i��K��PK8��4PK�F�Vmeta.xml��M��0����*pm�&�����$-�Q��C����t��{�1�.{ ��~l��\=<���	���Q��Vis������@���v���������E0���P���p+�v��38�%�0{
������X[���_8�K�FDv8aJH���N?������4E[��,K�FwT�w�~��������8���.���{?�iP���l�����"|�0�G�u���3���]
7�Z����|��_V�Q��~x�����1K�;�x�/��]����i+kK!U�3"R�1� I
IVt����UJ��14�P�,����2���1Y_�wl�J��dB5�U������3e����	���d���������s~.�����{��J;_#�6QB��ZNw���jv=�n*�b���;o�PKrN��%PK�F�V
styles.xml�]ms�6�~����u���IQ�m��3����L��4����L��`A����o��W�MQ�����C`��}v�X����C��%�F����=1p�R�D�����d^M^����}���G�4�7�����Q�T����EK��,#�d��%�q�5Z����+]B��d�y��,�@Q�Q���>��sF���==$��S��a�8�*�>$��w��l�63��t�XX�6#���.NY �<���%��lje�!���|��,��ocI\n����X�����wX`n1S�s<���I��/�;�����|�U �_��~�bzw�Xo�$qgt��J���:J�;�z��8�24�M�x�0�1+����.
��$���Q��o��3����N���4p*v���%�r�@����*�D>M#@	���?��Q��lY�PuD���bj[�&k�!C�f� ���%eiW��{�fD$�����7y�$8�FZ�Zc���ab�����R��!�����[�����Rj��A[�.#q�1��+��aTS(L|/"wF3�$����Yn�z���&���&�9}
���\lz�
��WjX���z=^O��+���_D
�����DN�kY���@�b����FL��N�0����|G���O��T��*a�FKdTX
Zs�#�KX�c�qX!�	w��H$b��jX��U���l�$��������:��Q��%�[As�n��hB}n���I�\5��d��F�.W)q���}�:Q�8k9��f�8&y�Z1��"��5d��
���M�$E"�����
�����P
�2|���Y��c.��R��"�&,�@�?>L�lM��(����F��hT�2�$����jtoY�?�o�)2�?��5�j�7�]�z�y���K�7����)���W?Wl��pF?	y
)�7���9:�"���7��Kg�����X�4VSUDM���$k���	��`n�������1�T���t3�?�
���M�x(�4��pL�a�HQ�Q�=N#������0���9�D6��a2����C���I�a15�%�d"��KW	����PR��I����&����@Qv���Y
B�t������Y��2�4:�l���6��l�� d�=b�!�:CZ=8O���*��0�$�
�}��3v|h�b[�DS��F	����J�P1��=��)$���W��#+�Qv��3��!�o�f:�,:�)���z�!��J��8�s�,���cV�`�F^kK��]M2n�F��86!NP���@�t�����4eI?aOQ��q���A�BB�q��G
�����)9l������������v�����b�����y�\��#X&��JXZ��+�'d���kHu�1m�ug��v ���N_�3�.��1���B�������{�4'�X�(�CuJ��������;X=�;�{z���4.���6jc�j��?j���KN��k+,B��r����O�w���?\�^��8�8�5��������A�)�4'�23U�#���S����\tG�=��;�6pN��g�������w6�����!���0������{b}�b��gi��5�f�x���#F��Z����4�����f�6���(��b�d�B���]>�n%��g��u�D^� ;M��Ph���=%{z>d5x:i���I����(���m��tGh9�/y
�����NC�S?�p����vg\���P��/0���>?���\����Q
.�����
�����]hIg��]�cn�v�#�K k���1+Q�*N'������$A~Y!~/8�k�I�&cb��#�#����q�w��~mG<������>�����
��}���}��c&����_N���(�gGC�e��/��k��(��iW��;}Rqv>f���g�;�l�4�x����Z��_Z2���p����'��=����!/ZN*�/�������b�l��(_��r���\P��������-p������������v"��b6b���~�@/��%��v��x�N�'����E���U��M�#��C]�����uw�s�q��������Z�������z�<;��}�K����COPg���g��Q�����metW5���S����������K���z���<�h�3���>#���&,,�q��j������������#	��G�BaL���6X���t��C�w��w�I{��������V;����A��P�a��C�'��D�Y�c�}�-��V4��y��U�j^��:�\)��%A��%�8#���o�l��W���O�1�C�'Jy4��zi��E8LU�C��P��P11�����.��x��IqF#=��dC<q��8��B�&�P?@^�&���(���b
��+�x�����!�r(��)g���H�������������%����n��{H@����
��rR���1���oV������ yZn��uA��Y2\���"�`��
����|������e��a����'���
�����a����"���{�h�h���<S-�cj�)�
������Xj��4��o��$�QM^�"�	3uAW�)��f
��D6YW���F����R\��#7~�����B�{���)��T��s�*;)Jq��:D��fV���j�+F�{���m��UW�|h�+����t�@�pWa	���DlE�����zY�L~W�*a��T����1����Z{(����&6Q��
j���j!���R�+�8su%��.���Rr7�X��2S�:���"lW��q����O)�����f����U��!��������-MO��N��z���!R���d���V��A�4)#�����v��R��h���*e�����P��,�^����k�$�iv_b�(.w|�e���/�Oz�'v��3f��/����_X��Z�	]�$g�_����W����0�+`n^�4q���cE}����WV�P��53���U���f��CI��A�`��U���qq3�D+�mI���������[Zk�:����U�r����^���B���PX7���q��2�z�~�J��"9&jR�C�R�����2a#'R�����L��t.'7�m������7�02�����Q����|����.�s=�AK�7W���������k�����,����G�P��x�8�?��a,Vz��?���n(�$*C��7�o���ba?4�=,?��7$�a���qX�j�����%��rj|7��}���,�&�+sea$��dR�X<�_!�HB�]�+�%�6����a;�9S�����PK�w���lPK�F�Vsettings.xml�\[��8~�_a�:�#�zFk�����mm����l��	�E��&�3���=�L�VqD��wN�������u����M�M����V6�ic����������}!��m��I��E8�
PR��B����^��F>��AC����C�������>����*�n��2��o�������c��7�8�?��D���tj����A�����X��:�w���iVo�p(���}��pe��euS�'3c7UZ���F��Z����2��l�A�GP%^�p1�y�����-���3�/��eX��m��S���u��n�!���I�[�6����W.��ll�-2�m���]J����wo��#��� �iToY@��Sz��
i�����,�q�C��K���������7g/���H`3���7"�M~��t
���
��� �s�P��B��eE|��������V���w�^�Z�<	C�^xA��R�����s�D��i�qy�Z������9����}L):!����~�r6l��D����=�������]��%t���R`�����l�D�����L�����H����I3'�wY��G�Wi
x:���"��R���_iv:��2��,�z���zK�-�����-�N���[��BI�%���[Ro���u9��x+3%3S$�;_��o?����b��qt�gNV5?5sNc�M�{�ex���Gl���xgf�'����y��OIXt1��P{�!��I��f�����m-�R�0��G.�I<D�D~FD���y���N������Qp�eye���	�I��K0
��R�VJ�f|�M�hc���$*�a��������)��/���[�C�z�SN�;����~������L�/~��������(;:��	�������Y���i��o_M"?Y���P�j���0��V�������a�CX|�t���������>4h3���Q�Z������#6�t�l{!�����+����:���V����X�
�u����`Y�W���I�M�o!����;���<?�D8/qF�~�����}�~1��������1�2r#�<���L���.~���QV�*�@�4�����t0���.8+8
��MK
m}
��A>NX~a#����C��)�w�>D�M~�y&>L�����\��#��ik#�)��d���)=����m�$ ����VBR���wY�A��O\W����Ku�������v��j\�{j�7F���=^2\'�GN���,<�?I����v������>��Q����l�P}@��v8q�N����.���$� pJ�W0=����3XX<zh������s��}���
����	xI�
������Mo@���VX4�czb���������o/���RZ5�_�Sc��c�Y:.��h�����@O���B��1��6{�{���+��?Y��U2$i��t:����Lh��(G��/�#��d	4@���u��o�;��O�u^dz�$]�l�u)f84_�'�%kW�X�8y�~���n�8T�)�@��c>z��\�E��g���+s�l(�Ij}�ikq<�B�n�������:������@o��zC�xTo�9�U�@}��U�^�x�h���$	����jlqmlN����/��i�0��(M���K�~z���#������?$����#��)�k@�xZ���`8IyV�I��O����;� 3�
�8L��+����&�7�_��2�'O�C0���C���m0L���?�|������9���RJ)��RJ)��RJ)��RJ)��RJ)��K�`�}��K�\^��\R���#W����A������f1����y���hM��+�����x����,?�^��6 ���|=W�6�j�N��j�u��r�s���Gpn���\C��M�~�~���;���R��4E��R�[F;�����������O���]Cl/��\�R��>K�nw�%I����������MLtf�Q��/�y�A��8_����"_�:_�WM��j�����V��&��PK�t�UOPK�F�VK���^�^Thumbnails/thumbnail.png�PNG


IHDR��PLTE

		
$
&((%-##((###++ +++22%55(;;,333??0<<<0M+6E2>M8AL<HX(CeBB2HH6KK8RR=BBBFGHKKKALZEQRUU@[[D__HSSS\\\BYw]igUapccKhhNkkPssVvvX{{\cccjjjcpm`sss}}}+R�5V�4e�=l�[r�Ws�Er�Mx�T}�eq�hs�lx�{~�Y��c��j��m��r��z����b��f��i��m��p��t��x��{��~������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#�e IDATx����wI�'������a�[��P���U�-)�6�8���|o|g����)`p6E
8��^���!�#e�h��{��g�gz
S7=S��:"3�)RdAd�+�P�������eF/I,��
H�tH���&����^w�dKq;c��!|�;#��p�xNg��"�k���Lr����ZpX��<�X�.����.����,fJ��BZ^��Tt�k����5�4�u�I�/$�!������C�v&��>H�:����bJ`0����=�@�H}��e�+���thi(~s���?����e4v.�d�^p�/�@������L.-��@�D
��+q59$��!��VC2R�V�&:Q�	�C+*!��C�|?�ak�2�R�4��kY���bpH��2X���B�PQ���>�F"�U����v>���
WQb��~�|�kk���L�� �Z�������3m���%~
���_q��	�w��mo�'�sg�NA���^���B5������d
��(y%e�9��x&x(%?ZhF5�}'G*��N������N�1�.a(�f�����ex��6F���?I�jUrYG�ru���d@P�4��Q|�b ��$"H�,4����x�U�P�N��4�
U�����xc��{�#�9e\�Z���j��+f�,M4,����2����BQ�
H��������'	I�(��(P�{$��iH�h��%eF��!�������E�!�S*�ir���{KEe���������1@�ZD4�q�����'�.���j����8P\��8$�A��5��B�������,���_@�����Vk���K
O��!��C�,LU���C�a�P�\�v$BJ��w��J���To�M�Y/����@���2�K�����?����9j/M*.�4I5gV�R:=�T\
n`��	Z�����"�n^�;q�_�f�Z�VrJe�R��e�`H��!|�!���j#o#0W	����h�89^*�ZF�R�_K�FhNb
���RJ��z�	)�$jB����X��($�&i��gGGRJ`\2ik*���8�jH<��;���7%%�j����x��E(��`*����zLtl�A'����U�r��u�\p�����W�������o{'X���������V��������,������/�����D�KGC��I����C���O�3=:o�;��������~��)t���|x��9	$7L��%��W�L�^�
S�*S�K�wJq%�$����?O���A���AR�WC�:�i�^�79�l������#{����c��0W�;�W./�M<���+h���jH���x��4�����?�Ns��W����UW��c�E&7/�H��z��kN�V������5q&
2�x��i���_<�_]s�v�^��U���@�c����'�!\���������t�z�@�
��hg2H������j�M�.^|(��p��@)xy��o<r�U������.��	���n��O�k@M���q�A�����D��n���D�wM�t�&UY�
�?��w�Ovb�AC�$%B	��mJ)�z
�|c��"�����V����k)�#�A�Y�
=�*9���T^QW��79i���m�Mrp��W2nI^��������cB*T�s�Z�h�	��g��)�_�!�KK���e]�9��E�_���;	)5����pxS��U<�$e�����
�;�j�y�������q
Rr��������K���5���������,�����X!uoD�^9�z#-O�~u=$�����&h'3,�C��7T$��\���$8���>^�\A�f��) 
:�
29p�:�~���2q&8���y�l��p�]���8��-A�����a�X+�7��B6�
N&���Xh���^j`l�E�*�)�\�+�Ly#�^���C��E7C)i�H1�������)H��	�P��5��\�V�J�ye%jK�H38(�#��CFcRA��]���dL('�-d�s���$���<��9��i���U��"H�f��R��B�@�JA���C	�(�&3� !D-q�:&����&���R)��'��2�Q\�H	Lk��,�4�����_��GY�B5���kY�k!��=�&5���7s����=W�EU�&�bVZ�@���y %_�
/��Sj"�y��	�YRI����4B�I��3a��O��dFJ��	BR��T�FA!`q�QH�W~��x#� 
��
 q����Nc������	H��E��7��7l�7r���F���"�p�Q�B���LN�y�����}�����BH��`�-���-�����]�
|\��(�)HZBqE ��y����kmI�i��Z�IkP�yLQ��w���_��G���u�/�
(�:�B/[p=_�	W�����6��Y�f��1\P�@��M�i)�@��%�jhx\j�n�����S���i� ��C��U!%g������W\C<%������@�bQmc��tE��x���H�D��#-�:N���5u1�5lP|�����N0��9����~>�����[�[��IE����(X��]f9<�;�e9��O�51|��`>^��TJZuVq�_`���6H����_��"���y-��B��&!�>p$A?��|���d�a80�H64�c8*�Q�Z���5����C�	�Iw^8j/:A��Gx�1Zpy�#���8�*d������A�`w�P�����/���7�W��(wv�:Ww�����E�W�2����\%�z�{p�x�U-h��
$^���E���K����w:��'�hAW�>��A���������������I�d>����d���]�`0�F��8�:hX������N��.�z��!i����B���+���r�����(���B���D:XR�s������X7��4��6
�9Lh	��_y#Rt���&�J	��|�s���7�����?��9�5W������b��*�����a�o#)%r-����8���y3�s��
��9zH�g����-#h�m\~7"m�����#���|���e�F�RX=l�t���`_2��5q0$�W1��2�@Y��h^�����k�M��X@R��v��r)i���o�[���\����h:���9�q��(&'��j^��|��G�$�����*��������DL�K%�T����5�#ZA�p5��������22
3P\���S�v�t�(�|��d��F��_��u$���X�e]���mM�� t��JlI'p5�J��@/���(\[K��5�U�w�V�k�}5,��e�:p.�-)���M�C�6����0-�CQ�:������fe;.�9;!�������h�n��;��\|L�F�+X�)(�dz
����f��Zt_
��pb�����4�[��W6$��kZ�<��VQ��f�BFJ����HI+�(<#HI(	.�D$����Z�k�$$�k����3k�g �N{H>�4�Y��V ��#$�D�j8��J�D�%��)8c���H*�9(�V����D3<�?���{�������_�^Ma���`f-��q9���0��`��R+����q_���[J�-o��f�L�����$)o��f	������T�bT=��S]��:������5�c�h�e��6�]��"��X��L�*`�+E*q���}��`����C��1��R�F�"5��t�s��Mp\
��C*<xBp17j�q}�H����l�ss-5�O�a��(������Rq��8��O�|�9���;�A�Pra�`�9�j�(6'��]����)��4�21t:�|����O�����,��S��a�(���E��Xr�G4����HA�W���P������C�/���X�����yg7��Q�]g�iY5>)�����(Fr���N��J�n/�x�cv���wt}�������i�u�A"%1Cr��HIII�����T�;B��^��yH��d�7	�:wn�
Cb]�2�c.�.�������H�W>�[J��pBU�����?�� �1d7P�T� ����prv�����zCM8G�����WC�e��D�p��,��sM�%;��B��S
d��a.�'d�&�l��f���������8'�]��������I}���B������(V)�"��ku��* q��!����.�Q�K�����hX��\T]4�$F���xu*V������F��_��E\��G]�c��)2\�\4_��1W�A��Le���9�\�������C�<��\�w���
t$.A�����|�������u�������Fx��������PJNRErl|$u�(-���n����{'�@M,�lK[�'����������!�����c@$\J�~�-w���)��K;��6>LcJ"�x�AY�*RM��\�wuA��$h�4��,������j^R"������
/~H������"�������'(��� �A��9p���[Qu�WAB�~o�x���p*��d
=Gr��d'�	���\��E��"_��e��!�7��/r���iH\����1Ar�W�����~�V
 � �]$��\�3��Z�K��b����m�[������{��Z! ���}HB>7}���/�!%H���O]N�"��Eqavm|����s	���~pz�"m(�P��p{��p�5��g���H�$A'���n���f�H2�����|!��U�tQ��������*�j+���	�1���������_�b����<��l��Gq+�(��j�Ki��n�[5Lq�� 4��%`��>V�o'�wY�qG�j��p�_�,%�s���{I����{��7I����S"%�DJ$�I I ��C�N�{��	����������xj����a�h����|��
�}����'������q��C���U�7����C��.��q���\�~yv����
������Fz8�i��L����)��������qBR�{B��k@}Z�V��+���#bF�3����{�H�n�_�XV����H�#�7���K���G���8���;n���q�W%�����]G-�i���]�G���(4���O������=&26� ���7R��hm�;@
r�<r^f!Gm��D�e�W�B��_��0NHD(�Ft��O}c(U��[
�3�������1��X2�-�h���;��7��*�d�K���\!��DP����c���Z.<��E<^�,@���?;I�_���&��Q��������d�������\���8��sV�N\!�V��]�J	V�������.���:��������)���QW7���l	��BJ�\��1p���!AV�I����@q�R/$��Lu��-��Ca��������0����(7U������,U���7�t���!����
�HHf�$V�5���I�\H�" uA��7������r��A�a�k"���Z�H�����\0�!$C���������yW���f�R8�-�>�D�����������95������u8�X�3���J�������CJ	7�#���!��BJ�N�R������Z����G�:�x\We'!y�{/����W��G&j	�������7�^��q�� d0+%����%D�>rc��-'��f�{~�4I��/������k�{w��u��U������M%����s��]88�z��b00��E4��������1���bC��Y��������h�i����'�{w��}�j������ku���I_�6�������B+�\�������w���������������)���I"%
�_&�$�$�$��<������Br�w�������Y�o�e��d�D)��rC7�_`�%>�����{v��0��Ih!:�d�%�~�'�1���K�p�G��}��!�}R}k���
�����
m�����U.�KER��v���.���p��Mc0�����zdl�(�aK�,38��"pg���t��*�^�Rr������"��f�/k�y-j8����r=� ��g'��jY�Ff_����p�^M��n*���������N���$�JDGyW�(%NQH������k��}(wu�M�;�������Gp��TU�1b�7�DoTg�!T����'���+�8�sM�U�
x&�|�#���>e2�M�(-	�M��Z�`��*�G1�P�U���:C$��$;�H�=�+9$7�������z�a8��L�NK��=��Ef(,�����g�3�Pq���-?���*�#]�M���r�&G�*�)�F���j��6��9�*2s\�U�����K���$~+��\��mD��b@�5|�},��
�*�kn�*3D���
bF����R|�,���C3)����C���e�G�3����
tl��W����jF������)�w��V�J�P(���<� ��)d�����7F����%���#��g���G���H9��������m[�����g7Rs2��0
�/b�������Z��mQ��:Q&M�����������E8���T�
�zc:�v��^�c�GQ3n��OSX�p�\JJ��
�k���y�;H�yg��FH���%W�g�P�������T�^���JD��8��S,�)�P9~���|b�V�[�1�x\�V(%��Do��bsz���d����kT7�F����x^>C������qyv���c7l�N�tO�����9�h��������<������������[��:�s����($�������!Ap�y�)��t^0���Q��7�7&mA���-+��
pB+��8��:����#��Q������������DO +#h5�2h5)%���4p��v�X
��ErE��U51C	gF�F�S�WxcnH�?C�VP�����R$`�h����0>D�������e�W����������T��d���I4���Bd�45��Y�V~3�|�Vj9�.�By�T��`���jXE���VmR�����M7:���5�{0m��/n�:����M�$�9Y�:�{������`���6�C�y���F��5Y2�2��+L��svu�cgAC�Vt�S5�6�+����Q5�n����v���Z������8c�w��M�=�Z{�xC�'�����z��@��B�7�K ��;��������S����r]�o^7$����pN[��J���	z�4���
�z���Oy���{��R�<���	�7�����QUA:�7<�/Do"�]�sK�hv��h{���%H��(tp�}���h�si'v���!i*jXG�?d�(
&!9w>�aHu�����_�f&�K���:������#��������U�hx�C�]~�p�����R��	V��T2����F�1������T\�����%�kS=�{��r��c#2g��	���5������
�b���!q�H�^��*�b�t����R�Sv�m���S�S�`��%f������I*���(V�����H&�����)���[l#K�WL��y�m��8�J�
�N���IA�G�J�Q;��;|���/eG���M�#��+��x�l]����$f�P���k� �r��Do�#$�i��� �J1g�������E����UD�w��P��I#�.���r�X�c�y�"�WAN+R�s����q�q^�L��\�/!�����s�Q��I�_����&N��"�����*�#��'�hV�z	J�8h���|)�+���QJ��=Kj?C��9zN�s����$��/~�E�5��wo*�
r�����;r���W��^P�&\���J���l������j�7���[vb���P��/A�[D��L���)�{/���s�tC�Clw�4QA})�W����|'�s�ei��D��/;�����!
���j��6���+�;�f+�P�n�����=�����������9��j�+�y3��dT�{�`I]�����k0�����B��G�"�����r����{d��&n��y���{�����!y���[H����)�$�$�$�$�$�$�$�t��.���$�dHx>�Bt�(������;;N����������D7z�Brr���|���������c��"���������t04���:=��%;��4�e2��/2���w�����-�\o����
� IDAT2=wB�q���������B��������1�n�%;�`�����k�����j�X��k��*Q���{��9"d��%ZW�������?�j��f�b��I	n.���P�r��$�XLBY���J;���c���q�b���U�$>�7�of;�	���6�FZ����-�>8���8�1�H��(���{Oh���p[3�`�]+�{��=j�r�ZN�Kv%��������?���w<'�Dm�(�P��[�{o�=���
������	-��8�2���"sr������/_{���o�����~�s���h�aI�!!��d�MW���/>%$wZ�o����p�����~/�R,�-2J:"�t%$v�	��yl@1C25��D���^��ZnVE%7��
�L��;)�Z��i�9		?%$���]�>��($\,]�3\*!)��`��Xb��-q���W$$#R�O��c\�vr!�h[hB��^��dW�����?���/����Ha�"��z�{.;���*V�Y����W�-"�;��
��KH��<!NH\�'� �v����#���3�r?��j�9�>�����w�h�B����~��0�����F�'8��Zx�)"�7AE��?�`��@q�1N���O3�t�\���{�����h.�q����_v�����]	�����Hn���"FI3q��-d��Op �����8�0T������A9�o��?�9w����X��7�,[��E:g���"�dW%�ZV�w��l��e��)�	�H�&)�|��Cr�u�p�5�v�*gY�y����;8�n�m(���K�["��mI�(�CO�{����0�T�Bkn��F�K�d�����N���VQF�\��z�c���:�����^"�k�z�v|o��i�1_�4��o*
I����@����U�(����I�s�8��#$wM	$	$	$	$	$	$	$wL#��
��<� �wio�[+C2|bj���]���Nq���l��|���J����
Zg������Zn0�Uw�������-�t�5r�|�{'�*�o�=��v\�������M�<;E����"
���K�|��5���^����*>��]����w�@[�R�P�������4=�P��$Y��3�V+��9���/!Ej�s����|���0����5|���
?%�[������B*d��L9��b��7>�}<�������
2���Z����f0�Q6�6������R�E�[�&$.��?�sv>�a���%9�����*����Ap��������9[�2\4���|���-)d�u�Xo�y�}���`�*S���3��X��nP��6���_���o����7tx�������52�RS8�w@����d������m�>������{��">V��MS�?���c��3��B�?��_����)]~�p��'8���s>��$wM	$	$	$	$	$2$��0��������(�D!2���9�X���N-���$nE!������A��}�;zeN�yu�h
�\�.�!'�#$j+��w=���,���`����a�4$����W�OAR&D��$$��r��������|��B��d�U�7D��	�6L������,��}P�Ch���W4�S��B���I��"�6��Z\|;������k�z-�P(m�����p�����nBH�+�����iU���%�A�p`A��z��h���HF���LCb��$����4�\�� ib��)
�c�������&�[s���I�tvM�]g�zcMIn�Q6��>�A��D��y*\��4HI�p��)H�\�]� �IuH��{x���[DV���n��i�\8F4[�OJ�������Z��j������}����SA���,t����QH��0C'��Fhfr���������o���\$Dz�S�������d:�2�����IT���)��l�����V4��i�<S�.���m��~��E�A�����*Z�tZ*4�
��f|R2�@RiK�R2*�&n�Dx;���P����.
�(�$j�
���
/5���.��($���K�������BQ#:4Q]��'����u?% ����@�np�(~o-,�����Rw���rBE�K����������V
w��s�h��SQ����v��]a�o��jp����Pv+��)>��a��+�p������R�������h7�����A]�J[��������V
�o�(�&��:�*$;p��X�B�B) ��H��Gl�I�����Q����@����@R&9�7:Jx!$_����#�`h�`����0jI����������-������D����
C�)r�X�BX�������x���Pqu����M��I�W�.Ve��e�pQ7O�eK���[8@s�����Z~Xm�);�S��#ic,�|%j�&�l�Zn��ZrV�������O����@�@��������������������������$%�$�$�$�$�$�$�$�$�$�$��2}���[��������5�������}��I ���A$��mR�@�@�����e�	$	����'�<4Hr	$WB��+����������*����7�����H��!9?9�%/ON�g����y�)������7�o�)�Eq�bC.^������=��:�/^u#������=���)�vq�^�-�bv��)c���	�����<N)��>N���'6�\XN-��^�.��A�&������v@���s��<]!���� A�K��g���"%���g�nj��������O��L�](>&�+\@�);'6�$�S}�M��O��g��6�o&R���
�!e���g����K6P}F�?c�OV!y)��`K�3}�� g�d����<c�������$%]fR�L����a���nO|�H)y�(J��8���.���>�wv�BH�4��X��5@����1v��������Z/*	��m���$;�Mm�����;\^d���!%�mv����1�O���"����VC��l�:�)9�+��<C�{Kp�����
����>��DJ���c��t�������K�,����(����x�)���7��..=����?�����
�!$RrH�w�M����>��d�/vN@m��g�@og�'k��Y�������NvvN������p����;��9IF�1�����[��\Z�S�H���KF��2�c"%�'8�$��DJHxz�{������|I���C��\��J��}+.�$�%w��d���+�G��2{�'�%a��9��=������"����Z����0C_0�����cq��
I�����)yc���Ux���_&Rr#Hzt��]O�>Y�]���q���e�H\���K����J���r��[8e����_����^��{;s�{���������������&��ez�z�\�/�����)�	$�=0�����-<\��\"�;������{���R�`#���2������0`�M�����y�C�g��;AWKo����K�����)���:^_ZP>����YYY^!�X���t��BJ��$'�����'vzqye9C�i��!�gH����B���u{�@C{������DJn�	�:��$���w_�?nL�6��s&l�I�����-���&Ko�� ��,t�v2���R���?��}H^�c_��DJn�N�i{A@r�
���������vfO��m���{!a�M���<�`;�X�|*,����9��K�1��$,�g�_$RrH����E�C��f�����%�����/��l��v3'��l�����>$���������G).Y,���t�������L�q)Y8d�d��&9Nl��l���������WQ6�n~��$G�~~������~�����'�g���g�}���g�W7�%��bu�p�1�wu��q8�O�}���.�V"%�@"��]6�f�������W���3-���W��'��F��<�?,H��^'���9�H���$��[A���W��< H>��|��H���dk��$Rr$0�[����G���*�_TV��OwV�p����GO�)y�|��9�O��I?��,�jm��-�~��]��~�r��&R�� a�]t���)�E/c��+�4���h~X[K���I��������<%?	m	:~S���n?�^]L���I����?���������������g��v)yoR���#Cu�S��$]{������)yo�����t��8������y�>�-��R�����N����yBI %�
����[$��,���<Td3|W����3I�$�'�$>�DJH)I�$���!�S/������'�?S���:��yAr�g���vIt����H>���H�u��cv�e~�3���u�c��'�1���e��q��3fw�C���3po��v�(�c�O��������7�������G������#+�[�:�2�5�����;�i5}j��e����l/m��p�z#�-�H�GrGY\�/�R����&+}��<�.n�����FJ~{?����q�-h�2��k���_!�>��iu��m����]���x�3�%�x��b���m*V���Z���W����S������7�����R�����I�������:�M�8K����B;�>c]rr��� -�	�}��h?]�X��c����H.�	���-�1�J�����U?���;K�_�(T�����g)�	����hs�O:�c`'{S�x�~��f_�>c %�y����pU
�e���kO��@B2�.���zS)���6�����9���E�d���l�Q�`i{�M�x�����
,���Og �k��1������_��[K�wP\gv��'�oJ)9��#zq���_�*��(���|�t}�E��a�����$;`Kl�;�2)���Bk�(%]^WOD�3{3��_�Rbo.ll,l��bf�1=e2�@���6����_/ �T6�K����(.*n����&)��|�=B��5�C��������~���BN0��Y�w�� )�x
;�e�';�pb�����b��Ow����!��pb�<��p�U�Y��RJ�ww��7�����������{u������O���gr���Z��?"���wf�RJ���j��j�x���q�,`��0���7��r���!���Au�@<��3�����L�����!M���/�0":Z!�o�������L�r��wa�wC�w��C>���z�A&�����;��2��c�H��@��������f�W��X����SP��MH���=K]�>;d\J�X�x�R��+����V�����W6�H����8��0�)���E�
��B���`9���^���W��U��>��[��^J_�����.����l	j�����p�Y���OQq-m�#��t3��W\,�d�o��}�dg��*��������^dV|^F�}r�`����4��a���d���($h�a����fl����^F���c����?:>�^��vyO��!Y��H��y�`l�K2���\����"��G �GHV7����<^�~��Oq��
��Lzeeue�g�)����dBHXJ�5�FJ6x��ev���m-���fj!d����3��P\�����`��
��U4;�3����K����,�g��@_3[���%����4Iz�RrF����&T����}�z! ���)�uBN�sz�������	$��aak(%j��5��>=�������������c��V���0��:�m�B�O�����m��"\�x�Xo�.�e��^��?�=.{�,-@�%t�/��3��.,�-�di)��CG��E�R�s�u~ZgOJ@q����aD���E���6n�������'|s-f��`w��T��~_�+�$�W8�����g�vz�?�x�Aq���g��j}u��
g��q������/�E�d�`)3/������]J�����zv�OA�$�7/�|��C0��d��wL~�R�-��l������X������A��v(���m�t�x��Vp���m�AH���������q���c�s��H�,��9\ZahK����mr��H�����!9�~�4[=�o���28z?�x�v�>)�6QX3��Em�C�_��_apx�~�	��z�
�V�%l��� �L|e��`�x��?<��I��
��|-$��*�w
���_��X"'���Y��r�C��������S�6�??�J{����~�K���R�6�]����c%E��z�f������t����|%�D6p���6���k��a4�J��	��=�.�����|*����3|�A"%7�d7un�V���fl5/���dL��_Y�)�O����g���y�����l�	��v�.���`K�I�>$;��<�c���N����,�yxIzG`�����?e�������?�'�l��6��7���}����g�/����l	�y�b��)h�'?E"%o����^�G�+���<��v�.[������1y���m�idE�dX��N�7w�]�����}gI���B�VRBJ��������G�K�G�O�R�0�:(�.�z�B��p��C_J>��d������%Q���+����^><Q�V�@J(�y&8�=
����!%�r?��R��S��3b������u"�d;��%�v>��{������-��
�^��E+���+�%��7N�� %�	�����Io���-	�)�o���_�����2=d�tf+�����������d�1�6 ���c�@J^��������DJ��q���]$�D���8v��Wbw���v���}p���F�?��Z;�>�vm������';�gx�J>�l��Y��P���O��x\����F8������ ��g��-�
�N�AOp���q%�$R�H���������)�#$������DJ��m^��|�DJnI������^}�����a~��St������{���������������	|����z�}����N7m�?]�=�2;O�9p���l��q�)�1$����9�gV��mF������������I�p*��<������6����z$��G3�?]]d,��y����C�}��������>K������~�]��z_.0Fw@t��?���j����c��1#"��S�m-���*v	��4���k;�.������{v�Y��!�/��m\�������`��|�����o2������D�nFDw�W���M�\��O���.Y�~�ty��-y'H���wa�V���/��.�=Y�!5����9B)����/%"�����x8��<��y��"���%� �_�-����q��
n���a,��Op�#�C���	)+X�y	����S@��Nq)���.PJ��"����������['��S0��_8a�8���f��&��q�uH�{�q(%/2��6�����^����QJ�q�)�������B"%��l�I�o,��19["8�=1T�!a{
����9av�&+�)iK���%���;Do/��>��[H�%�Cv��aD�� ��\��2 ,�fj7a�a���c��N�3���>����|O�����m���"����N��_�k����i�����H��|�_��/�g�7_�Qq���>����DJ����jK�H���
\�J��v��/�	[��x��!��H��m!a��{=��&�"|�+	_���"[yj!X�������;z�Cfw_��vp���{�i/��8!�P�>$l�n��p��<�T�i�.������:��L3�5��[\}���%R�F���=�[����3��2�y���~j:���b�(R}
0m��qgi#�b��&R�F������b���:���c��D(�Ct7.�1{/#��}����������~��[C���������d��t-�R��U{EB�R�+T��`�}����E"%1��}�������K�.��t����m;��m�*R [�h�)%=���&K�d������va4�[�3��R��q���u�^�����b/R=��l�u<��������������W,��H��;��"�G]�� ���Lj�k����<)�e��[6_�#����S���Rj�����}`�	)��.�����'���R��*����_�x����{j�^1�m�/E��=IDATx$��I"%w�GF�3A����.�����`�R��w�$�?����/yp�$����o~�%R�!}H.d�=��WO�nW�|3(Y��i)��N���y^�+����B���p�9�.K�/��$<��]�^� �����y�4S���t������{�S��N|�<\��S�����{m_��; %�9s>���a<����<H8��"��������y�������r
����:��4u�V����ee�l~���=���o����]���"'�		�Zx3��o�3��w�e6�lM�X]�Pu�>$?$;�����A�p��������AH$$����"=v�)9�B�o-|��s���������%���V7�QE`���d��8�n���C�{6���0hS��[��E��-v�/{��h���R"�pm/�(%l;E����'%?�����������6���1��[��Y;�f�a���\��y�R"c�����l�wMJ~$�\�s
-��!$�0H�)k���;��������2\7����~H������p�w��nI�/��;$�yZ���g2��K�Oy'X���W@����/R+OW��/�l������cQ)y����tp�V��9)�[/)�O�u����Cv��9���W�En-��������#��������~0T<����aH�����w��|��X �	�6���M~���.
�6u�wKJ~��
I�	��p�	~��$R���V"%��/$��D��g���X{������m��������
)�Qo	H��~�M�z��R[{[t:�k����J�z�����TZY����bz1�:�����_�	N�������tz��0~��(�~�l!�/>�����!���DJ�L�8�_��R(�I�^��0���������S->	5��9@�kvc'�FI�Hl�!��8�w�����|������;�'R#">$��=��s�dWn*��.����~��@)������=����DJb����wV��%����f��=�-���\J����EN�)	��~�������
RrL�$�x,�-w�^�MLT�'��P\'������{i/�!ai>��m��������;)��-�Y"%���#Rb�`@4P\�����-�)fm����J0��K	[�wO����=�l�DJ�4�)����'�/L����#��~���j�<ImL���n��q�< �a5���7�a�P*���[�2�aJI,�\�vd�f�'�~z��'������/yxz+����m=%R2��3/��Soy_��_��?�������Ar�E>o�rO��~X�|��	$wO��M�Ok&�<�.��q?i�A��/��zzh�|�%�$�$���pI"$"$���HbH?Oy`�$=�IbIHH�I�Lz�
���?4H����$�$
�o~���� I�$�$�����	�DH$Ix��Q�?�e�M�����k���}����3����j��:����KQ7*���_�b�&�����_���(��/:.���x���Fe<u���Fy<u��z��W���QFo�"�	WB2p�K���$���V/����M�R���>���:�����?�mnR�R�5$E���jP�T�Z�3YqH�V1 �:��:��J!���O��.%�>yK.#�C�&�Y%d2�I�	%�h�HHLQ9���?��
�����B�qT�u�I�O���R����K~�
Hr�lk�9�5$-��j����!!
����E%M�k��BT��lU:\P��&Q�>$�,Q�*j@[��vV��+���t�DL���g�����*.TN!�#^�65�a8�P@����(�a��z[U:��0Z}�w�D��|�b�	-B;l���B�n?����y�$uY9%�^�4E�����L����� �dR��&~���J��a@^�o���2`�A�%�������\C�*"(N�������A���pm�R�(+P�9T:��%R��A1CVC�u�h�W#���� tq��j!%Z�<hvPG?�F;��u���PMTcm(���!4$x� �h\��I�"A�*~t���J��W%
�
Pi���^$E�zc����,��, ����bh�4B']����D�)B,
%�j�(�Xja7����C�
Rj
NB���%A$?����j�����)W��%���PU���+�E�bK>��*	������A_�	�F,��
���.�m�dc�A!J�-*���e������dY'FT�
�Q#~XP���H�3��W\��4H!�QZ=.m���t:.�[��rE2	H�r��-
����Zv�X�M_���6�v�8�jj�����U\�p$$5���}�H�t��cM��	i���R��WJy
_$��u\����!���b������C�%��de��d�"�B�R^m�$�!��GY��{X��e���W���I�F�f���2HB����}H>�B��[���nv�~@9�c���Pu>Cr�@�TK���g irHt�fIv
�R���Q�����d4��*�r�P��� �VA'Z.fI�A�Ub�l"��J�%-bB�AY'��uxk��m�kU\�-Y���H:@bJ4j~���5$9�����+��\J:R_9�A	�������\f�Z�Hd�ucaBH��2��aMI		E�#$-��s�����4x��`]A�H~�u����MII.*%Cj��Yby��pd�_�W@����uJJ��5�k1$U���i�C�G�[�X�����"����	��/%�r�/>�H���������;�H	���^)%AMH�\���D�t��h�!swJJt<��p�)m	����TR�����-��dR�A����`�VN������y��E�S4\2��f[B'$��8\I[ONm^!%��p!��moR�;��N3v���x����(;��CR�����ugF�a�����jaK�H���cm�����U�,�qI�������!��
Hx�!4
�����pM���WN485T������p���$�s��P��B�Fz\>$q8��$$C,*V�%RR�{-��~�(�Dhq���	����,�(���js��K<�)t�
^���\(i�����$m�����Jh%�x���f��U��0�yc��q	w��������X�q�}p>.i\�7�i�M.i�	���3����0kn�5���`\�1v��C�f���0A�!?0.1@n��&^������C�:��C�wd�X��6t�nk��nO��4�**y�9��`\�9W�E�
�w���K�7~����55.(��MEV�����������(��P��'8nH~w�e�&:
����@z�������BB�T_���D���y��$��[@t&�~
+a$���IL��H�5gH�����3/��+��]��=��zx�
Nq����7x��P��':���l��_��)H�$�Z����B�w���X�5?T����
��yH��^��k'm8�:Np������3$f��;Ft/$
��'H��B2������V�(td��,����)%t���zi(�h�|�4���Q��������^��Jz���u�q����o����Q�w�x9
D�GO*bS
%�����*������?</O���A����a�9��va\2��(�c+FQ���F�����j�)�_0�]�+�e�C�Z3*:�J�T�����2��L�r�> ����"��+)�z����p���hY����}�t��`� ��)��:p�V*���%����V�o��I��6H��Iu
n����U7�q�i�^�����)z�J��WT��hGU��PTU�d��
���@�MK�$J��U8p��h�\�����,�t[�4�c�%9���(V]�o�V��+�A�A����y�8�[m�����{���@�**D���+.G��fP�9�����u����D�'����U����pj
��"�@S�'V����*V�e]�����o\��!�����:����P\&��d���o�������oZ�+�������'1����(��P_4P���p������+�<���)n�9�r:������_q��2Vw
�������X���F��L:��4mP�����!I7~[y
���<�� ���s�D|�:��W�A�o��?�|%�o�>�Z���j�88���?�]1`���"�J]�z	r6c��:7�%�\TJ&H�x��4��iNC"O!$A�
����r�M��EmI.G�55�19��/Q8x��PJ��^EH�iX�P��j`�D����EmOCB�($Ci��9x����<���g!��p^SUA��4����Z�J�T?.��+��8���r���z����3��5�m�_��-�<�H�<5!��`�bNd���W�N�w^�A�mN���bx�-r_nS���!)\�J��E))F�FHI�]CJ�>$�$b������z9�9���?��d�T����+h��
��H���5��h�Y�L��lO�W\
���S_J��m�A'x�!��p O�5n4*��F���/%������2[��6
��x��R��|
����AA���)��X����l1�%_S�81A���mFJ*:ol���r����$�x����ge%�8���r����\qVJL�-+�4:dt=$8�pmcQ��Rb!$�x���5A�3��_�B����Z]����d�}��S�"[��f�fK�4*M>�P����9"F��a[��Y�}���n������r�PF��q�i�*f�K���R�{�q:���'�)6��R��7j���]0���l�RrnDJ*�Y)i�"tvPxj�{=$j��A�:�V-kJ��{��&:|�{�F:�TU
!����"V��z	�zM���U��KP��*���R�l���S��E��U�P��_"�ku��
`�1hC���������85�2�j���4����������4�Q����w��u>.1��E�G��>��p����E>.��BCSw��o��Z0mk��*��;V� ������=0@���Hq42�*�w���(h��Q
�mU�#U�0���SN�������Q�(��%m�`\RhbK����+4�0���2���D��v����[�����������-�n�.d�;9
��������j]���2��;��x��?��Bw�
����0$����?�������VMF��������_$d���������$�<���I�$�v��lV���}<��?�j����8��nT�Dcd���-���uy�0)�)�^�yO�L�r��trJA��]�����!�b���bQTk��5�a��B���S��z�P�ArEG�R[��3R���d��/UT$��c��3q�:�!V�i�o�|��^��X��uSQ8##���\qR�0f)�<�`������;$��"k\����^��y����#�� ��G�yp��$����QQ�6z��`H\�bz��HC/7$�?jG<���%�*��5�V{���8��+J�����S\�O%�x�����?�;$�V�`��(�U&c\�i�p��]�sR��U9��������)�J#rP�U�q[����C�$���@T��;��%V����	�sDK�W����J��	+M4���k�]i~ ��Y����A�#���|�R��m�~D�R4�I.�H������N3��^K�@ �j�lmI5���h��sY���]�??��������f#N��?������=��;_$�*A�����U�������c���7����V�X1�("����k$�I�v�V�p�<��^>b�4[���{+`0��\���8�pL,�
�(3Zo�Z���T�8
�l����w�\���
eKL�d�0u�����sv0��TA��~������V�.@���zU�@D����8��Qt���������K�������$%�$�$)�$�$I�1H|><�!���o����1��{�r�xQ�_���0��;���q�)2|��.�3��w��}]v��0f����}�����r����|x�i��R�-?cp0���}��@}zp9eP��3q&j�7���q�)2<B}O��@"8�cv�>(��k��J����{�)kx���s)V*�>o��P�Fi ���]���T���v�F�N=Cr�5�
����r���3M��!�]�\�B/8�gv����@���P��!��;��"��QGs��{���7H�P�#��&���fp�����V
������k�bnk�e[c5����b�������vs�����1Bryv���t�b����T��r��6�u�{�����2\P�#?>�h6P�\��Ky��
�������9�>�0���[����|x�i���o������>p�o�F���fr�55b��;�����.����F�����$��cMSd8��Kr����%�!�p����3��B�5���0:��!�^�]+��W�������
JIv�8!y#;��UB����	�������Lq����k�?d��������\J����]\F�'�����$%�$�$)�$�$I$�6���������;/��L+����Q�{� �>����=s�eb���C� �;������.�V�&�A�G�w�k��7T7):�w�{}nU������p	����=p���N�6�#���WW&g�
�
o�g��S����bJ���
���1�����
Z�4U�dv�{��Rn�E���!p��G�J�Ud��6�-y��PP !U���v�{��[�`_�����{��tND�n(k����]*���]���]J��"X�v�����u�E���@2C��M����p�dI���Q���w��N4Nq�
b��{_��
i���� �
�@�=q��?�@0�a��7�wC�_e�e����I��qW���4W��{���S3�;����m���������xr*nA���X'�!k@�
�4�_VP>M��1A�{O7l���k�>j|�����PT^;�}2|5��
`*��&��:��q�m��^����s	��c7.c3X�1R����xff.j/��5.��k��~���`�(3Z���������y8�+�v����	��o*������h�	BS�������1����� ���(�Q2%�`4J�����<�S�IEND�B`�PK�F�VMETA-INF/manifest.xml��M��0���+��T|C���=�����R�=W^�+�)6����!a�"e�����=��3�e������������c��,�9ee��z|0"�~u��	���M����g�w-K8T$�� �%���������d���u$����+��o[&���w,'RY������K�JxQ��#C���vH�����]U
��T�N�:rJ�m �I�T4Y�,7������i��b
 u�-R~<��L���"������N�����l7F����F�+���^���E�.x[�SJo�v\�}����2zPF��k�u�%7��\^r���Kn8/����:���~��Ws�>����"���~�2�Bn+x�3�d�?�����������<T(2�7	������N%@� C	���@�Q���P|l(�$P�P�P�P�e�3��8#������>sV��k���Yt�/��Q3;�pQY#�]!��{�E�!*�J�
%�J�
%F�cC�'�����c�~l�_��i� �[�?\��\���
$X� %e��?����Z	K�C�a�� �&%X�����6���PK��C�PK�F�Vcontent.xml�}�rKr��}
�����U��u��9�#*<�����s����c"A> A�.�����z~�������&�B<JDW6��+�2+/������t�q<_Lf�?���z5_���&�?���_�~�_�����w�����������j|����������o��^�����F������j�x�<}3�_���������'���.���7�_AQ���A)z_�>��~���tx>cf�nF�����/�<�\����O�>U���JBx]_]=;�����O�Qg�����|����������r�������b�e����������Tb{�v~�������f6_n��|�)��d,/��_v6��tr<{��>^$?��-���r4O~T���y�!�-���S_�^}���?Z��^4������i>Y������~:��n���1�i:���M��_���Op�������c����b���g����X{����'ri4���<���u8����^���/�W�v�!W�C������;���|2���f�h�mf���b8a
=����G}�vW��e�\���|����wZw~��4d��=�On�.�~0u�����3�pvv�x��:�]�,U�Z�0�(z}��g�����~>�_��O�>��[,G�w{�����qi1��������d���6�X�����~-����7���|��G������p�&>����g.����A|m�����F��N�����t����"�|<��?����y;[�����~]/������dW��=��-~��&x�z���4���
�������?F�z[g+����8<���^��y��^|Y,�W��L��,�G��������Y�����%��i�X<��������������;�{4����=������������t>[�����3������G�����6I^}>�]�x����lD���`&�3������������������V����������x�����u9�P�z}���O�3����A�^���cgoP�7�<��o6�<��olh���)���wj��Sr�����
L�;��xc$��Pn����m�w>���1��>W��|x9�\\���*���|7����pv��\��������8�����oclg��G�E�-J��7P��/G�e��������ft1F����,��l����������L����s��i6�U.^��������?0����������dq3}Y�b��mL�/�W�3���|���3���;����W^��Ng��[ s`f�o7U~��������6���������x�����Q�W������7�Nk�o?/Xx^��\M�������?����d�W�p��.���������n~{�l�,t�,`�,��Y�nu�6S~<����J�0A��p�/��n���[!mk@�m8�N.�����\bG���p��f������h�XkiQ�����Z�!��l(����&��i�2]�7���s����Bw���F�v�a`q9�\�:\��������>�|N�=��>���+��c�����],'�_V�|���]0���M}��C2���h~��:>�M���Y�D������C#�M����W��^�������G��/�����|v{q����=H���1����X���?\L��j}�����KX?���w,.Ggr6�����g�y��������/�����|�p�����<��X�����x�d�u�b:�|w���7�[�s����C�
�q�s�&�k�������Q�NR����_�p�n;Cy��^6��-�f���d}���%�� C���#{������U���xu��r$9"�����Y>T�������w�MI43��5����x�Y_l�������*}����j�:xq�^����0������S/>��������#{1�8hK�y�F����^�h���F��4�K��e��{��O��i_�/��e���x�J�P/���@�������������~�ex�e������d�����'(�������������"�/��r2�|O�/�[G�����n����%'M��f���w������u]y�������t=��@�#����r6��e&���2#E��j�����������(
/���e=����� ���>k0�����(���V2Ym]���7W(���j�f���/�jnX���'s}�]���������N�R�;��_��a���{��/�������W�c6�����xp=������d�����'���;�NU��u��.���[R�����	���^/��E��d�Y1z����rrv6�~��pv~�/���(��<�x:�W�p�������Av��-'�S?���F�����
�V�u#���'y������;Y���R�YXNo����K�8�I���l]�V���-�[���x'{���4������#����,�\��n���QR����q�o65�j���S\����\_��������/���7=�kbys�r��������v�]���_�\����Z���t2>oX[�W�&�+9�oW��^��_n.����F�2���#��z?�
��m�e?z��^�����e?�?��-�x��xC�R��!�sw����-o��t���3�~�U��Z}Q�/j����-+?W�~�O1-_��E���^�������fg_� 
n�r2q9/�]�m)0"���dGZ�u�����9_/���x'�����O�������t�h�tq��/�������/�B��O����h~�X�U�������_�>O�XM�M������hOU���#�����a�su��s�	������u������������jl\Cwme��B��D���?��������\���~������y��X[�Rn������i�����1��y�=��{b���iz��]�3�!]
]c&���wu�������o�~����5�'�N��'�����YF^
��B
���r������W=X���;i�aX��_~����e���dN?���;�-�=E�yh������z�5�����������MP��b����o��E��D���<W;��P��������
�����lr]���hZ.3��*������;;�4���������������^_\��A�������/����`4��v<��p�=�\������A\��{�����f��7���|�$������Z��!�S���[6�j���V�lb�@����9Ocu�1[�>���+^c���������n���KW�ze�}Z�
����/C6a��[�X|�'�7�Fa�����6,��4��y�'�l��h�J�w�Q��<��1>]�X�I
�����F��=�����q���	������o!��o!��Y|����$���,�O��>�U\v����cQ��c\��|%�qX��-.������?�.ok�av����ls�q#�����Zh�+v�����u��'f6<���%���+`����l~������������5�s>������>A{���7��fv#��[!��l�-g��`�BHeS��M�q`�scO�W�����O,����O����q&w����
�z�����
-�w���lE���n�l&�g&%�P.g���`���B (�*)LT�MO���/����	���%�:H
:(�zm)]R�r��KJ�p���A`��E.8�|���-��FTD��v&�K����ZPLA�����,�<�&A�������R��������#��@�d��\8��h�S��A��bkI�$E��,&�T k�EE���wQY~�����Uc�
&X>���]a6	��
����Jd�p6�.Iq����b����Z;LS�{������((������Y/���Ul�{��-/��
��
�i�6	�W,��lZ��?�kA�E+���O����E3��������*�M��G��������KMT�M��\�@d�O��j�~�%��r�e(&v��,��qI���i���%��?�����N}�+��_����f�S�o�Wg�������VgM��LNx&'�3���z����irV�A�t��iU����� @p��(l�i����ynL�����6y������O�'���)����y�����7@��5���o`���/��|��������m�QN��\���m�vN�n���'�dT��r2��b��&'���ap+'�1J��*3�D�������5�D��GY��e����Z��t4������yN�Nk��m�m�T
���N��@:�a�T�d�C���2�|%si�r��j��>y����fkq3���d�R@�b��T�)mT���Qj���IZ��`�M�~SF!/g�P^�����K���m�'y����~���������9�����<���d�w�G}�b��==d*������z���d)j/'!��aO�$?���1�R>2�oe�i���_>��_x��e!��Z��_���fm�6���L�&`����BkT���r�*�Y�����!�uD��~��������]2{��c�BEm!��4,x���*,-�l��!{�Jy@T�*T6���Q!!)o��������T='bAL��.��,�<#�jhKWh��y��dKb�F�EhIr���R���mqaE��� ���dM����_���K�{�1��/�,-�yf�fHK�J,�
yj�����K^��l>#����X�61g���YZb�/t��5lR������l�]|�%V���l^����M�z����D�]�cy��X�����P�!y���<_���KbV�'��e�#RE�3M$CXZ
����K�7�����`�L������X2�5R�����Yb�/t��Eb��7^����H|�I�����K�e(BM�e�NS�5Qia	b)G������ma	}KL���L_�X�������)���������*�������`�N� g����$�{p�[�p�����1W���Cl��T`����2G���	_����+'3���k��l��r�'���D=�����;9,���=FOyga�]V�J{�{��y�0Ho��O<���[���Qd��H�PS��=B��"-*m�QA)�Z��hu�����3��
5�+ "��j�����6�m0f6]Zz���7W��\4���` �Q3e+}�!�T�����(]o�K���mi)�KEl?��n��^*�c�>�>v	����r��T�����8��,��������'M��I�,��]r�E�Cign������|����������NM��Z�-�f7g�����S���3���+l��m/C[zk���.]$�ES�T}�-1���B�J{e�s!��������7����!h��
��G0�gMP80���?�?���U���c������x�-�z��Wy{���h>���8d�������&*n/�aD�U7��}KK��c�(�
*h�6'�b�p�	/:�#(�
��A��U����JskC!�s��|rU������.Q|�=�cA��C�fi����%�z(m��E��u���l�N��oT*8��!�Ti�}$}�lX���%��.>b9��o�p��
�o�%���>jb}-��#�(a
w��m�K��Af�1������S��S�H>��R:�H>GZ�wm��H�>����%@�]g������>3��[��e�R�>���b��|���FJ$�������l����[ �����|T�}|�<��������Y���2�d'/g;�%F��K$��3����'lY����>����c�!�������6bi��J��=Z�n0�����B����A��lc���
G���x���A����R$�M"�CMTZZs�A����B�s2�P>u	�[�Pz 6�������HK����XF�f�Sp�R���xi{W�n�Hur��#Mz���@>u	�SPh�6�(9�:;~;,���;�v
/C"�4%g_a�����ma�*xRS�P����@>u*�G�-���a�Y]�����!m%��v)ozo4��O5p�p��F�A��r�d =���SZb ����y%������>/T���M���?����|�;�m�J��0��� J��U�L,y�����|�T�H;�+�tF�j�w�[K"�m�UH����&hE@�Z��
���#�~J<[����M��.�|���K>5����r||�.~���-w6,/��R�����w��i��
��$������)-1�o��Mp
�%���R��������X:�O&kA^���j��l�<+����d�%��71�o:��k�R�eC0#U�����P�ci)��)x��4�S���5Qq��P3�����TK��g'��@���'tb��`���j�r��Xb�y�7k����K�3��"x�������&�YZb �t��O��g'�a[s�iy���[[Z/5���6�6��Pj��'�����B�)TM��c�]�(�z����V^��Q��$���D������61�t�0`�s,�M��i����W_cd\s�[�S��o�K9�)�n��y����Le��c�	��%�mW��T@������Ym_�vT����
������;����~M�x*���<F�w�,����~���I�'y����H���Tf�/���q���	��[o��C]��e�h��,;*^�Y��������������{ ���&�;T�&;�?s��&m��N�RX��2,-VYv!S����������Q"�ST�0JCjL�&������������b��@Nq��AO�C����A�Dmg�@k��G�!HC39�Fi(�(�B��������
����i�HLN�����?D�I�{�����K�_�D�;H�"��#�H�s2���}�%����4E�}k���S�J?�#�g>	�j���i�--"QY����U��d� %�KM�s^���}�%��� �[6��m�������y9D�I@��
�p���GQYc�U�i����j�}xY;�%F�mv��v����!�5l ��+����%�Q(��_[������>�+���Re=�b
�1�:5�,11Ho���"1�zB���1�9'd��v�"s�X�`�����G�k1��@MTXdU��
�s�0�z��zN���j�fW��"�T7!0����������I��!)����� ���D���
��u�� M�y���N���.���6�t_DF#()<�0r9v��Y��&0%{�����f=�HS�O[9��e�u�M"Q���m���]�����l��$9�EU��=���/N�	d,�Il��
S�PY�V��{>%2*D{8��)21�o;��#�Rk"��j;�l1���f��Q�~L%��mb���>0����I�.��t�IdT�zvf\���pm�iE��lf�d��;�&P%���,�����cES�O�������ab�[$�9(�b��eG��Cf�S3C�W�����E� �*Q��x^��C���c$*{R��U��9���&�Q!r=��w�q�Zf�1��r�+����G/3	Z�`*gH�o�\�+����
�_&���O�#Q�2��.;�M3�P����f���e� �+}�$�����K�&*{�,��Qi{)����}0�Sfb�����	
�������9j�������������|�$?w�HT�Q#<���g/%��H����$�-	�!�������$�G/4	e���y��?^�J��k����h\%���`{u�D}MLp��{�D�2�
��"4��:v�����+`]g@Ab7�Iq6���&j���+��E&��.��4Zf���j��������3��j��*)�	h��6���R��
�^�}���R�f��
�o�b�������m�#���������&���*��5P�RO�#QYF��i���Q�>S���b"����
�
��j�;m�����D�������F�#����
o��VV"4 m���e��#4>&�N��ioI� C��F�rp�9��	tI�V�o����&�[�DeC4���k�hVi���"Q��e|L��R�S�w��;��Vq���,����$-W^��(�OXf4T��i��!$��z���
�;�hE@l?��������w`�$�%�e}��6�B��WDeE������%��WD=�LL��R�l��i�
����~��+�*�={�N[)~��1�QaF]�xUyp�|"y$�����T������&kvk����������7UIq���6����0��3T��:�%iF��
����exF
�#�$i����$&��:
5���D���:_���X'*��H����,�)�����%G�m�R�2c*{�'���������VD�e�*v�
/�0��-D������;u�Z��c���
h���9LLLr�e8/M��RH\�5QYF=UZ[�Ti%n����������@�}�)�8	M^#sG/3��I>T��
�u�K�m�
��x��*P����2�D=�LL��i�A�f�,�/�e#67�<~����4�U���FIC����5�7`��e�����H�s�Y�)�[
����
���Y��z��3�	F�	o����*��G���66����D�������	�dO(�
Q�:���:��h�����B�4e���
IL�����<O�n�`��C{B��G����7g��c����=��}~��O�w}��[#$���!�^5��	�+AaZ�����rv5ZNN������h�`��on�_�g����zx5;�/?~����l#�g����tY?�@�����{���{�g����������d�p|���X�i�a��R���=lW�"<��_��NH����;�}�����+���w���#��Mx���U�}�6�w��X�6�8b�8bO�#����	������o!��o!��Y|����$���,�O��>	- ���@���L? ���&O������_�O��P�5����O���l�U��.���9���\���-�U����z9�-���u��u`[_2Q�\d]y��4z���k��|M���{bWr��-�/L4��lb�bQ�������&v�����
����|���eM�
�Y�$U�� 0
�b���V�~D�j����/�w�������-D��$��I�>Jl[=�%������F���^�]a%��$��:Tf�C,h�6�&zYR��@������2l�&�BT��3rx��Z����B����������dd��}�5c��T���f��v&��dN���j}S�P�����W����f4��A�rd=���}s:����~��l��������*����zi������zwV�I]|�����<v%�|�,�o��z����PMZ���1�.��M���
�R��h~1�/Z�;E���S�C���u���!D���`y���w��c���}S�����`94�����H����`��U��o�����`y��r�������Xy^�<���y����~���I�'r��2��2*3����a��$B�=�l������|~*�
����7*���������q��}�b��/�����I.���H������M�|����C�Wa0��Z��$.�`J���t����RQY���b,����K�"������%��\�r��b����uZxG��l[Z�����6�5s�T��������{Av�"������(�u�g��Z���!�W�l���������4�Ro���E�����w|�M� �iQ��H[���@���fi�G/-i��S�"�V�1��MMyT����f���������-9%CVb�"��YD�U����U��B=�wZ>w��;����3�&6 �������cWN���05U���Av�"�X�<q/�;l�3��9vaIS���	z����0��a����+8��@��R��B���a����K{"dQ�(9��m�M��U8z\�4��*�V�\���7�-,����������T{��v�
��D���#�!�V��	>tFgbS����H3qJ�)o�����T*��7	��T��IB����nB��M�6��E	���,�����������>]i�k�)4��*-LT�Z�
�W��t�[�}�,����DZ��������n��;�N���
����x!*����w]�3�X~��"��{�q!�9��1��]B��x����t�V���<�6u9����F�y��t�
P'D�D�
F����jm��`0���d���>v���sA$�z�B���?ziI�/Q���F��.������p�/�`�4V`3��Q���c��%�O��!���l+�n����?K���e~��YF�d�%$���D��dG�t�"�X�������.1|B�@l����<��^Z=KG�%
�Q��D!*�&-�X����Ti��-���.Q|������t9h����X8z��D��}������%;LK ?5��8l�t�K����d��g��Q|��w^���h �=Gb�;@�L
e��p�(�U O6YXL�sn��$k�~rg�����.a|��G"M�jl;0m�u�Gb�.A��Y�&�nS ���#�*OZ�%�A��L�[�R�KZ(��K �)���7A���Zb�H��;���ii<�
����di���"�m�l+�oj�a�w��H>u��[g�fqQH!���6�����Z -�_�pW����l�
�d�x��(o^�&�[=�^`�vIK��S�H>����b��S[t�����7� Mmw�<��e��
����n-X<(�,Je&E�|"F1�O]"�V��2��ae���Z���oI��/�C�7 ���`�H>�,�y���E�9��b ���{�N��A��,����$��/���X��
(��SO��?�s:(�F��D�!��D�b ���-�
R�#~K��oY�����}$�gJ(���������i�$��J9�������|��w�<��2�-T��w[��vq")����8�*�{F_��R�������	���p�SZb �:�!h���J�^���/-izJ�����<o.���TiK�q�yL����K�(F��S=���5��02���{-ni�kKKq����$99�G��BHN���C����-D=KK��S�H>�(��7�U�q$f��;ziIY+�'
���
+k��*--�@ T��%���>�;wI���|�%���F%���P�%�}�Xb�!(�I��2����&)�������(��Q5M�����K�Hc$�������}�>�����	d��.��-�S�^/��{�g����V��������KY��`��x���6o,��D,�K�4��R/������l-C&*--���:��y��=W���7]��DN��r��%#�B}Be"�2�^���6���Bj����������{E:�0����@���U�Q&8�����lM��tZ�	.)0)��8O��X��g������^�<=��i�ll�V�>1�U�#Z�w�[�0���K�N��^'d�����9�W_cd���[)�wY�qp5�N'��o���R�w|��������i������\O�|J��c��������T��qy0z>��Y�����$���<�O�x?	���H�S�y� }/��}��7pl�M���9� 	AJ��t��N��,����F���85C�z��21Zn�D��TYjGNZ��A�4�����Y����Q�x���s��gN���p���
�R�;J-��D�����r�!^����m�V.�����9zy9D@G�~����
��5Ma>A�d�,��)1�R���1fn����
:���iZ�3�A[�CDt�uN��F�>�6#��PI X��mk��%/B��v������5�RC�O�-a��j�W���� !���%%���j��a@tU`������6�z�6�]��>hl{CF������^� a���:P������
�QeMus�����2Q�0�6�m�
x��a+�{����U�yF��;��5�u^[�`����5Qa���HU/��XHd4�,/1�m�D�]P�X6���:$4�K8z��wt��
����@�/�����eU��2E��SlF���%V��.U��9�wRQ�3�1[��GJ9H|G/HU�����Lm�*D�O����a_D�R�O�"Q����m�2x�d�+�yA[�Q���G�#��AI�m�A��%�����Fh*v��A,0���"Q��J+�m���A��I� �	��l�}m���<'�!yzH�D�q��Cz�R*-��~tmY�[{�����}�%�/���
��*��V��O���������v*1	!m����5�B�hQ�>��~�	�>�/��+T���G���fR9��tj�I$*-1����3%H��Z�l���w���Zo�b�?`N�{�=Xe	��J�����T.�=gi��Ul�8�m[e��fX�,11��:���R�j4}N�"���[e	� �=J�Bgtj�HTX�U�r�����&�91��H��T �5?vAQ�����@�������D�������#Ma�1���;e��m����&�Y`b��u	��2,4����2B�����A�?��Y��=;�!W1�
�v5��
��B�9���Q��C.�]'�z����4 ���[z��Qv�����f��~�x�S��
2�,�.�����g���~�%���y���X�����e		h��� M=<�T������������C����D=G�]���.�
����h�B}�abAzi�`m+4����D�%�W&�s$���xN5Q���]��.!�DJip����U�G�|�p�@�wb{����=�1�l�������#�H��������GC�H��f�J�F�9z��0!!�������`\"�������J�y����bb�5�+1>F�}��?�:iog���\��K.�M
�����'+���-.��������z���]�H��<w%����:�2�������#g����HTX��*����+��UyE����x��������������&;HtH��������j�E��*��F��w�T�S�,11��;�����3����jo������&B$X�5���(��k�����^�v���D��H����K��(��Z������8~�� Q"��J9!���*�BT����y�ZY�*5�_���1����y�T�k���m9��V�=���a"E�S� ���S���5Qa��������D���z�����P�I��Q�T�}V�A�E�cm��eg�W#%J���K�W��-��DFk������]���Ri���?��a�F�3Up���e����������������o���%��Vb����m�Y�Uv�{�aG�����G��HTX�u4�������eM��#��S����8�v���Y���O*;L�H��"���%7���|Kbj������5*�oE������"��P�r� 'C�5q��H���~����[/���y�%��D���#�\�J������
�3dOVD=�j1g t������^�og3�j�sm��A$��<����������-z
[�����<�������4��NGKA��%*�1�>d����|��]�j`u9����Y�����rv�7;��No,����E&^���~��������l������slZ+�����q����G��e��M����a�h�r��{}k
!���������d����=��F$����(������|�m{���+��j��\U�kx��6�w[`�����\5�,W���5�Q�Do2	����o1��b�7���m�Nr�p�;���9����]�����]�����pH�Oe��Z�<v���RXL�2���/��^�a��[G������|\�����7�{|��|\�~KOa���M"���6Jf��j���`t��Q�9�O�&��lS-���FS[M9|`��J���T&�{���������6f���F8��&�K1M�2��2S�B��x�F��A���E�7����.�������D����r��G���5X^��=a�{�,g�_��<��������J����q�s��|0��?�����?����y5�\,^���a9�
�V�;�4Zlnx6]���W����S_]=��~�����2��i|���]�����V��j��Sv�Z����ddY�H@Q�������=���w9w|����HTY�1��M���[��:;&������R�`�w�m�4���*�=�wp[S�fxy��1M���e���0mK�G�e*p��z������e�Kk�<z��;}���I����s2Z"�
Y��,zh�-NL<��&�,M���dAi���X}������f&��>�P���.;���@p��
>#�N0�[n]��<#���(�mk������WB�������x�>�����TbrY�9U��|�zd�*������*���r��QuCRM�<�'bs�*���{��=8}��_���6Ors�����3�X0����a"�F=�O�SW��.*`[I`��������w�W��yJV�����r����������kyf���iw����P����F[*�F���[�'�hO��7�!�F�2o�;�3�]��7�������������������I�<N��q�=���y������y����;i����#�w8y8O��'�<9{���������0�y���������7�������_Y~��H���C[;�G���N�����V������XTi�#��}�A@���m�����)O��k�1��L7�=��	�r���S�3���3�������MG�X���Q.���hJ�&�\�Er�&��c{�M1�:$�!���
����]�5�O�M�g�Q�������_�L{���@STxO����\�8�4��k$���zpi��������^�S�4��f�E+)��6'����4�'��u����F�%�����[N���*/��R�0���,���Me1.���]�q���v�h�!M5�c�Lk��6��1�����J~��4�I[��dzh]yp���3]=%:%���SJlk�vR��-����7���v�Yj�!M�9k-��
�F9&^��<aV��E� �����m{m{�v�Z����{�$��[����+����@S���&�#,��7n��%��lvc���A:�;�sT��q�=���y�d��${����.{�����l[��>����r�=���t��j���Y�K��K�Hqn%Q�%q��9��8B��D�s=�t������fQiQ��b��O~d�����WH�T�V��\i]�����t��v��;�f����\��)q*N'V���fQ+tm@[,������t"b�jl��)�Q��/c�&F�UNS�!{���}Vh�9232��m�C����6q\P������Y�XAb)�zp���#J|����j�U��	`�h����,�9im�T�[��d��}V��I"6�tz
�����^;l��
��n;��\�E^���_�-�
��4�x=�W����C��������H$3'q^Z��������v ����5�i�����M�6��G&b��gQ2������K�(�����������F��k�

���Z{�v�2��S�YY��x��r������F�#����,�G���������7�@2�]/�r�����lq9/�>�0;�r�����V����������PK���=J�PK�F�V�l9�..mimetypePK�F�Ve�F�QTObjectReplacements/Object 3PK�F�V��;��]�ObjectReplacements/Object 2PK�F�V�]9�]�ObjectReplacements/Object 1PK�F�V�(�QV
��Y	ObjectReplacements/Object 5PK�F�V����
���ObjectReplacements/Object 4PK�F�V�����NR$ObjectReplacements/Object 6PK�F�V-�A\
���+ObjectReplacements/Object 8PK�F�V�b��&
�%9ObjectReplacements/Object 7PK�F�V��6�lS�FObjectReplacements/Object 9PK�F�V7�R��U{NObjectReplacements/Object 10PK�F�Vo�$�*W�VObjectReplacements/Object 11PK�F�V�v�7i�^Object 1/meta.xmlPK�F�V�E4��)`Object 1/styles.xmlPK�F�VU3�\�0FbObject 1/content.xmlPK�F�V�v�7iLhObject 3/meta.xmlPK�F�V�E4���iObject 3/styles.xmlPK�F�V_ry���/�kObject 3/content.xmlPK�F�V�v�7i�tObject 2/meta.xmlPK�F�V�E4��&vObject 2/styles.xmlPK�F�V��N�� CxObject 2/content.xmlPK�F�V�v�7i[Object 5/meta.xmlPK�F�V�E4����Object 5/styles.xmlPK�F�Vdo��?oX��Object 5/content.xmlPK�F�V�v�7io�Object 4/meta.xmlPK�F�V�E4����Object 4/styles.xmlPK�F�V�����QX�Object 4/content.xmlPK�F�V�v�7i��Object 6/meta.xmlPK�F�V�E4��;�Object 6/styles.xmlPK�F�VI��f��/X�Object 6/content.xmlPK�F�V��h��)�manifest.rdfPK�F�Vh�Configurations2/toolbar/PK�F�V��Configurations2/popupmenu/PK�F�V��Configurations2/progressbar/PK�F�V�Configurations2/images/Bitmaps/PK�F�VM�Configurations2/statusbar/PK�F�V��Configurations2/floater/PK�F�V��Configurations2/menubar/PK�F�V��Configurations2/toolpanel/PK�F�V)�Configurations2/accelerator/PK�F�V�v�7ic�Object 8/meta.xmlPK�F�V�E4����Object 8/styles.xmlPK�F�Va���RiX��Object 8/content.xmlPK�F�V�v�7i��Object 7/meta.xmlPK�F�V�E4���Object 7/styles.xmlPK�F�V�V��oYX�Object 7/content.xmlPK�F�V�v�7i��Object 9/meta.xmlPK�F�V�E4��D�Object 9/styles.xmlPK�F�VC4$Z�3a�Object 9/content.xmlPK�F�V�v�7i��Object 10/meta.xmlPK�F�V�E4��t�Object 10/styles.xmlPK�F�V�,��4��Object 10/content.xmlPK�F�V�v�7iV�Object 11/meta.xmlPK�F�V�E4����Object 11/styles.xmlPK�F�V8��4��Object 11/content.xmlPK�F�VrN��%��meta.xmlPK�F�V�w���l
}�styles.xmlPK�F�V�t�UO�settings.xmlPK�F�VK���^�^�Thumbnails/thumbnail.pngPK�F�V��C��jMETA-INF/manifest.xmlPK�F�V���=J�?mcontent.xmlPK==�
�
bump_allocator.patchtext/plain; charset=US-ASCII; name=bump_allocator.patchDownload
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 72c7963578..a603f1d549 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -149,7 +149,7 @@ my @abstract_types = qw(Node);
 # they otherwise don't participate in node support.
 my @extra_tags = qw(
   IntList OidList XidList
-  AllocSetContext GenerationContext SlabContext
+  AllocSetContext GenerationContext SlabContext BumpContext
   TIDBitmap
   WindowObjectData
 );
diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index dae3432c98..01a1fb8527 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	alignedalloc.o \
 	aset.o \
+	bump.o \
 	dsa.o \
 	freepage.o \
 	generation.o \
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
new file mode 100644
index 0000000000..910d2b1bdc
--- /dev/null
+++ b/src/backend/utils/mmgr/bump.c
@@ -0,0 +1,751 @@
+/*-------------------------------------------------------------------------
+ *
+ * bump.c
+ *	  Bump allocator definitions.
+ *
+ * Bump is a MemoryContext implementation designed for memory usages which
+ * require allocating a large number of chunks, none of which ever need to be
+ * pfree'd or realloc'd.  Chunks allocated by this context have no chunk header
+ * and operations which ordinarily require looking at the chunk header cannot
+ * be performed.  For example, pfree, realloc, GetMemoryChunkSpace and
+ * GetMemoryChunkContext are all not possible with bump allocated chunks.  The
+ * only way to release memory allocated by this context type is to reset or
+ * destroy the entire context.
+ *
+ * Portions Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/bump.c
+ *
+ *
+ *	Bump is best suited to cases which require a large number of short-lived
+ *	chunks and performance matters.  Because bump allocated chunks don't have
+ *	a chunk header, it can fit more chunks on each block.  This means we can
+ *	do more with less memory and fewer cache lines.  The reason it's best
+ *	suited for short-lived usages of memory is that ideally, pointers to bump
+ *	allocated chunks won't be visible to a large amount of code.  The more
+ *	code that operates on memory allocated by this allocator, the more chances
+ *	that some code will try to perform a pfree or one of the other operations
+ *	which are made impossible due to the lack of chunk header.  In order to
+ *	to detect accidental usage of the various disallowed operations, we do add
+ *	a MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
+ *	various disallowed functions raise an ERROR.
+ *
+ *	Allocations are MAXALIGNED().
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "port/pg_bitutils.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
+
+
+#define Bump_BLOCKHDRSZ	MAXALIGN(sizeof(BumpBlock))
+
+/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
+#ifdef MEMORY_CONTEXT_CHECKING
+#define Bump_CHUNKHDRSZ	sizeof(MemoryChunk)
+#else
+#define Bump_CHUNKHDRSZ	0
+#endif
+
+#define Bump_CHUNK_FRACTION	8
+
+typedef struct BumpBlock BumpBlock; /* forward reference */
+
+typedef struct BumpContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* Bump context parameters */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
+
+	BumpBlock  *block;			/* current (most recently allocated) block */
+	BumpBlock  *keeper;			/* keep this block over resets */
+	dlist_head	blocks;			/* list of blocks */
+} BumpContext;
+
+/*
+ * BumpBlock
+ *		BumpBlock is the unit of memory that is obtained by bump.c from
+ *		malloc().  It contains zero or more allocations, which are the
+ *		units requested by palloc().
+ */
+struct BumpBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+#ifdef MEMORY_CONTEXT_CHECKING
+	BumpContext *context;		/* pointer back to the owning context */
+#endif
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+/*
+ * BumpIsValid
+ *		True iff set is valid bump context.
+ */
+#define BumpIsValid(set) \
+	(PointerIsValid(set) && IsA(set, BumpContext))
+
+/*
+ * BumpBlockIsValid
+ *		True iff block is valid block of a bump context
+ */
+#define BumpBlockIsValid(block) \
+	(PointerIsValid(block) && BumpIsValid((block)->context))
+
+/*
+ * We always store external chunks on a dedicated block.  This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+	(BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
+
+/* Inlined helper functions */
+static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
+								 Size blksize);
+static inline bool BumpBlockIsEmpty(BumpBlock *block);
+static inline void BumpBlockMarkEmpty(BumpBlock *block);
+static inline Size BumpBlockFreeBytes(BumpBlock *block);
+static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
+
+
+/*
+* BumpContextCreate
+*		Create a new Bump context.
+*
+* parent: parent context, or NULL if top-level context
+* name: name of context (must be statically allocated)
+* minContextSize: minimum context size
+* initBlockSize: initial allocation block size
+* maxBlockSize: maximum allocation block size
+*/
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+				  const char *name,
+				  uint32 minContextSize,
+				  uint32 initBlockSize,
+				  uint32 maxBlockSize)
+{
+	Size		firstBlockSize;
+	Size		allocSize;
+	BumpContext *set;
+	BumpBlock  *block;
+
+	/* ensure MemoryChunk's size is properly maxaligned */
+	StaticAssertDecl(Bump_CHUNKHDRSZ == MAXALIGN(Bump_CHUNKHDRSZ),
+					 "sizeof(MemoryChunk) is not maxaligned");
+
+	/*
+	 * First, validate allocation parameters.  Asserts seem sufficient because
+	 * nobody varies their parameters at runtime.  We somewhat arbitrarily
+	 * enforce a minimum 1K block size.  We restrict the maximum block size to
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+	 * regards to addressing the offset between the chunk and the block that
+	 * the chunk is stored on.  We would be unable to store the offset between
+	 * the chunk and block for any chunks that were beyond
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+	 * larger than this.
+	 */
+	Assert(initBlockSize == MAXALIGN(initBlockSize) &&
+		   initBlockSize >= 1024);
+	Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
+		   maxBlockSize >= initBlockSize &&
+		   AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
+	Assert(minContextSize == 0 ||
+		   (minContextSize == MAXALIGN(minContextSize) &&
+			minContextSize >= 1024 &&
+			minContextSize <= maxBlockSize));
+	Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+
+	/* Determine size of initial block */
+	allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+		Bump_CHUNKHDRSZ;
+	if (minContextSize != 0)
+		allocSize = Max(allocSize, minContextSize);
+	else
+		allocSize = Max(allocSize, initBlockSize);
+
+	/*
+	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
+	 * the context header and its block header follows that.
+	 */
+	set = (BumpContext *) malloc(allocSize);
+	if (set == NULL)
+	{
+		MemoryContextStats(TopMemoryContext);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("Failed while creating memory context \"%s\".",
+						   name)));
+	}
+
+	/*
+	 * Avoid writing code that can fail between here and MemoryContextCreate;
+	 * we'd leak the header if we ereport in this stretch.
+	 */
+	dlist_init(&set->blocks);
+
+	/* Fill in the initial block's block header */
+	block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
+	/* determine the block size and initialize it */
+	firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
+	BumpBlockInit(set, block, firstBlockSize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	/* use it as the current allocation block */
+	set->block = block;
+
+	/* Mark block as not to be released at reset time */
+	set->keeper = block;
+
+	/* Fill in BumpContext-specific header fields */
+	set->initBlockSize = initBlockSize;
+	set->maxBlockSize = maxBlockSize;
+	set->nextBlockSize = initBlockSize;
+
+	/*
+	 * Compute the allocation chunk size limit for this context.
+	 *
+	 * Limit the maximum size a non-dedicated chunk can be so that we can fit
+	 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
+	 * block.  We must further limit this value so that it's no more than
+	 * MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks larger
+	 * than that value as we store the chunk size in the MemoryChunk 'value'
+	 * field in the call to MemoryChunkSetHdrMask().
+	 */
+	set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
+	while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
+		   (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
+		set->allocChunkLimit >>= 1;
+
+	/* Finally, do the type-independent part of context creation */
+	MemoryContextCreate((MemoryContext) set,
+						T_BumpContext,
+						MCTX_BUMP_ID,
+						parent,
+						name);
+
+	((MemoryContext) set)->mem_allocated = firstBlockSize;
+
+	return (MemoryContext) set;
+}
+
+/*
+* BumpReset
+*		Frees all memory which is allocated in the given set.
+*
+* The code simply frees all the blocks in the context apart from the keeper
+* block.
+*/
+void
+BumpReset(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_mutable_iter miter;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	BumpCheck(context);
+#endif
+
+	dlist_foreach_modify(miter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, miter.cur);
+
+		if (block == set->keeper)
+			BumpBlockMarkEmpty(block);
+		else
+			BumpBlockFree(set, block);
+	}
+
+	/* set it so new allocations to make use of the keeper block */
+	set->block = set->keeper;
+
+	/* Reset block size allocation sequence, too */
+	set->nextBlockSize = set->initBlockSize;
+
+	/* Ensure there is only 1 item in the dlist */
+	Assert(!dlist_is_empty(&set->blocks));
+	Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
+}
+
+/*
+ * BumpDelete
+ *		Free all memory which is allocated in the given context.
+ */
+void
+BumpDelete(MemoryContext context)
+{
+	/* Reset to release all releasable BumpBlocks */
+	BumpReset(context);
+	/* And free the context header and keeper block */
+	free(context);
+}
+
+/*
+ * BumpAlloc
+ *		Returns pointer to allocated memory of the given size or NULL if
+ *		the request could not be completed; memory is added to the set.
+ *
+ * No request may exceed:
+ *		MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
+ * All callers use a much-lower limit.
+ *
+ * Note: when using valgrind, it doesn't matter how the returned allocation
+ * is marked, as mcxt.c will set it to UNDEFINED.
+ */
+void *
+BumpAlloc(MemoryContext context, Size size)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#else
+	void	   *ptr;
+#endif
+	Size		chunk_size;
+	Size		required_size;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+
+	/* is it an over-sized chunk? if yes, allocate special block */
+	if (chunk_size > set->allocChunkLimit)
+	{
+		Size		blksize = required_size + Bump_BLOCKHDRSZ;
+
+		block = (BumpBlock *) malloc(blksize);
+		if (block == NULL)
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		/* the block is completely full */
+		block->freeptr = block->endptr = ((char *) block) + blksize;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+		/* block with a single (used) chunk */
+		block->context = set;
+
+		chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
+
+		/* mark the MemoryChunk as externally managed */
+		MemoryChunkSetHdrMaskExternal(chunk, MCTX_BUMP_ID);
+
+		chunk->requested_size = size;
+		/* set mark to catch clobber of "unused" space */
+		Assert(size < chunk_size);
+		set_sentinel(MemoryChunkGetPointer(chunk), size);
+#endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+		/* fill the allocated space with junk */
+		randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+		/* add the block to the list of allocated blocks */
+		dlist_push_head(&set->blocks, &block->node);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+		/* Ensure any padding bytes are marked NOACCESS. */
+		VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+								   chunk_size - size);
+
+		/* Disallow access to the chunk header. */
+		VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+		return MemoryChunkGetPointer(chunk);
+#else
+		return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
+#endif
+
+	}
+
+	/*
+	 * Not an oversized chunk.  We try to first make use of the current block,
+	 * but if there's not enough space in it we must allocate a new block.
+	 */
+	block = set->block;
+
+	if (BumpBlockFreeBytes(block) < required_size)
+	{
+		Size		blksize;
+
+		/*
+		 * The first such block has size initBlockSize, and we double the
+		 * space in each succeeding block, but not more than maxBlockSize.
+		 */
+		blksize = set->nextBlockSize;
+		set->nextBlockSize <<= 1;
+		if (set->nextBlockSize > set->maxBlockSize)
+			set->nextBlockSize = set->maxBlockSize;
+
+		/* we'll need a block hdr too, so add that to the required size */
+		required_size += Bump_BLOCKHDRSZ;
+
+		/* round the size up to the next power of 2 */
+		if (blksize < required_size)
+			blksize = pg_nextpower2_size_t(required_size);
+
+		block = (BumpBlock *) malloc(blksize);
+
+		if (block == NULL)
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		/* initialize the new block */
+		BumpBlockInit(set, block, blksize);
+
+		/* add it to the doubly-linked list of blocks */
+		dlist_push_head(&set->blocks, &block->node);
+
+		/* and also use it as the current allocation block */
+		set->block = block;
+	}
+
+	/* we're supposed to have a block with enough free space now */
+	Assert(block != NULL);
+	Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	chunk = (MemoryChunk *) block->freeptr;
+#else
+	ptr = (void *) block->freeptr;
+#endif
+
+	/* point the freeptr beyond this chunk */
+	block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
+	Assert(block->freeptr <= block->endptr);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+	/* Prepare to initialize the chunk header. */
+	VALGRIND_MAKE_MEM_UNDEFINED(chunk, Bump_CHUNKHDRSZ);
+
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return ptr;
+#endif							/* MEMORY_CONTEXT_CHECKING */
+}
+
+/*
+ * BumpBlockInit
+ *		Initializes 'block' assuming 'blksize'.  Does not update the context's
+ *		mem_allocated field.
+ */
+static inline void
+BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	block->context = context;
+#endif
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+	block->endptr = ((char *) block) + blksize;
+
+	/* Mark unallocated space NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockIsEmpty
+ *		Returns true iff 'block' contains no chunks
+ */
+static inline bool
+BumpBlockIsEmpty(BumpBlock *block)
+{
+	/* it's empty if the freeptr has not moved */
+	return (block->freeptr == (char *) block + Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockMarkEmpty
+ *		Set a block as empty.  Does not free the block.
+ */
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+	char	   *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
+#endif
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(datastart, block->freeptr - datastart);
+#else
+	/* wipe_mem() would have done this */
+	VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
+#endif
+
+	/* Reset the block, but don't return it to malloc */
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+}
+
+/*
+ * BumpBlockFreeBytes
+ *		Returns the number of bytes free in 'block'
+ */
+static inline Size
+BumpBlockFreeBytes(BumpBlock *block)
+{
+	return (block->endptr - block->freeptr);
+}
+
+/*
+ * BumpBlockFree
+ *		Remove 'block' from 'set' and release the memory consumed by it.
+ */
+static inline void
+BumpBlockFree(BumpContext *set, BumpBlock *block)
+{
+	/* Make sure nobody tries to free the keeper block */
+	Assert(block != set->keeper);
+
+	/* release the block from the list of blocks */
+	dlist_delete(&block->node);
+
+	((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(block, ((char *) block->endptr - (char *) block));
+#endif
+
+	free(block);
+}
+
+/*
+ * BumpFree
+ *		Unsupported.
+ */
+void
+BumpFree(void *pointer)
+{
+	elog(ERROR, "pfree is not supported by the bump memory allocator");
+}
+
+/*
+ * BumpRealloc
+ *		Unsupported.
+ */
+void *
+BumpRealloc(void *pointer, Size size)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * BumpGetChunkContext
+ *		Unsupported.
+ */
+MemoryContext
+BumpGetChunkContext(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+* BumpGetChunkSpace
+*		Given a currently-allocated chunk, determine the total space
+*		it occupies (including all memory-allocation overhead).
+*/
+Size
+BumpGetChunkSpace(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
+	return 0;					/* keep compiler quiet */
+}
+
+/*
+ * BumpIsEmpty
+ *		Is a BumpContext empty of any allocated space?
+ */
+bool
+BumpIsEmpty(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		if (!BumpBlockIsEmpty(block))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * BumpStats
+ *		Compute stats about memory consumption of a Bump context.
+ *
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ * print_to_stderr: print stats to stderr if true, elog otherwise.
+ */
+void
+BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+		  void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
+{
+	BumpContext *set = (BumpContext *) context;
+	Size		nblocks = 0;
+	Size		totalspace = 0;
+	Size		freespace = 0;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		nblocks++;
+		totalspace += (block->endptr - (char *) block);
+		freespace += (block->endptr - block->freeptr);
+	}
+
+	if (printfunc)
+	{
+		char		stats_string[200];
+
+		snprintf(stats_string, sizeof(stats_string),
+				 "%zu total in %zu blocks; %zu free; %zu used",
+				 totalspace, nblocks, freespace, totalspace - freespace);
+		printfunc(context, passthru, stats_string, print_to_stderr);
+	}
+
+	if (totals)
+	{
+		totals->nblocks += nblocks;
+		totals->totalspace += totalspace;
+		totals->freespace += freespace;
+	}
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * BumpCheck
+ *		Walk through chunks and check consistency of memory.
+ *
+ * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+ * find yourself in an infinite loop when trouble occurs, because this
+ * routine will be entered again when elog cleanup tries to release memory!
+ */
+void
+BumpCheck(MemoryContext context)
+{
+	BumpContext *bump = (BumpContext *) context;
+	const char *name = context->name;
+	dlist_iter	iter;
+	Size		total_allocated = 0;
+
+	/* walk all blocks in this context */
+	dlist_foreach(iter, &bump->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+		int			nchunks;
+		char	   *ptr;
+		bool		has_external_chunk = false;
+
+		total_allocated += block->endptr - (char *) block;
+
+		/* check block belongs to the correct context */
+		if (block->context != bump)
+			elog(WARNING, "problem in Bump %s: bogus context link in block %p",
+				 name, block);
+
+		/* now walk through the chunks and count them */
+		nchunks = 0;
+		ptr = ((char *) block) + Bump_BLOCKHDRSZ;
+
+		while (ptr < block->freeptr)
+		{
+			MemoryChunk *chunk = (MemoryChunk *) ptr;
+			BumpBlock  *chunkblock;
+			Size		chunksize;
+
+			/* allow access to the chunk header */
+			VALGRIND_MAKE_MEM_DEFINED(chunk, Bump_CHUNKHDRSZ);
+
+			if (MemoryChunkIsExternal(chunk))
+			{
+				chunkblock = ExternalChunkGetBlock(chunk);
+				chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+				has_external_chunk = true;
+			}
+			else
+			{
+				chunkblock = MemoryChunkGetBlock(chunk);
+				chunksize = MemoryChunkGetValue(chunk);
+			}
+
+			/* move to the next chunk */
+			ptr += (chunksize + Bump_CHUNKHDRSZ);
+
+			nchunks += 1;
+
+			/* chunks have both block and context pointers, so check both */
+			if (chunkblock != block)
+				elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
+					 name, block, chunk);
+		}
+
+		if (has_external_chunk && nchunks > 1)
+			elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
+				 name, block);
+
+	}
+
+	Assert(total_allocated == context->mem_allocated);
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 9fc83f11f6..0eab919236 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -43,6 +43,20 @@ static Size BogusGetChunkSpace(void *pointer);
  *****************************************************************************/
 
 static const MemoryContextMethods mcxt_methods[] = {
+	/* bump.c */
+	[MCTX_BUMP_ID].alloc = BumpAlloc,
+	[MCTX_BUMP_ID].free_p = BumpFree,
+	[MCTX_BUMP_ID].realloc = BumpRealloc,
+	[MCTX_BUMP_ID].reset = BumpReset,
+	[MCTX_BUMP_ID].delete_context = BumpDelete,
+	[MCTX_BUMP_ID].get_chunk_context = BumpGetChunkContext,
+	[MCTX_BUMP_ID].get_chunk_space = BumpGetChunkSpace,
+	[MCTX_BUMP_ID].is_empty = BumpIsEmpty,
+	[MCTX_BUMP_ID].stats = BumpStats,
+#ifdef MEMORY_CONTEXT_CHECKING
+	[MCTX_BUMP_ID].check = BumpCheck,
+#endif
+
 	/* aset.c */
 	[MCTX_ASET_ID].alloc = AllocSetAlloc,
 	[MCTX_ASET_ID].free_p = AllocSetFree,
@@ -121,11 +135,6 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
 	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
 	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
 };
 
 /*
diff --git a/src/backend/utils/mmgr/meson.build b/src/backend/utils/mmgr/meson.build
index e6ebe145ea..2d68c97a34 100644
--- a/src/backend/utils/mmgr/meson.build
+++ b/src/backend/utils/mmgr/meson.build
@@ -3,6 +3,7 @@
 backend_sources += files(
   'alignedalloc.c',
   'aset.c',
+  'bump.c',
   'dsa.c',
   'freepage.c',
   'generation.c',
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index e5a4e5b371..b43d03d870 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -194,6 +194,11 @@ struct Tuplesortstate
 								 * tuples to return? */
 	bool		boundUsed;		/* true if we made use of a bounded heap */
 	int			bound;			/* if bounded, the maximum number of tuples */
+	int64		tupleMem;		/* memory consumed by individual tuples.
+								 * storing this separately from what we track
+								 * in availMem allows us to subtract the
+								 * memory consumed by all tuples when dumping
+								 * tuples to tape */
 	int64		availMem;		/* remaining memory available, in bytes */
 	int64		allowedMem;		/* total memory allowed, in bytes */
 	int			maxTapes;		/* max number of input tapes to merge in each
@@ -770,18 +775,18 @@ tuplesort_begin_batch(Tuplesortstate *state)
 	 * in the parent context, not this context, because there is no need to
 	 * free memtuples early.  For bounded sorts, tuples may be pfreed in any
 	 * order, so we use a regular aset.c context so that it can make use of
-	 * free'd memory.  When the sort is not bounded, we make use of a
-	 * generation.c context as this keeps allocations more compact with less
-	 * wastage.  Allocations are also slightly more CPU efficient.
+	 * free'd memory.  When the sort is not bounded, we make use of a bump.c
+	 * context as this keeps allocations more compact with less wastage.
+	 * Allocations are also slightly more CPU efficient.
 	 */
 	if (state->base.sortopt & TUPLESORT_ALLOWBOUNDED)
 		state->base.tuplecontext = AllocSetContextCreate(state->base.sortcontext,
 														 "Caller tuples",
 														 ALLOCSET_DEFAULT_SIZES);
 	else
-		state->base.tuplecontext = GenerationContextCreate(state->base.sortcontext,
-														   "Caller tuples",
-														   ALLOCSET_DEFAULT_SIZES);
+		state->base.tuplecontext = BumpContextCreate(state->base.sortcontext,
+													 "Caller tuples",
+													 ALLOCSET_DEFAULT_SIZES);
 
 
 	state->status = TSS_INITIAL;
@@ -1187,15 +1192,16 @@ noalloc:
  * Shared code for tuple and datum cases.
  */
 void
-tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev)
+tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple,
+						  bool useAbbrev, Size tuplen)
 {
 	MemoryContext oldcontext = MemoryContextSwitchTo(state->base.sortcontext);
 
 	Assert(!LEADER(state));
 
-	/* Count the size of the out-of-line data */
-	if (tuple->tuple != NULL)
-		USEMEM(state, GetMemoryChunkSpace(tuple->tuple));
+	/* account for the memory used for this tuple */
+	USEMEM(state, tuplen);
+	state->tupleMem += tuplen;
 
 	if (!useAbbrev)
 	{
@@ -2403,13 +2409,6 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 		SortTuple  *stup = &state->memtuples[i];
 
 		WRITETUP(state, state->destTape, stup);
-
-		/*
-		 * Account for freeing the tuple, but no need to do the actual pfree
-		 * since the tuplecontext is being reset after the loop.
-		 */
-		if (stup->tuple != NULL)
-			FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	}
 
 	state->memtupcount = 0;
@@ -2417,12 +2416,19 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 	/*
 	 * Reset tuple memory.  We've freed all of the tuples that we previously
 	 * allocated.  It's important to avoid fragmentation when there is a stark
-	 * change in the sizes of incoming tuples.  Fragmentation due to
-	 * AllocSetFree's bucketing by size class might be particularly bad if
-	 * this step wasn't taken.
+	 * change in the sizes of incoming tuples.  In bounded sorts,
+	 * fragmentation due to AllocSetFree's bucketing by size class might be
+	 * particularly bad if this step wasn't taken.
 	 */
 	MemoryContextReset(state->base.tuplecontext);
 
+	/*
+	 * Now update the memory accounting to subtract the memory used by the
+	 * tuple.
+	 */
+	FREEMEM(state, state->tupleMem);
+	state->tupleMem = 0;
+
 	markrunend(state->destTape);
 
 #ifdef TRACE_SORT
diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c
index eb6cfcfd00..c1de6a954e 100644
--- a/src/backend/utils/sort/tuplesortvariants.c
+++ b/src/backend/utils/sort/tuplesortvariants.c
@@ -605,6 +605,7 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 	SortTuple	stup;
 	MinimalTuple tuple;
 	HeapTupleData htup;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tuple = ExecCopySlotMinimalTuple(slot);
@@ -617,9 +618,14 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 							   tupDesc,
 							   &stup.isnull1);
 
+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tuple);
+	else
+		tuplen = MAXALIGN(tuple->t_len);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -636,6 +642,7 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 	TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tup = heap_copytuple(tup);
@@ -653,10 +660,15 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 								   &stup.isnull1);
 	}
 
+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tup);
+	else
+		tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->haveDatum1 &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -674,6 +686,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 	IndexTuple	tuple;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
+	Size		tuplen;
 
 	stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
 										  isnull, base->tuplecontext);
@@ -685,10 +698,15 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 								RelationGetDescr(arg->indexRel),
 								&stup.isnull1);
 
+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tuple);
+	else
+		tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 }
 
 /*
@@ -735,7 +753,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
 
 	tuplesort_puttuple_common(state, &stup,
 							  base->tuples &&
-							  base->sortKeys->abbrev_converter && !isNull);
+							  base->sortKeys->abbrev_converter && !isNull, 0);
 
 	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index ff6453bb7a..893237d331 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -108,6 +108,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), BumpContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 21640d62a6..c89778ac5d 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -108,6 +108,13 @@ extern void ProcessLogMemoryContextInterrupt(void);
  * Memory-context-type-specific functions
  */
 
+/* bump.c */
+extern MemoryContext BumpContextCreate(MemoryContext parent,
+									   const char *name,
+									   uint32 minContextSize,
+									   uint32 initBlockSize,
+									   uint32 maxBlockSize);
+
 /* aset.c */
 extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
 												   const char *name,
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index 2d107bbf9d..c851fd66fa 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -18,6 +18,23 @@
 
 #include "utils/memutils.h"
 
+ /* These functions implement the MemoryContext API for the Bump context. */
+extern void *BumpAlloc(MemoryContext context, Size size);
+extern void BumpFree(void *pointer);
+extern void *BumpRealloc(void *pointer, Size size);
+extern void BumpReset(MemoryContext context);
+extern void BumpDelete(MemoryContext context);
+extern MemoryContext BumpGetChunkContext(void *pointer);
+extern Size BumpGetChunkSpace(void *pointer);
+extern bool BumpIsEmpty(MemoryContext context);
+extern void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+					  void *passthru, MemoryContextCounters *totals,
+					  bool print_to_stderr);
+#ifdef MEMORY_CONTEXT_CHECKING
+extern void BumpCheck(MemoryContext context);
+#endif
+
+
 /* These functions implement the MemoryContext API for AllocSet context. */
 extern void *AllocSetAlloc(MemoryContext context, Size size);
 extern void AllocSetFree(void *pointer);
@@ -106,12 +123,13 @@ typedef enum MemoryContextMethodID
 {
 	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
 	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_BUMP_ID,				/* glibc malloc'd chunks > 128kB match 010
+								 * XXX? */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID				/* 111 occurs in wipe_mem'd memory */
+	MCTX_UNUSED3_ID				/* 111 occurs in wipe_mem'd memory */
 } MemoryContextMethodID;
 
 /*
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index af057b6358..06db9b003e 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -109,7 +109,8 @@ typedef struct TuplesortInstrumentation
  * a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
  * which is a separate palloc chunk --- we assume it is just one chunk and
  * can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
+ * simple slab allocator and when performing a non-bounded sort where we
+ * use a bump allocator).  SortTuples also contain the tuple's first key
  * column in Datum/nullflag format, and a source/input tape number that
  * tracks which tape each heap element/slot belongs to during merging.
  *
@@ -356,7 +357,8 @@ extern Tuplesortstate *tuplesort_begin_common(int workMem,
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
 extern bool tuplesort_used_bound(Tuplesortstate *state);
 extern void tuplesort_puttuple_common(Tuplesortstate *state,
-									  SortTuple *tuple, bool useAbbrev);
+									  SortTuple *tuple, bool useAbbrev,
+									  Size tuplen);
 extern void tuplesort_performsort(Tuplesortstate *state);
 extern bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 									  SortTuple *stup);
sortbenchall.sh.txttext/plain; charset=US-ASCII; name=sortbenchall.sh.txtDownload
#2David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#1)
1 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 27 Jun 2023 at 21:19, David Rowley <dgrowleyml@gmail.com> wrote:

I've attached the bump allocator patch and also the script I used to
gather the performance results in the first 2 tabs in the attached
spreadsheet.

I've attached a v2 patch which changes the BumpContext a little to
remove some of the fields that are not really required. There was no
need for the "keeper" field as the keeper block always comes at the
end of the BumpContext as these are allocated in a single malloc().
The pointer to the "block" also isn't really needed. This is always
the same as the head element in the blocks dlist.

David

Attachments:

bump_allocator_v2.patchtext/plain; charset=US-ASCII; name=bump_allocator_v2.patchDownload
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 72c7963578..a603f1d549 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -149,7 +149,7 @@ my @abstract_types = qw(Node);
 # they otherwise don't participate in node support.
 my @extra_tags = qw(
   IntList OidList XidList
-  AllocSetContext GenerationContext SlabContext
+  AllocSetContext GenerationContext SlabContext BumpContext
   TIDBitmap
   WindowObjectData
 );
diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index dae3432c98..01a1fb8527 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	alignedalloc.o \
 	aset.o \
+	bump.o \
 	dsa.o \
 	freepage.o \
 	generation.o \
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
new file mode 100644
index 0000000000..015e66709f
--- /dev/null
+++ b/src/backend/utils/mmgr/bump.c
@@ -0,0 +1,747 @@
+/*-------------------------------------------------------------------------
+ *
+ * bump.c
+ *	  Bump allocator definitions.
+ *
+ * Bump is a MemoryContext implementation designed for memory usages which
+ * require allocating a large number of chunks, none of which ever need to be
+ * pfree'd or realloc'd.  Chunks allocated by this context have no chunk header
+ * and operations which ordinarily require looking at the chunk header cannot
+ * be performed.  For example, pfree, realloc, GetMemoryChunkSpace and
+ * GetMemoryChunkContext are all not possible with bump allocated chunks.  The
+ * only way to release memory allocated by this context type is to reset or
+ * delete the context.
+ *
+ * Portions Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/bump.c
+ *
+ *
+ *	Bump is best suited to cases which require a large number of short-lived
+ *	chunks where performance matters.  Because bump allocated chunks don't
+ *	have a chunk header, it can fit more chunks on each block.  This means we
+ *	can do more with less memory and fewer cache lines.  The reason it's best
+ *	suited for short-lived usages of memory is that ideally, pointers to bump
+ *	allocated chunks won't be visible to a large amount of code.  The more
+ *	code that operates on memory allocated by this allocator, the more chances
+ *	that some code will try to perform a pfree or one of the other operations
+ *	which are made impossible due to the lack of chunk header.  In order to
+ *	to detect accidental usage of the various disallowed operations, we do add
+ *	a MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
+ *	various disallowed functions raise an ERROR.
+ *
+ *	Allocations are MAXALIGNed.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "port/pg_bitutils.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
+
+#define Bump_BLOCKHDRSZ	MAXALIGN(sizeof(BumpBlock))
+
+/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
+#ifdef MEMORY_CONTEXT_CHECKING
+#define Bump_CHUNKHDRSZ	sizeof(MemoryChunk)
+#else
+#define Bump_CHUNKHDRSZ	0
+#endif
+
+#define Bump_CHUNK_FRACTION	8
+
+/* The keeper block is allocated in the same allocation as the set */
+#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
+#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))
+
+typedef struct BumpBlock BumpBlock; /* forward reference */
+
+typedef struct BumpContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* Bump context parameters */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
+
+	dlist_head	blocks;			/* list of blocks with the block currently
+								 * being filled at the head */
+} BumpContext;
+
+/*
+ * BumpBlock
+ *		BumpBlock is the unit of memory that is obtained by bump.c from
+ *		malloc().  It contains zero or more allocations, which are the
+ *		units requested by palloc().
+ */
+struct BumpBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+#ifdef MEMORY_CONTEXT_CHECKING
+	BumpContext *context;		/* pointer back to the owning context */
+#endif
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+/*
+ * BumpIsValid
+ *		True iff set is valid bump context.
+ */
+#define BumpIsValid(set) \
+	(PointerIsValid(set) && IsA(set, BumpContext))
+
+/*
+ * BumpBlockIsValid
+ *		True iff block is valid block of a bump context
+ */
+#define BumpBlockIsValid(block) \
+	(PointerIsValid(block) && BumpIsValid((block)->context))
+
+/*
+ * We always store external chunks on a dedicated block.  This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+	(BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
+
+/* Inlined helper functions */
+static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
+								 Size blksize);
+static inline bool BumpBlockIsEmpty(BumpBlock *block);
+static inline void BumpBlockMarkEmpty(BumpBlock *block);
+static inline Size BumpBlockFreeBytes(BumpBlock *block);
+static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
+
+
+/*
+* BumpContextCreate
+*		Create a new Bump context.
+*
+* parent: parent context, or NULL if top-level context
+* name: name of context (must be statically allocated)
+* minContextSize: minimum context size
+* initBlockSize: initial allocation block size
+* maxBlockSize: maximum allocation block size
+*/
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+				  const char *name,
+				  Size minContextSize,
+				  Size initBlockSize,
+				  Size maxBlockSize)
+{
+	Size		firstBlockSize;
+	Size		allocSize;
+	BumpContext *set;
+	BumpBlock  *block;
+
+	/* ensure MemoryChunk's size is properly maxaligned */
+	StaticAssertDecl(Bump_CHUNKHDRSZ == MAXALIGN(Bump_CHUNKHDRSZ),
+					 "sizeof(MemoryChunk) is not maxaligned");
+
+	/*
+	 * First, validate allocation parameters.  Asserts seem sufficient because
+	 * nobody varies their parameters at runtime.  We somewhat arbitrarily
+	 * enforce a minimum 1K block size.  We restrict the maximum block size to
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+	 * regards to addressing the offset between the chunk and the block that
+	 * the chunk is stored on.  We would be unable to store the offset between
+	 * the chunk and block for any chunks that were beyond
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+	 * larger than this.
+	 */
+	Assert(initBlockSize == MAXALIGN(initBlockSize) &&
+		   initBlockSize >= 1024);
+	Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
+		   maxBlockSize >= initBlockSize &&
+		   AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
+	Assert(minContextSize == 0 ||
+		   (minContextSize == MAXALIGN(minContextSize) &&
+			minContextSize >= 1024 &&
+			minContextSize <= maxBlockSize));
+	Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+
+	/* Determine size of initial block */
+	allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+		Bump_CHUNKHDRSZ;
+	if (minContextSize != 0)
+		allocSize = Max(allocSize, minContextSize);
+	else
+		allocSize = Max(allocSize, initBlockSize);
+
+	/*
+	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
+	 * the context header and its block header follows that.
+	 */
+	set = (BumpContext *) malloc(allocSize);
+	if (set == NULL)
+	{
+		MemoryContextStats(TopMemoryContext);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("Failed while creating memory context \"%s\".",
+						   name)));
+	}
+
+	/*
+	 * Avoid writing code that can fail between here and MemoryContextCreate;
+	 * we'd leak the header if we ereport in this stretch.
+	 */
+	dlist_init(&set->blocks);
+
+	/* Fill in the initial block's block header */
+	block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
+	/* determine the block size and initialize it */
+	firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
+	BumpBlockInit(set, block, firstBlockSize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	/*
+	 * Fill in BumpContext-specific header fields.  The Asserts above should
+	 * ensure that these all fit inside a uint32.
+	 */
+	set->initBlockSize = (uint32) initBlockSize;
+	set->maxBlockSize = (uint32) maxBlockSize;
+	set->nextBlockSize = (uint32) initBlockSize;
+
+	/*
+	 * Compute the allocation chunk size limit for this context.
+	 *
+	 * Limit the maximum size a non-dedicated chunk can be so that we can fit
+	 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
+	 * block.  We must further limit this value so that it's no more than
+	 * MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks larger
+	 * than that value as we store the chunk size in the MemoryChunk 'value'
+	 * field in the call to MemoryChunkSetHdrMask().
+	 */
+	set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
+	while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
+		   (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
+		set->allocChunkLimit >>= 1;
+
+	/* Finally, do the type-independent part of context creation */
+	MemoryContextCreate((MemoryContext) set,
+						T_BumpContext,
+						MCTX_BUMP_ID,
+						parent,
+						name);
+
+	((MemoryContext) set)->mem_allocated = allocSize;
+
+	return (MemoryContext) set;
+}
+
+/*
+* BumpReset
+*		Frees all memory which is allocated in the given set.
+*
+* The code simply frees all the blocks in the context apart from the keeper
+* block.
+*/
+void
+BumpReset(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_mutable_iter miter;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	BumpCheck(context);
+#endif
+
+	dlist_foreach_modify(miter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, miter.cur);
+
+		if (IsKeeperBlock(set, block))
+			BumpBlockMarkEmpty(block);
+		else
+			BumpBlockFree(set, block);
+	}
+
+	/* Reset block size allocation sequence, too */
+	set->nextBlockSize = set->initBlockSize;
+
+	/* Ensure there is only 1 item in the dlist */
+	Assert(!dlist_is_empty(&set->blocks));
+	Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
+}
+
+/*
+ * BumpDelete
+ *		Free all memory which is allocated in the given context.
+ */
+void
+BumpDelete(MemoryContext context)
+{
+	/* Reset to release all releasable BumpBlocks */
+	BumpReset(context);
+	/* And free the context header and keeper block */
+	free(context);
+}
+
+/*
+ * BumpAlloc
+ *		Returns pointer to allocated memory of the given size or NULL if
+ *		the request could not be completed; memory is added to the set.
+ *
+ * No request may exceed:
+ *		MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
+ * All callers use a much-lower limit.
+ *
+ * Note: when using valgrind, it doesn't matter how the returned allocation
+ * is marked, as mcxt.c will set it to UNDEFINED.
+ */
+void *
+BumpAlloc(MemoryContext context, Size size)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#else
+	void	   *ptr;
+#endif
+	Size		chunk_size;
+	Size		required_size;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+
+	/* is it an over-sized chunk? if yes, allocate special block */
+	if (chunk_size > set->allocChunkLimit)
+	{
+		Size		blksize = required_size + Bump_BLOCKHDRSZ;
+
+		block = (BumpBlock *) malloc(blksize);
+		if (block == NULL)
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		/* the block is completely full */
+		block->freeptr = block->endptr = ((char *) block) + blksize;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+		/* block with a single (used) chunk */
+		block->context = set;
+
+		chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
+
+		/* mark the MemoryChunk as externally managed */
+		MemoryChunkSetHdrMaskExternal(chunk, MCTX_BUMP_ID);
+
+		chunk->requested_size = size;
+		/* set mark to catch clobber of "unused" space */
+		Assert(size < chunk_size);
+		set_sentinel(MemoryChunkGetPointer(chunk), size);
+#endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+		/* fill the allocated space with junk */
+		randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+		/* add the block to the list of allocated blocks */
+		dlist_push_head(&set->blocks, &block->node);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+		/* Ensure any padding bytes are marked NOACCESS. */
+		VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+								   chunk_size - size);
+
+		/* Disallow access to the chunk header. */
+		VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+		return MemoryChunkGetPointer(chunk);
+#else
+		return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
+#endif
+
+	}
+
+	/*
+	 * Not an oversized chunk.  We try to first make use of the latest block,
+	 * but if there's not enough space in it we must allocate a new block.
+	 */
+	block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
+
+	if (BumpBlockFreeBytes(block) < required_size)
+	{
+		Size		blksize;
+
+		/*
+		 * The first such block has size initBlockSize, and we double the
+		 * space in each succeeding block, but not more than maxBlockSize.
+		 */
+		blksize = set->nextBlockSize;
+		set->nextBlockSize <<= 1;
+		if (set->nextBlockSize > set->maxBlockSize)
+			set->nextBlockSize = set->maxBlockSize;
+
+		/* we'll need a block hdr too, so add that to the required size */
+		required_size += Bump_BLOCKHDRSZ;
+
+		/* round the size up to the next power of 2 */
+		if (blksize < required_size)
+			blksize = pg_nextpower2_size_t(required_size);
+
+		block = (BumpBlock *) malloc(blksize);
+
+		if (block == NULL)
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		/* initialize the new block */
+		BumpBlockInit(set, block, blksize);
+
+		/* add it to the doubly-linked list of blocks */
+		dlist_push_head(&set->blocks, &block->node);
+	}
+
+	/* we're supposed to have a block with enough free space now */
+	Assert(block != NULL);
+	Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	chunk = (MemoryChunk *) block->freeptr;
+#else
+	ptr = (void *) block->freeptr;
+#endif
+
+	/* point the freeptr beyond this chunk */
+	block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
+	Assert(block->freeptr <= block->endptr);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+	/* Prepare to initialize the chunk header. */
+	VALGRIND_MAKE_MEM_UNDEFINED(chunk, Bump_CHUNKHDRSZ);
+
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return ptr;
+#endif							/* MEMORY_CONTEXT_CHECKING */
+}
+
+/*
+ * BumpBlockInit
+ *		Initializes 'block' assuming 'blksize'.  Does not update the context's
+ *		mem_allocated field.
+ */
+static inline void
+BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	block->context = context;
+#endif
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+	block->endptr = ((char *) block) + blksize;
+
+	/* Mark unallocated space NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockIsEmpty
+ *		Returns true iff 'block' contains no chunks
+ */
+static inline bool
+BumpBlockIsEmpty(BumpBlock *block)
+{
+	/* it's empty if the freeptr has not moved */
+	return (block->freeptr == (char *) block + Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockMarkEmpty
+ *		Set a block as empty.  Does not free the block.
+ */
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+	char	   *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
+#endif
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(datastart, block->freeptr - datastart);
+#else
+	/* wipe_mem() would have done this */
+	VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
+#endif
+
+	/* Reset the block, but don't return it to malloc */
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+}
+
+/*
+ * BumpBlockFreeBytes
+ *		Returns the number of bytes free in 'block'
+ */
+static inline Size
+BumpBlockFreeBytes(BumpBlock *block)
+{
+	return (block->endptr - block->freeptr);
+}
+
+/*
+ * BumpBlockFree
+ *		Remove 'block' from 'set' and release the memory consumed by it.
+ */
+static inline void
+BumpBlockFree(BumpContext *set, BumpBlock *block)
+{
+	/* Make sure nobody tries to free the keeper block */
+	Assert(!IsKeeperBlock(set, block));
+
+	/* release the block from the list of blocks */
+	dlist_delete(&block->node);
+
+	((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(block, ((char *) block->endptr - (char *) block));
+#endif
+
+	free(block);
+}
+
+/*
+ * BumpFree
+ *		Unsupported.
+ */
+void
+BumpFree(void *pointer)
+{
+	elog(ERROR, "pfree is not supported by the bump memory allocator");
+}
+
+/*
+ * BumpRealloc
+ *		Unsupported.
+ */
+void *
+BumpRealloc(void *pointer, Size size)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * BumpGetChunkContext
+ *		Unsupported.
+ */
+MemoryContext
+BumpGetChunkContext(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+* BumpGetChunkSpace
+*		Given a currently-allocated chunk, determine the total space
+*		it occupies (including all memory-allocation overhead).
+*/
+Size
+BumpGetChunkSpace(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
+	return 0;					/* keep compiler quiet */
+}
+
+/*
+ * BumpIsEmpty
+ *		Is a BumpContext empty of any allocated space?
+ */
+bool
+BumpIsEmpty(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		if (!BumpBlockIsEmpty(block))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * BumpStats
+ *		Compute stats about memory consumption of a Bump context.
+ *
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ * print_to_stderr: print stats to stderr if true, elog otherwise.
+ */
+void
+BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+		  void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
+{
+	BumpContext *set = (BumpContext *) context;
+	Size		nblocks = 0;
+	Size		totalspace = 0;
+	Size		freespace = 0;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		nblocks++;
+		totalspace += (block->endptr - (char *) block);
+		freespace += (block->endptr - block->freeptr);
+	}
+
+	if (printfunc)
+	{
+		char		stats_string[200];
+
+		snprintf(stats_string, sizeof(stats_string),
+				 "%zu total in %zu blocks; %zu free; %zu used",
+				 totalspace, nblocks, freespace, totalspace - freespace);
+		printfunc(context, passthru, stats_string, print_to_stderr);
+	}
+
+	if (totals)
+	{
+		totals->nblocks += nblocks;
+		totals->totalspace += totalspace;
+		totals->freespace += freespace;
+	}
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * BumpCheck
+ *		Walk through chunks and check consistency of memory.
+ *
+ * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+ * find yourself in an infinite loop when trouble occurs, because this
+ * routine will be entered again when elog cleanup tries to release memory!
+ */
+void
+BumpCheck(MemoryContext context)
+{
+	BumpContext *bump = (BumpContext *) context;
+	const char *name = context->name;
+	dlist_iter	iter;
+	Size		total_allocated = 0;
+
+	/* walk all blocks in this context */
+	dlist_foreach(iter, &bump->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+		int			nchunks;
+		char	   *ptr;
+		bool		has_external_chunk = false;
+
+		if (IsKeeperBlock(bump, block))
+			total_allocated += block->endptr - (char *) bump;
+		else
+			total_allocated += block->endptr - (char *) block;
+
+		/* check block belongs to the correct context */
+		if (block->context != bump)
+			elog(WARNING, "problem in Bump %s: bogus context link in block %p",
+				 name, block);
+
+		/* now walk through the chunks and count them */
+		nchunks = 0;
+		ptr = ((char *) block) + Bump_BLOCKHDRSZ;
+
+		while (ptr < block->freeptr)
+		{
+			MemoryChunk *chunk = (MemoryChunk *) ptr;
+			BumpBlock  *chunkblock;
+			Size		chunksize;
+
+			/* allow access to the chunk header */
+			VALGRIND_MAKE_MEM_DEFINED(chunk, Bump_CHUNKHDRSZ);
+
+			if (MemoryChunkIsExternal(chunk))
+			{
+				chunkblock = ExternalChunkGetBlock(chunk);
+				chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+				has_external_chunk = true;
+			}
+			else
+			{
+				chunkblock = MemoryChunkGetBlock(chunk);
+				chunksize = MemoryChunkGetValue(chunk);
+			}
+
+			/* move to the next chunk */
+			ptr += (chunksize + Bump_CHUNKHDRSZ);
+
+			nchunks += 1;
+
+			/* chunks have both block and context pointers, so check both */
+			if (chunkblock != block)
+				elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
+					 name, block, chunk);
+		}
+
+		if (has_external_chunk && nchunks > 1)
+			elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
+				 name, block);
+
+	}
+
+	Assert(total_allocated == context->mem_allocated);
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 9fc83f11f6..0eab919236 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -43,6 +43,20 @@ static Size BogusGetChunkSpace(void *pointer);
  *****************************************************************************/
 
 static const MemoryContextMethods mcxt_methods[] = {
+	/* bump.c */
+	[MCTX_BUMP_ID].alloc = BumpAlloc,
+	[MCTX_BUMP_ID].free_p = BumpFree,
+	[MCTX_BUMP_ID].realloc = BumpRealloc,
+	[MCTX_BUMP_ID].reset = BumpReset,
+	[MCTX_BUMP_ID].delete_context = BumpDelete,
+	[MCTX_BUMP_ID].get_chunk_context = BumpGetChunkContext,
+	[MCTX_BUMP_ID].get_chunk_space = BumpGetChunkSpace,
+	[MCTX_BUMP_ID].is_empty = BumpIsEmpty,
+	[MCTX_BUMP_ID].stats = BumpStats,
+#ifdef MEMORY_CONTEXT_CHECKING
+	[MCTX_BUMP_ID].check = BumpCheck,
+#endif
+
 	/* aset.c */
 	[MCTX_ASET_ID].alloc = AllocSetAlloc,
 	[MCTX_ASET_ID].free_p = AllocSetFree,
@@ -121,11 +135,6 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
 	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
 	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
 };
 
 /*
diff --git a/src/backend/utils/mmgr/meson.build b/src/backend/utils/mmgr/meson.build
index e6ebe145ea..2d68c97a34 100644
--- a/src/backend/utils/mmgr/meson.build
+++ b/src/backend/utils/mmgr/meson.build
@@ -3,6 +3,7 @@
 backend_sources += files(
   'alignedalloc.c',
   'aset.c',
+  'bump.c',
   'dsa.c',
   'freepage.c',
   'generation.c',
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index e5a4e5b371..b43d03d870 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -194,6 +194,11 @@ struct Tuplesortstate
 								 * tuples to return? */
 	bool		boundUsed;		/* true if we made use of a bounded heap */
 	int			bound;			/* if bounded, the maximum number of tuples */
+	int64		tupleMem;		/* memory consumed by individual tuples.
+								 * storing this separately from what we track
+								 * in availMem allows us to subtract the
+								 * memory consumed by all tuples when dumping
+								 * tuples to tape */
 	int64		availMem;		/* remaining memory available, in bytes */
 	int64		allowedMem;		/* total memory allowed, in bytes */
 	int			maxTapes;		/* max number of input tapes to merge in each
@@ -770,18 +775,18 @@ tuplesort_begin_batch(Tuplesortstate *state)
 	 * in the parent context, not this context, because there is no need to
 	 * free memtuples early.  For bounded sorts, tuples may be pfreed in any
 	 * order, so we use a regular aset.c context so that it can make use of
-	 * free'd memory.  When the sort is not bounded, we make use of a
-	 * generation.c context as this keeps allocations more compact with less
-	 * wastage.  Allocations are also slightly more CPU efficient.
+	 * free'd memory.  When the sort is not bounded, we make use of a bump.c
+	 * context as this keeps allocations more compact with less wastage.
+	 * Allocations are also slightly more CPU efficient.
 	 */
 	if (state->base.sortopt & TUPLESORT_ALLOWBOUNDED)
 		state->base.tuplecontext = AllocSetContextCreate(state->base.sortcontext,
 														 "Caller tuples",
 														 ALLOCSET_DEFAULT_SIZES);
 	else
-		state->base.tuplecontext = GenerationContextCreate(state->base.sortcontext,
-														   "Caller tuples",
-														   ALLOCSET_DEFAULT_SIZES);
+		state->base.tuplecontext = BumpContextCreate(state->base.sortcontext,
+													 "Caller tuples",
+													 ALLOCSET_DEFAULT_SIZES);
 
 
 	state->status = TSS_INITIAL;
@@ -1187,15 +1192,16 @@ noalloc:
  * Shared code for tuple and datum cases.
  */
 void
-tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev)
+tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple,
+						  bool useAbbrev, Size tuplen)
 {
 	MemoryContext oldcontext = MemoryContextSwitchTo(state->base.sortcontext);
 
 	Assert(!LEADER(state));
 
-	/* Count the size of the out-of-line data */
-	if (tuple->tuple != NULL)
-		USEMEM(state, GetMemoryChunkSpace(tuple->tuple));
+	/* account for the memory used for this tuple */
+	USEMEM(state, tuplen);
+	state->tupleMem += tuplen;
 
 	if (!useAbbrev)
 	{
@@ -2403,13 +2409,6 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 		SortTuple  *stup = &state->memtuples[i];
 
 		WRITETUP(state, state->destTape, stup);
-
-		/*
-		 * Account for freeing the tuple, but no need to do the actual pfree
-		 * since the tuplecontext is being reset after the loop.
-		 */
-		if (stup->tuple != NULL)
-			FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	}
 
 	state->memtupcount = 0;
@@ -2417,12 +2416,19 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 	/*
 	 * Reset tuple memory.  We've freed all of the tuples that we previously
 	 * allocated.  It's important to avoid fragmentation when there is a stark
-	 * change in the sizes of incoming tuples.  Fragmentation due to
-	 * AllocSetFree's bucketing by size class might be particularly bad if
-	 * this step wasn't taken.
+	 * change in the sizes of incoming tuples.  In bounded sorts,
+	 * fragmentation due to AllocSetFree's bucketing by size class might be
+	 * particularly bad if this step wasn't taken.
 	 */
 	MemoryContextReset(state->base.tuplecontext);
 
+	/*
+	 * Now update the memory accounting to subtract the memory used by the
+	 * tuple.
+	 */
+	FREEMEM(state, state->tupleMem);
+	state->tupleMem = 0;
+
 	markrunend(state->destTape);
 
 #ifdef TRACE_SORT
diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c
index eb6cfcfd00..c1de6a954e 100644
--- a/src/backend/utils/sort/tuplesortvariants.c
+++ b/src/backend/utils/sort/tuplesortvariants.c
@@ -605,6 +605,7 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 	SortTuple	stup;
 	MinimalTuple tuple;
 	HeapTupleData htup;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tuple = ExecCopySlotMinimalTuple(slot);
@@ -617,9 +618,14 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 							   tupDesc,
 							   &stup.isnull1);
 
+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tuple);
+	else
+		tuplen = MAXALIGN(tuple->t_len);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -636,6 +642,7 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 	TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tup = heap_copytuple(tup);
@@ -653,10 +660,15 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 								   &stup.isnull1);
 	}
 
+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tup);
+	else
+		tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->haveDatum1 &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -674,6 +686,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 	IndexTuple	tuple;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
+	Size		tuplen;
 
 	stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
 										  isnull, base->tuplecontext);
@@ -685,10 +698,15 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 								RelationGetDescr(arg->indexRel),
 								&stup.isnull1);
 
+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tuple);
+	else
+		tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 }
 
 /*
@@ -735,7 +753,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
 
 	tuplesort_puttuple_common(state, &stup,
 							  base->tuples &&
-							  base->sortKeys->abbrev_converter && !isNull);
+							  base->sortKeys->abbrev_converter && !isNull, 0);
 
 	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index ff6453bb7a..893237d331 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -108,6 +108,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), BumpContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 21640d62a6..6d5a70f3a7 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -108,6 +108,13 @@ extern void ProcessLogMemoryContextInterrupt(void);
  * Memory-context-type-specific functions
  */
 
+/* bump.c */
+extern MemoryContext BumpContextCreate(MemoryContext parent,
+									   const char *name,
+									   Size minContextSize,
+									   Size initBlockSize,
+									   Size maxBlockSize);
+
 /* aset.c */
 extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
 												   const char *name,
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index 2d107bbf9d..c851fd66fa 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -18,6 +18,23 @@
 
 #include "utils/memutils.h"
 
+ /* These functions implement the MemoryContext API for the Bump context. */
+extern void *BumpAlloc(MemoryContext context, Size size);
+extern void BumpFree(void *pointer);
+extern void *BumpRealloc(void *pointer, Size size);
+extern void BumpReset(MemoryContext context);
+extern void BumpDelete(MemoryContext context);
+extern MemoryContext BumpGetChunkContext(void *pointer);
+extern Size BumpGetChunkSpace(void *pointer);
+extern bool BumpIsEmpty(MemoryContext context);
+extern void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+					  void *passthru, MemoryContextCounters *totals,
+					  bool print_to_stderr);
+#ifdef MEMORY_CONTEXT_CHECKING
+extern void BumpCheck(MemoryContext context);
+#endif
+
+
 /* These functions implement the MemoryContext API for AllocSet context. */
 extern void *AllocSetAlloc(MemoryContext context, Size size);
 extern void AllocSetFree(void *pointer);
@@ -106,12 +123,13 @@ typedef enum MemoryContextMethodID
 {
 	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
 	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_BUMP_ID,				/* glibc malloc'd chunks > 128kB match 010
+								 * XXX? */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID				/* 111 occurs in wipe_mem'd memory */
+	MCTX_UNUSED3_ID				/* 111 occurs in wipe_mem'd memory */
 } MemoryContextMethodID;
 
 /*
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index af057b6358..06db9b003e 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -109,7 +109,8 @@ typedef struct TuplesortInstrumentation
  * a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
  * which is a separate palloc chunk --- we assume it is just one chunk and
  * can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
+ * simple slab allocator and when performing a non-bounded sort where we
+ * use a bump allocator).  SortTuples also contain the tuple's first key
  * column in Datum/nullflag format, and a source/input tape number that
  * tracks which tape each heap element/slot belongs to during merging.
  *
@@ -356,7 +357,8 @@ extern Tuplesortstate *tuplesort_begin_common(int workMem,
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
 extern bool tuplesort_used_bound(Tuplesortstate *state);
 extern void tuplesort_puttuple_common(Tuplesortstate *state,
-									  SortTuple *tuple, bool useAbbrev);
+									  SortTuple *tuple, bool useAbbrev,
+									  Size tuplen);
 extern void tuplesort_performsort(Tuplesortstate *state);
 extern bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 									  SortTuple *stup);
#3Nathan Bossart
nathandbossart@gmail.com
In reply to: David Rowley (#1)
Re: Add bump memory context type and use it for tuplesorts

On Tue, Jun 27, 2023 at 09:19:26PM +1200, David Rowley wrote:

Because of all of what is mentioned above about the current state of
tuplesort, there does not really seem to be much need to have chunk
headers in memory we allocate for tuples at all. Not having these
saves us a further 8 bytes per tuple.

In the attached patch, I've added a bump memory allocator which
allocates chunks without and chunk header. This means the chunks
cannot be pfree'd or realloc'd. That seems fine for the use case for
storing tuples in tuplesort. I've coded bump.c in such a way that when
built with MEMORY_CONTEXT_CHECKING, we *do* have chunk headers. That
should allow us to pick up any bugs that are introduced by any code
which accidentally tries to pfree a bump.c chunk.

This is a neat idea.

In terms of performance of tuplesort, there's a small (~5-6%)
performance gain. Not as much as I'd hoped, but I'm also doing a bit
of other work on tuplesort to make it more efficient in terms of CPU,
so I suspect the cache efficiency improvements might be more
pronounced after those.

Nice.

One thing that might need more thought is that we're running a bit low
on MemoryContextMethodIDs. I had to use an empty slot that has a bit
pattern like glibc malloc'd chunks sized 128kB. Maybe it's worth
freeing up a bit from the block offset in MemoryChunk. This is
currently 30 bits allowing 1GB offset, but these offsets are always
MAXALIGNED, so we could free up a couple of bits since those 2
lowest-order bits will always be 0 anyway.

I think it'd be okay to steal those bits. AFAICT it'd complicate the
macros in memutils_memorychunk.h a bit, but that doesn't seem like such a
terrible price to pay to allow us to keep avoiding the glibc bit patterns.

+	if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+		tuplen = GetMemoryChunkSpace(tuple);
+	else
+		tuplen = MAXALIGN(tuple->t_len);

nitpick: I see this repeated in a few places, and I wonder if it might
deserve a comment.

I haven't had a chance to try out your benchmark, but I'm hoping to do so
in the near future.

--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com

#4Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: David Rowley (#2)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 11 Jul 2023 at 01:51, David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 27 Jun 2023 at 21:19, David Rowley <dgrowleyml@gmail.com> wrote:

I've attached the bump allocator patch and also the script I used to
gather the performance results in the first 2 tabs in the attached
spreadsheet.

I've attached a v2 patch which changes the BumpContext a little to
remove some of the fields that are not really required. There was no
need for the "keeper" field as the keeper block always comes at the
end of the BumpContext as these are allocated in a single malloc().
The pointer to the "block" also isn't really needed. This is always
the same as the head element in the blocks dlist.

Neat idea, +1.

I think it would make sense to split the "add a bump allocator"
changes from the "use the bump allocator in tuplesort" patches.

Tangent: Do we have specific notes on worst-case memory usage of
memory contexts with various allocation patterns? This new bump
allocator seems to be quite efficient, but in a worst-case allocation
pattern it can still waste about 1/3 of its allocated memory due to
never using free space on previous blocks after an allocation didn't
fit on that block.
It probably isn't going to be a huge problem in general, but this
seems like something that could be documented as a potential problem
when you're looking for which allocator to use and compare it with
other allocators that handle different allocation sizes more
gracefully.

+++ b/src/backend/utils/mmgr/bump.c
+BumpBlockIsEmpty(BumpBlock *block)
+{
+    /* it's empty if the freeptr has not moved */
+    return (block->freeptr == (char *) block + Bump_BLOCKHDRSZ);
[...]
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+    char       *datastart = ((char *) block) + Bump_BLOCKHDRSZ;

These two use different definitions of the start pointer. Is that deliberate?

+++ b/src/include/utils/tuplesort.h
@@ -109,7 +109,8 @@ typedef struct TuplesortInstrumentation
* a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
* which is a separate palloc chunk --- we assume it is just one chunk and
* can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
+ * simple slab allocator and when performing a non-bounded sort where we
+ * use a bump allocator).  SortTuples also contain the tuple's first key

I'd go with something like the following:

+ * ...(except during merge *where* we use a
+ * simple slab allocator, and during a non-bounded sort where we
+ * use a bump allocator).

Kind regards,

Matthias van de Meent
Neon (https://neon.tech)

#5Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: Matthias van de Meent (#4)
Re: Add bump memory context type and use it for tuplesorts

On Mon, 6 Nov 2023 at 19:54, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

On Tue, 11 Jul 2023 at 01:51, David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 27 Jun 2023 at 21:19, David Rowley <dgrowleyml@gmail.com> wrote:

I've attached the bump allocator patch and also the script I used to
gather the performance results in the first 2 tabs in the attached
spreadsheet.

I've attached a v2 patch which changes the BumpContext a little to
remove some of the fields that are not really required. There was no
need for the "keeper" field as the keeper block always comes at the
end of the BumpContext as these are allocated in a single malloc().
The pointer to the "block" also isn't really needed. This is always
the same as the head element in the blocks dlist.

+++ b/src/backend/utils/mmgr/bump.c
[...]
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+                  const char *name,
+                  Size minContextSize,
+                  Size initBlockSize,
+                  Size maxBlockSize)
[...]
+    /* Determine size of initial block */
+    allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+    if (minContextSize != 0)
+        allocSize = Max(allocSize, minContextSize);
+    else
+        allocSize = Max(allocSize, initBlockSize);

Shouldn't this be the following, considering the meaning of "initBlockSize"?

+    allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+        Bump_CHUNKHDRSZ + initBlockSize;
+    if (minContextSize != 0)
+        allocSize = Max(allocSize, minContextSize);
+ * BumpFree
+ *        Unsupported.
[...]
+ * BumpRealloc
+ *        Unsupported.

Rather than the error, can't we make this a no-op (potentially
optionally, or in a different memory context?)

What I mean is, I get that it is an easy validator check that the code
that uses this context doesn't accidentally leak memory through
assumptions about pfree, but this does make this memory context
unusable for more generic operations where leaking a little memory is
preferred over the overhead of other memory contexts, as
MemoryContextReset is quite cheap in the grand scheme of things.

E.g. using a bump context in btree descent could speed up queries when
we use compare operators that do allocate memory (e.g. numeric, text),
because btree operators must not leak memory and thus always have to
manually keep track of all allocations, which can be expensive.

I understand that allowing pfree/repalloc in bump contexts requires
each allocation to have a MemoryChunk prefix in overhead, but I think
it's still a valid use case to have a very low overhead allocator with
no-op deallocator (except context reset). Do you have performance
comparison results between with and without the overhead of
MemoryChunk?

Kind regards,

Matthias van de Meent
Neon (https://neon.tech)

#6Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Matthias van de Meent (#5)
Re: Add bump memory context type and use it for tuplesorts

Hi,

I wanted to take a look at this patch but it seems to need a rebase,
because of a seemingly trivial conflict in MemoryContextMethodID:

--- src/include/utils/memutils_internal.h
+++ src/include/utils/memutils_internal.h
@@ -123,12 +140,13 @@ typedef enum MemoryContextMethodID
 {
   MCTX_UNUSED1_ID,      /* 000 occurs in never-used memory */
   MCTX_UNUSED2_ID,      /* glibc malloc'd chunks usually match 001 */
-  MCTX_UNUSED3_ID,      /* glibc malloc'd chunks > 128kB match 010 */
+  MCTX_BUMP_ID,        /* glibc malloc'd chunks > 128kB match 010
+                 * XXX? */
   MCTX_ASET_ID,
   MCTX_GENERATION_ID,
   MCTX_SLAB_ID,
   MCTX_ALIGNED_REDIRECT_ID,
-  MCTX_UNUSED4_ID        /* 111 occurs in wipe_mem'd memory */
+  MCTX_UNUSED3_ID        /* 111 occurs in wipe_mem'd memory */
 } MemoryContextMethodID;

I wasn't paying much attention to these memcontext reworks in 2022, so
my instinct was simply to use one of those "UNUSED" IDs. But after
looking at the 80ef92675823 a bit more, are those IDs really unused? I
mean, we're relying on those values to detect bogus pointers, right? So
if we instead start using those values for a new memory context, won't
we lose the ability to detect those issues?

Maybe I'm completely misunderstanding the implication of those limits,
but doesn't this mean the claim that we can support 8 memory context
types is not quite true, and the limit is 4, because the 4 IDs are
already used for malloc stuff?

One thing that confuses me a bit is that the comments introduced by
80ef92675823 talk about glibc, but the related discussion in [1]/messages/by-id/2910981.1665080361@sss.pgh.pa.us talks a
lot about FreeBSD, NetBSD, ... which don't actually use glibc (AFAIK).
So how portable are those unused IDs, actually?

Or am I just too caffeine-deprived and missing something obvious?

regards

[1]: /messages/by-id/2910981.1665080361@sss.pgh.pa.us

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#7Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Matthias van de Meent (#4)
Re: Add bump memory context type and use it for tuplesorts

On 11/6/23 19:54, Matthias van de Meent wrote:

...

Tangent: Do we have specific notes on worst-case memory usage of
memory contexts with various allocation patterns? This new bump
allocator seems to be quite efficient, but in a worst-case allocation
pattern it can still waste about 1/3 of its allocated memory due to
never using free space on previous blocks after an allocation didn't
fit on that block.
It probably isn't going to be a huge problem in general, but this
seems like something that could be documented as a potential problem
when you're looking for which allocator to use and compare it with
other allocators that handle different allocation sizes more
gracefully.

I don't think it's documented anywhere, but I agree it might be an
interesting piece of information. It probably did not matter too much
when we had just AllocSet, but now we have 3 very different allocators,
so maybe we should explain this.

When implementing these allocators, it didn't feel that important,
because the new allocators started as intended for a very specific part
of the code (as in "This piece of code has a very unique allocation
pattern, let's develop a custom allocator for it."), but if we feel we
want to make it simpler to use the allocators elsewhere ...

I think there are two obvious places where to document this - either in
the header of each memory context .c file, or a README in the mmgr
directory. Or some combination of it.

At some point I was thinking about writing a "proper paper" comparing
these allocators in a more scientific / thorough way, but I never got to
do it. I wonder if that'd be interesting for enough people.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tomas Vondra (#6)
Re: Add bump memory context type and use it for tuplesorts

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

Maybe I'm completely misunderstanding the implication of those limits,
but doesn't this mean the claim that we can support 8 memory context
types is not quite true, and the limit is 4, because the 4 IDs are
already used for malloc stuff?

Well, correct code would still work, but we will take a hit in our
ability to detect bogus chunk pointers if we convert any of the four
remaining bit-patterns to valid IDs. That has costs for debugging.
The particular bit patterns we left unused were calculated to make it
likely that we could detect a malloced-instead-of-palloced chunk (at
least with glibc); but in general, reducing the number of invalid
patterns makes it more likely that a just-plain-bad pointer would
escape detection.

I am less concerned about that than I was in 2022, because people
have already had some time to flush out bugs associated with the
GUC malloc->palloc conversion. Still, maybe we should think harder
about whether we can free up another ID bit before we go eating
more ID types. It's not apparent to me that the "bump context"
idea is valuable enough to foreclose ever adding more context types,
yet it will be pretty close to doing that if we commit it as-is.

If we do kick this can down the road, then I concur with eating 010
next, as it seems the least likely to occur in glibc-malloced
chunks.

One thing that confuses me a bit is that the comments introduced by
80ef92675823 talk about glibc, but the related discussion in [1] talks a
lot about FreeBSD, NetBSD, ... which don't actually use glibc (AFAIK).

The conclusion was that the specific invalid values didn't matter as
much on the other platforms as they do with glibc. But right now you
have a fifty-fifty chance that a pointer to garbage will look valid.
Do we want to increase those odds?

regards, tom lane

#9Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tom Lane (#8)
Re: Add bump memory context type and use it for tuplesorts

On 2/17/24 00:14, Tom Lane wrote:

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

Maybe I'm completely misunderstanding the implication of those limits,
but doesn't this mean the claim that we can support 8 memory context
types is not quite true, and the limit is 4, because the 4 IDs are
already used for malloc stuff?

Well, correct code would still work, but we will take a hit in our
ability to detect bogus chunk pointers if we convert any of the four
remaining bit-patterns to valid IDs. That has costs for debugging.
The particular bit patterns we left unused were calculated to make it
likely that we could detect a malloced-instead-of-palloced chunk (at
least with glibc); but in general, reducing the number of invalid
patterns makes it more likely that a just-plain-bad pointer would
escape detection.

I am less concerned about that than I was in 2022, because people
have already had some time to flush out bugs associated with the
GUC malloc->palloc conversion. Still, maybe we should think harder
about whether we can free up another ID bit before we go eating
more ID types. It's not apparent to me that the "bump context"
idea is valuable enough to foreclose ever adding more context types,
yet it will be pretty close to doing that if we commit it as-is.

If we do kick this can down the road, then I concur with eating 010
next, as it seems the least likely to occur in glibc-malloced
chunks.

I don't know if the bump context for tuplesorts alone is worth it, but
I've been thinking it's not the only place doing something like that.
I'm aware of two other places doing this "dense allocation" - spell.c
and nodeHash.c. And in those cases it definitely made a big difference
(ofc, the question is how big the difference would be now, with all the
palloc improvements).

But maybe we could switch all those places to a proper memcontext
(instead of something built on top of a memory context) ... Of course,
the code in spell.c/nodeHash.c is quite stable, so the custom code does
not cost much.

One thing that confuses me a bit is that the comments introduced by
80ef92675823 talk about glibc, but the related discussion in [1] talks a
lot about FreeBSD, NetBSD, ... which don't actually use glibc (AFAIK).

The conclusion was that the specific invalid values didn't matter as
much on the other platforms as they do with glibc. But right now you
have a fifty-fifty chance that a pointer to garbage will look valid.
Do we want to increase those odds?

Not sure. The ability to detect bogus pointers seems valuable, but is
the difference between 4/8 and 3/8 really qualitatively different? If it
is, maybe we should try to increase it by simply adding a bit.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tomas Vondra (#9)
Re: Add bump memory context type and use it for tuplesorts

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

On 2/17/24 00:14, Tom Lane wrote:

The conclusion was that the specific invalid values didn't matter as
much on the other platforms as they do with glibc. But right now you
have a fifty-fifty chance that a pointer to garbage will look valid.
Do we want to increase those odds?

Not sure. The ability to detect bogus pointers seems valuable, but is
the difference between 4/8 and 3/8 really qualitatively different? If it
is, maybe we should try to increase it by simply adding a bit.

I think it'd be worth taking a fresh look at the bit allocation in the
header word to see if we can squeeze another bit without too much
pain. There's basically no remaining headroom in the current design,
and it starts to seem like we want some. (I'm also wondering whether
the palloc_aligned stuff should have been done some other way than
by consuming a context type ID.)

regards, tom lane

#11Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#10)
Re: Add bump memory context type and use it for tuplesorts

Hi,

On 2024-02-16 20:10:48 -0500, Tom Lane wrote:

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

On 2/17/24 00:14, Tom Lane wrote:

The conclusion was that the specific invalid values didn't matter as
much on the other platforms as they do with glibc. But right now you
have a fifty-fifty chance that a pointer to garbage will look valid.
Do we want to increase those odds?

Not sure. The ability to detect bogus pointers seems valuable, but is
the difference between 4/8 and 3/8 really qualitatively different? If it
is, maybe we should try to increase it by simply adding a bit.

I think it'd be worth taking a fresh look at the bit allocation in the
header word to see if we can squeeze another bit without too much
pain. There's basically no remaining headroom in the current design,
and it starts to seem like we want some.

I think we could fairly easily "move" some bits around, by restricting the
maximum size of a non-external chunk (i.e. allocations coming out of a larger
block, not a separate allocation). Right now we reserve 30 bits for the offset
from the block header to the allocation.

It seems unlikely that it's ever worth having an undivided 1GB block. Even if
we wanted something that large - say because we want to use 1GB huge pages to
back the block - we could just add a few block headers ever couple hundred
MBs.

Another avenue is that presumably the chunk<->block header offset always has
at least the two lower bits set to zero, so perhaps we could just shift
blockoffset right by two bits in MemoryChunkSetHdrMask() and left in
MemoryChunkGetBlock()?

(I'm also wondering whether the palloc_aligned stuff should have been done
some other way than by consuming a context type ID.)

Possibly, I just don't quite know how.

Greetings,

Andres Freund

#12David Rowley
dgrowleyml@gmail.com
In reply to: Matthias van de Meent (#4)
Re: Add bump memory context type and use it for tuplesorts

Thanks for having a look at this.

On Tue, 7 Nov 2023 at 07:55, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

I think it would make sense to split the "add a bump allocator"
changes from the "use the bump allocator in tuplesort" patches.

I've done this and will post updated patches after replying to the
other comments.

Tangent: Do we have specific notes on worst-case memory usage of
memory contexts with various allocation patterns? This new bump
allocator seems to be quite efficient, but in a worst-case allocation
pattern it can still waste about 1/3 of its allocated memory due to
never using free space on previous blocks after an allocation didn't
fit on that block.
It probably isn't going to be a huge problem in general, but this
seems like something that could be documented as a potential problem
when you're looking for which allocator to use and compare it with
other allocators that handle different allocation sizes more
gracefully.

It might be a good idea to document this. The more memory allocator
types we add, the harder it is to decide which one to use when writing
new code.

+++ b/src/backend/utils/mmgr/bump.c
+BumpBlockIsEmpty(BumpBlock *block)
+{
+    /* it's empty if the freeptr has not moved */
+    return (block->freeptr == (char *) block + Bump_BLOCKHDRSZ);
[...]
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+    char       *datastart = ((char *) block) + Bump_BLOCKHDRSZ;

These two use different definitions of the start pointer. Is that deliberate?

hmm, I'm not sure if I follow what you mean. Are you talking about
the "datastart" variable and the assignment of block->freeptr (which
you've not quoted?)

+++ b/src/include/utils/tuplesort.h
@@ -109,7 +109,8 @@ typedef struct TuplesortInstrumentation
* a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
* which is a separate palloc chunk --- we assume it is just one chunk and
* can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
+ * simple slab allocator and when performing a non-bounded sort where we
+ * use a bump allocator).  SortTuples also contain the tuple's first key

I'd go with something like the following:

+ * ...(except during merge *where* we use a
+ * simple slab allocator, and during a non-bounded sort where we
+ * use a bump allocator).

Adjusted.

#13David Rowley
dgrowleyml@gmail.com
In reply to: Nathan Bossart (#3)
Re: Add bump memory context type and use it for tuplesorts

On Wed, 26 Jul 2023 at 12:11, Nathan Bossart <nathandbossart@gmail.com> wrote:

I think it'd be okay to steal those bits. AFAICT it'd complicate the
macros in memutils_memorychunk.h a bit, but that doesn't seem like such a
terrible price to pay to allow us to keep avoiding the glibc bit patterns.

I've not adjusted anything here and I've kept the patch using the

128KB glibc bit pattern. I think it was a good idea to make our

lives easier if someone came to us with a bug report, but it's not
like the reserved patterns are guaranteed to cover all malloc
implementations. What's there is just to cover the likely candidates.
I'd like to avoid adding any bit shift instructions in the code that
decodes the hdrmask.

+     if (base->sortopt & TUPLESORT_ALLOWBOUNDED)
+             tuplen = GetMemoryChunkSpace(tuple);
+     else
+             tuplen = MAXALIGN(tuple->t_len);

nitpick: I see this repeated in a few places, and I wonder if it might
deserve a comment.

I ended up adding a macro and a comment in each location that does this.

I haven't had a chance to try out your benchmark, but I'm hoping to do so
in the near future.

Great. It would be good to get a 2nd opinion.

David

#14David Rowley
dgrowleyml@gmail.com
In reply to: Matthias van de Meent (#5)
Re: Add bump memory context type and use it for tuplesorts

On Fri, 26 Jan 2024 at 01:29, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

+    allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+    if (minContextSize != 0)
+        allocSize = Max(allocSize, minContextSize);
+    else
+        allocSize = Max(allocSize, initBlockSize);

Shouldn't this be the following, considering the meaning of "initBlockSize"?

No, we want to make the blocksize exactly initBlockSize if we can. Not
initBlockSize plus all the header stuff. We do it that way for all
the other contexts and I agree that it's a good idea as it keeps the
malloc request sizes powers of 2.

+ * BumpFree
+ *        Unsupported.
[...]
+ * BumpRealloc
+ *        Unsupported.

Rather than the error, can't we make this a no-op (potentially
optionally, or in a different memory context?)

Unfortunately not. There are no MemoryChunks on bump chunks so we've
no way to determine the context type a given pointer belongs to. I've
left the MemoryChunk on there for debug builds so we can get the
ERRORs to allow us to fix the broken code that is pfreeing these
chunks.

I understand that allowing pfree/repalloc in bump contexts requires
each allocation to have a MemoryChunk prefix in overhead, but I think
it's still a valid use case to have a very low overhead allocator with
no-op deallocator (except context reset). Do you have performance
comparison results between with and without the overhead of
MemoryChunk?

Oh right, you've taken this into account. I was hoping not to have
the headers otherwise the only gains we see over using generation.c is
that of the allocation function being faster.

I certainly did do benchmarks in [1]/messages/by-id/CAApHDvoH4ASzsAOyHcxkuY01Qf++8JJ0paw+03dk+W25tQEcNQ@mail.gmail.com and saw the 338% increase due to
the reduction in memory. That massive jump happened by accident as
the sort on tenk1 went from not fitting into default 4MB work_mem to
fitting in, so what I happened to measure there was the difference of
spilling to disk and not. The same could happen for this case, so the
overhead of having the chunk headers really depends on what the test
is. Probably, "potentially large" is likely a good way to describe the
overhead of having chunk headers. However, to a lesser extent, there
will be a difference for large sorts as we'll be able to fit more
tuples per batch and do fewer batches. The smaller the tuple, the
more that will be noticeable as the chunk header is a larger portion
of the overall allocation with those.

David

[1]: /messages/by-id/CAApHDvoH4ASzsAOyHcxkuY01Qf++8JJ0paw+03dk+W25tQEcNQ@mail.gmail.com

#15David Rowley
dgrowleyml@gmail.com
In reply to: Tomas Vondra (#6)
2 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

Thanks for taking an interest in this.

On Sat, 17 Feb 2024 at 11:46, Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

I wasn't paying much attention to these memcontext reworks in 2022, so
my instinct was simply to use one of those "UNUSED" IDs. But after
looking at the 80ef92675823 a bit more, are those IDs really unused? I
mean, we're relying on those values to detect bogus pointers, right? So
if we instead start using those values for a new memory context, won't
we lose the ability to detect those issues?

I wouldn't say we're "relying" on them. Really there just there to
improve debugability. If we call any code that tries to look at the
MemoryChunk header of a malloc'd chunk, then we can expect bad things
to happen. We no longer have any code which does this.
MemoryContextContains() did, and it's now gone.

Maybe I'm completely misunderstanding the implication of those limits,
but doesn't this mean the claim that we can support 8 memory context
types is not quite true, and the limit is 4, because the 4 IDs are
already used for malloc stuff?

I think we all expected a bit more pain from the memory context
change. I was happy that Tom did the extra work to look at the malloc
patterns of glibc, but I think there's been very little gone wrong.
The reserved MemoryContextMethodIDs do seem to have allowed [1]/messages/by-id/796b65c3-57b7-bddf-b0d5-a8afafb8b627@gmail.com to be
found, but I guess there'd have been a segfault instead of an ERROR
without the reserved IDs.

I've attached version 2, now split into 2 patches.

0001 for the bump allocator
0002 to use the new allocator for tuplesorts

David

[1]: /messages/by-id/796b65c3-57b7-bddf-b0d5-a8afafb8b627@gmail.com

Attachments:

v3-0001-Introduce-a-bump-memory-allocator.patchtext/plain; charset=US-ASCII; name=v3-0001-Introduce-a-bump-memory-allocator.patchDownload
From 3270fbbb9bceb3b082a0b27f7fd1759c2b1c4150 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 21:49:57 +1300
Subject: [PATCH v3 1/2] Introduce a bump memory allocator

---
 src/backend/nodes/gen_node_support.pl |   2 +-
 src/backend/utils/mmgr/Makefile       |   1 +
 src/backend/utils/mmgr/bump.c         | 747 ++++++++++++++++++++++++++
 src/backend/utils/mmgr/mcxt.c         |  19 +-
 src/backend/utils/mmgr/meson.build    |   1 +
 src/include/nodes/memnodes.h          |   3 +-
 src/include/utils/memutils.h          |   7 +
 src/include/utils/memutils_internal.h |  21 +-
 src/tools/pgindent/typedefs.list      |   2 +
 9 files changed, 794 insertions(+), 9 deletions(-)
 create mode 100644 src/backend/utils/mmgr/bump.c

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 2f0a59bc87..9401f8d7a1 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -149,7 +149,7 @@ my @abstract_types = qw(Node);
 # they otherwise don't participate in node support.
 my @extra_tags = qw(
   IntList OidList XidList
-  AllocSetContext GenerationContext SlabContext
+  AllocSetContext GenerationContext SlabContext BumpContext
   TIDBitmap
   WindowObjectData
 );
diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index dae3432c98..01a1fb8527 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	alignedalloc.o \
 	aset.o \
+	bump.o \
 	dsa.o \
 	freepage.o \
 	generation.o \
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
new file mode 100644
index 0000000000..015e66709f
--- /dev/null
+++ b/src/backend/utils/mmgr/bump.c
@@ -0,0 +1,747 @@
+/*-------------------------------------------------------------------------
+ *
+ * bump.c
+ *	  Bump allocator definitions.
+ *
+ * Bump is a MemoryContext implementation designed for memory usages which
+ * require allocating a large number of chunks, none of which ever need to be
+ * pfree'd or realloc'd.  Chunks allocated by this context have no chunk header
+ * and operations which ordinarily require looking at the chunk header cannot
+ * be performed.  For example, pfree, realloc, GetMemoryChunkSpace and
+ * GetMemoryChunkContext are all not possible with bump allocated chunks.  The
+ * only way to release memory allocated by this context type is to reset or
+ * delete the context.
+ *
+ * Portions Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/bump.c
+ *
+ *
+ *	Bump is best suited to cases which require a large number of short-lived
+ *	chunks where performance matters.  Because bump allocated chunks don't
+ *	have a chunk header, it can fit more chunks on each block.  This means we
+ *	can do more with less memory and fewer cache lines.  The reason it's best
+ *	suited for short-lived usages of memory is that ideally, pointers to bump
+ *	allocated chunks won't be visible to a large amount of code.  The more
+ *	code that operates on memory allocated by this allocator, the more chances
+ *	that some code will try to perform a pfree or one of the other operations
+ *	which are made impossible due to the lack of chunk header.  In order to
+ *	to detect accidental usage of the various disallowed operations, we do add
+ *	a MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
+ *	various disallowed functions raise an ERROR.
+ *
+ *	Allocations are MAXALIGNed.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "port/pg_bitutils.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
+
+#define Bump_BLOCKHDRSZ	MAXALIGN(sizeof(BumpBlock))
+
+/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
+#ifdef MEMORY_CONTEXT_CHECKING
+#define Bump_CHUNKHDRSZ	sizeof(MemoryChunk)
+#else
+#define Bump_CHUNKHDRSZ	0
+#endif
+
+#define Bump_CHUNK_FRACTION	8
+
+/* The keeper block is allocated in the same allocation as the set */
+#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
+#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))
+
+typedef struct BumpBlock BumpBlock; /* forward reference */
+
+typedef struct BumpContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* Bump context parameters */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
+
+	dlist_head	blocks;			/* list of blocks with the block currently
+								 * being filled at the head */
+} BumpContext;
+
+/*
+ * BumpBlock
+ *		BumpBlock is the unit of memory that is obtained by bump.c from
+ *		malloc().  It contains zero or more allocations, which are the
+ *		units requested by palloc().
+ */
+struct BumpBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+#ifdef MEMORY_CONTEXT_CHECKING
+	BumpContext *context;		/* pointer back to the owning context */
+#endif
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+/*
+ * BumpIsValid
+ *		True iff set is valid bump context.
+ */
+#define BumpIsValid(set) \
+	(PointerIsValid(set) && IsA(set, BumpContext))
+
+/*
+ * BumpBlockIsValid
+ *		True iff block is valid block of a bump context
+ */
+#define BumpBlockIsValid(block) \
+	(PointerIsValid(block) && BumpIsValid((block)->context))
+
+/*
+ * We always store external chunks on a dedicated block.  This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+	(BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
+
+/* Inlined helper functions */
+static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
+								 Size blksize);
+static inline bool BumpBlockIsEmpty(BumpBlock *block);
+static inline void BumpBlockMarkEmpty(BumpBlock *block);
+static inline Size BumpBlockFreeBytes(BumpBlock *block);
+static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
+
+
+/*
+* BumpContextCreate
+*		Create a new Bump context.
+*
+* parent: parent context, or NULL if top-level context
+* name: name of context (must be statically allocated)
+* minContextSize: minimum context size
+* initBlockSize: initial allocation block size
+* maxBlockSize: maximum allocation block size
+*/
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+				  const char *name,
+				  Size minContextSize,
+				  Size initBlockSize,
+				  Size maxBlockSize)
+{
+	Size		firstBlockSize;
+	Size		allocSize;
+	BumpContext *set;
+	BumpBlock  *block;
+
+	/* ensure MemoryChunk's size is properly maxaligned */
+	StaticAssertDecl(Bump_CHUNKHDRSZ == MAXALIGN(Bump_CHUNKHDRSZ),
+					 "sizeof(MemoryChunk) is not maxaligned");
+
+	/*
+	 * First, validate allocation parameters.  Asserts seem sufficient because
+	 * nobody varies their parameters at runtime.  We somewhat arbitrarily
+	 * enforce a minimum 1K block size.  We restrict the maximum block size to
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+	 * regards to addressing the offset between the chunk and the block that
+	 * the chunk is stored on.  We would be unable to store the offset between
+	 * the chunk and block for any chunks that were beyond
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+	 * larger than this.
+	 */
+	Assert(initBlockSize == MAXALIGN(initBlockSize) &&
+		   initBlockSize >= 1024);
+	Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
+		   maxBlockSize >= initBlockSize &&
+		   AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
+	Assert(minContextSize == 0 ||
+		   (minContextSize == MAXALIGN(minContextSize) &&
+			minContextSize >= 1024 &&
+			minContextSize <= maxBlockSize));
+	Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+
+	/* Determine size of initial block */
+	allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+		Bump_CHUNKHDRSZ;
+	if (minContextSize != 0)
+		allocSize = Max(allocSize, minContextSize);
+	else
+		allocSize = Max(allocSize, initBlockSize);
+
+	/*
+	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
+	 * the context header and its block header follows that.
+	 */
+	set = (BumpContext *) malloc(allocSize);
+	if (set == NULL)
+	{
+		MemoryContextStats(TopMemoryContext);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("Failed while creating memory context \"%s\".",
+						   name)));
+	}
+
+	/*
+	 * Avoid writing code that can fail between here and MemoryContextCreate;
+	 * we'd leak the header if we ereport in this stretch.
+	 */
+	dlist_init(&set->blocks);
+
+	/* Fill in the initial block's block header */
+	block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
+	/* determine the block size and initialize it */
+	firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
+	BumpBlockInit(set, block, firstBlockSize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	/*
+	 * Fill in BumpContext-specific header fields.  The Asserts above should
+	 * ensure that these all fit inside a uint32.
+	 */
+	set->initBlockSize = (uint32) initBlockSize;
+	set->maxBlockSize = (uint32) maxBlockSize;
+	set->nextBlockSize = (uint32) initBlockSize;
+
+	/*
+	 * Compute the allocation chunk size limit for this context.
+	 *
+	 * Limit the maximum size a non-dedicated chunk can be so that we can fit
+	 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
+	 * block.  We must further limit this value so that it's no more than
+	 * MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks larger
+	 * than that value as we store the chunk size in the MemoryChunk 'value'
+	 * field in the call to MemoryChunkSetHdrMask().
+	 */
+	set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
+	while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
+		   (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
+		set->allocChunkLimit >>= 1;
+
+	/* Finally, do the type-independent part of context creation */
+	MemoryContextCreate((MemoryContext) set,
+						T_BumpContext,
+						MCTX_BUMP_ID,
+						parent,
+						name);
+
+	((MemoryContext) set)->mem_allocated = allocSize;
+
+	return (MemoryContext) set;
+}
+
+/*
+* BumpReset
+*		Frees all memory which is allocated in the given set.
+*
+* The code simply frees all the blocks in the context apart from the keeper
+* block.
+*/
+void
+BumpReset(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_mutable_iter miter;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	BumpCheck(context);
+#endif
+
+	dlist_foreach_modify(miter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, miter.cur);
+
+		if (IsKeeperBlock(set, block))
+			BumpBlockMarkEmpty(block);
+		else
+			BumpBlockFree(set, block);
+	}
+
+	/* Reset block size allocation sequence, too */
+	set->nextBlockSize = set->initBlockSize;
+
+	/* Ensure there is only 1 item in the dlist */
+	Assert(!dlist_is_empty(&set->blocks));
+	Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
+}
+
+/*
+ * BumpDelete
+ *		Free all memory which is allocated in the given context.
+ */
+void
+BumpDelete(MemoryContext context)
+{
+	/* Reset to release all releasable BumpBlocks */
+	BumpReset(context);
+	/* And free the context header and keeper block */
+	free(context);
+}
+
+/*
+ * BumpAlloc
+ *		Returns pointer to allocated memory of the given size or NULL if
+ *		the request could not be completed; memory is added to the set.
+ *
+ * No request may exceed:
+ *		MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
+ * All callers use a much-lower limit.
+ *
+ * Note: when using valgrind, it doesn't matter how the returned allocation
+ * is marked, as mcxt.c will set it to UNDEFINED.
+ */
+void *
+BumpAlloc(MemoryContext context, Size size)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#else
+	void	   *ptr;
+#endif
+	Size		chunk_size;
+	Size		required_size;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+
+	/* is it an over-sized chunk? if yes, allocate special block */
+	if (chunk_size > set->allocChunkLimit)
+	{
+		Size		blksize = required_size + Bump_BLOCKHDRSZ;
+
+		block = (BumpBlock *) malloc(blksize);
+		if (block == NULL)
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		/* the block is completely full */
+		block->freeptr = block->endptr = ((char *) block) + blksize;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+		/* block with a single (used) chunk */
+		block->context = set;
+
+		chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
+
+		/* mark the MemoryChunk as externally managed */
+		MemoryChunkSetHdrMaskExternal(chunk, MCTX_BUMP_ID);
+
+		chunk->requested_size = size;
+		/* set mark to catch clobber of "unused" space */
+		Assert(size < chunk_size);
+		set_sentinel(MemoryChunkGetPointer(chunk), size);
+#endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+		/* fill the allocated space with junk */
+		randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+		/* add the block to the list of allocated blocks */
+		dlist_push_head(&set->blocks, &block->node);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+		/* Ensure any padding bytes are marked NOACCESS. */
+		VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+								   chunk_size - size);
+
+		/* Disallow access to the chunk header. */
+		VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+		return MemoryChunkGetPointer(chunk);
+#else
+		return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
+#endif
+
+	}
+
+	/*
+	 * Not an oversized chunk.  We try to first make use of the latest block,
+	 * but if there's not enough space in it we must allocate a new block.
+	 */
+	block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
+
+	if (BumpBlockFreeBytes(block) < required_size)
+	{
+		Size		blksize;
+
+		/*
+		 * The first such block has size initBlockSize, and we double the
+		 * space in each succeeding block, but not more than maxBlockSize.
+		 */
+		blksize = set->nextBlockSize;
+		set->nextBlockSize <<= 1;
+		if (set->nextBlockSize > set->maxBlockSize)
+			set->nextBlockSize = set->maxBlockSize;
+
+		/* we'll need a block hdr too, so add that to the required size */
+		required_size += Bump_BLOCKHDRSZ;
+
+		/* round the size up to the next power of 2 */
+		if (blksize < required_size)
+			blksize = pg_nextpower2_size_t(required_size);
+
+		block = (BumpBlock *) malloc(blksize);
+
+		if (block == NULL)
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		/* initialize the new block */
+		BumpBlockInit(set, block, blksize);
+
+		/* add it to the doubly-linked list of blocks */
+		dlist_push_head(&set->blocks, &block->node);
+	}
+
+	/* we're supposed to have a block with enough free space now */
+	Assert(block != NULL);
+	Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	chunk = (MemoryChunk *) block->freeptr;
+#else
+	ptr = (void *) block->freeptr;
+#endif
+
+	/* point the freeptr beyond this chunk */
+	block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
+	Assert(block->freeptr <= block->endptr);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+	/* Prepare to initialize the chunk header. */
+	VALGRIND_MAKE_MEM_UNDEFINED(chunk, Bump_CHUNKHDRSZ);
+
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return ptr;
+#endif							/* MEMORY_CONTEXT_CHECKING */
+}
+
+/*
+ * BumpBlockInit
+ *		Initializes 'block' assuming 'blksize'.  Does not update the context's
+ *		mem_allocated field.
+ */
+static inline void
+BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	block->context = context;
+#endif
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+	block->endptr = ((char *) block) + blksize;
+
+	/* Mark unallocated space NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockIsEmpty
+ *		Returns true iff 'block' contains no chunks
+ */
+static inline bool
+BumpBlockIsEmpty(BumpBlock *block)
+{
+	/* it's empty if the freeptr has not moved */
+	return (block->freeptr == (char *) block + Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockMarkEmpty
+ *		Set a block as empty.  Does not free the block.
+ */
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+	char	   *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
+#endif
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(datastart, block->freeptr - datastart);
+#else
+	/* wipe_mem() would have done this */
+	VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
+#endif
+
+	/* Reset the block, but don't return it to malloc */
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+}
+
+/*
+ * BumpBlockFreeBytes
+ *		Returns the number of bytes free in 'block'
+ */
+static inline Size
+BumpBlockFreeBytes(BumpBlock *block)
+{
+	return (block->endptr - block->freeptr);
+}
+
+/*
+ * BumpBlockFree
+ *		Remove 'block' from 'set' and release the memory consumed by it.
+ */
+static inline void
+BumpBlockFree(BumpContext *set, BumpBlock *block)
+{
+	/* Make sure nobody tries to free the keeper block */
+	Assert(!IsKeeperBlock(set, block));
+
+	/* release the block from the list of blocks */
+	dlist_delete(&block->node);
+
+	((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(block, ((char *) block->endptr - (char *) block));
+#endif
+
+	free(block);
+}
+
+/*
+ * BumpFree
+ *		Unsupported.
+ */
+void
+BumpFree(void *pointer)
+{
+	elog(ERROR, "pfree is not supported by the bump memory allocator");
+}
+
+/*
+ * BumpRealloc
+ *		Unsupported.
+ */
+void *
+BumpRealloc(void *pointer, Size size)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * BumpGetChunkContext
+ *		Unsupported.
+ */
+MemoryContext
+BumpGetChunkContext(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+* BumpGetChunkSpace
+*		Given a currently-allocated chunk, determine the total space
+*		it occupies (including all memory-allocation overhead).
+*/
+Size
+BumpGetChunkSpace(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
+	return 0;					/* keep compiler quiet */
+}
+
+/*
+ * BumpIsEmpty
+ *		Is a BumpContext empty of any allocated space?
+ */
+bool
+BumpIsEmpty(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		if (!BumpBlockIsEmpty(block))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * BumpStats
+ *		Compute stats about memory consumption of a Bump context.
+ *
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ * print_to_stderr: print stats to stderr if true, elog otherwise.
+ */
+void
+BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+		  void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
+{
+	BumpContext *set = (BumpContext *) context;
+	Size		nblocks = 0;
+	Size		totalspace = 0;
+	Size		freespace = 0;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		nblocks++;
+		totalspace += (block->endptr - (char *) block);
+		freespace += (block->endptr - block->freeptr);
+	}
+
+	if (printfunc)
+	{
+		char		stats_string[200];
+
+		snprintf(stats_string, sizeof(stats_string),
+				 "%zu total in %zu blocks; %zu free; %zu used",
+				 totalspace, nblocks, freespace, totalspace - freespace);
+		printfunc(context, passthru, stats_string, print_to_stderr);
+	}
+
+	if (totals)
+	{
+		totals->nblocks += nblocks;
+		totals->totalspace += totalspace;
+		totals->freespace += freespace;
+	}
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * BumpCheck
+ *		Walk through chunks and check consistency of memory.
+ *
+ * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+ * find yourself in an infinite loop when trouble occurs, because this
+ * routine will be entered again when elog cleanup tries to release memory!
+ */
+void
+BumpCheck(MemoryContext context)
+{
+	BumpContext *bump = (BumpContext *) context;
+	const char *name = context->name;
+	dlist_iter	iter;
+	Size		total_allocated = 0;
+
+	/* walk all blocks in this context */
+	dlist_foreach(iter, &bump->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+		int			nchunks;
+		char	   *ptr;
+		bool		has_external_chunk = false;
+
+		if (IsKeeperBlock(bump, block))
+			total_allocated += block->endptr - (char *) bump;
+		else
+			total_allocated += block->endptr - (char *) block;
+
+		/* check block belongs to the correct context */
+		if (block->context != bump)
+			elog(WARNING, "problem in Bump %s: bogus context link in block %p",
+				 name, block);
+
+		/* now walk through the chunks and count them */
+		nchunks = 0;
+		ptr = ((char *) block) + Bump_BLOCKHDRSZ;
+
+		while (ptr < block->freeptr)
+		{
+			MemoryChunk *chunk = (MemoryChunk *) ptr;
+			BumpBlock  *chunkblock;
+			Size		chunksize;
+
+			/* allow access to the chunk header */
+			VALGRIND_MAKE_MEM_DEFINED(chunk, Bump_CHUNKHDRSZ);
+
+			if (MemoryChunkIsExternal(chunk))
+			{
+				chunkblock = ExternalChunkGetBlock(chunk);
+				chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+				has_external_chunk = true;
+			}
+			else
+			{
+				chunkblock = MemoryChunkGetBlock(chunk);
+				chunksize = MemoryChunkGetValue(chunk);
+			}
+
+			/* move to the next chunk */
+			ptr += (chunksize + Bump_CHUNKHDRSZ);
+
+			nchunks += 1;
+
+			/* chunks have both block and context pointers, so check both */
+			if (chunkblock != block)
+				elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
+					 name, block, chunk);
+		}
+
+		if (has_external_chunk && nchunks > 1)
+			elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
+				 name, block);
+
+	}
+
+	Assert(total_allocated == context->mem_allocated);
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index ad7409a02c..4b1f5f4957 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -43,6 +43,20 @@ static Size BogusGetChunkSpace(void *pointer);
  *****************************************************************************/
 
 static const MemoryContextMethods mcxt_methods[] = {
+	/* bump.c */
+	[MCTX_BUMP_ID].alloc = BumpAlloc,
+	[MCTX_BUMP_ID].free_p = BumpFree,
+	[MCTX_BUMP_ID].realloc = BumpRealloc,
+	[MCTX_BUMP_ID].reset = BumpReset,
+	[MCTX_BUMP_ID].delete_context = BumpDelete,
+	[MCTX_BUMP_ID].get_chunk_context = BumpGetChunkContext,
+	[MCTX_BUMP_ID].get_chunk_space = BumpGetChunkSpace,
+	[MCTX_BUMP_ID].is_empty = BumpIsEmpty,
+	[MCTX_BUMP_ID].stats = BumpStats,
+#ifdef MEMORY_CONTEXT_CHECKING
+	[MCTX_BUMP_ID].check = BumpCheck,
+#endif
+
 	/* aset.c */
 	[MCTX_ASET_ID].alloc = AllocSetAlloc,
 	[MCTX_ASET_ID].free_p = AllocSetFree,
@@ -121,11 +135,6 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
 	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
 	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
 };
 
 /*
diff --git a/src/backend/utils/mmgr/meson.build b/src/backend/utils/mmgr/meson.build
index 9dcf990cdc..dd43a6844c 100644
--- a/src/backend/utils/mmgr/meson.build
+++ b/src/backend/utils/mmgr/meson.build
@@ -3,6 +3,7 @@
 backend_sources += files(
   'alignedalloc.c',
   'aset.c',
+  'bump.c',
   'dsa.c',
   'freepage.c',
   'generation.c',
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index a48f7e5a18..a0bd273337 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -108,6 +108,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), BumpContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 7fd41d20ca..4f811641ec 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -107,6 +107,13 @@ extern void ProcessLogMemoryContextInterrupt(void);
  * Memory-context-type-specific functions
  */
 
+/* bump.c */
+extern MemoryContext BumpContextCreate(MemoryContext parent,
+									   const char *name,
+									   Size minContextSize,
+									   Size initBlockSize,
+									   Size maxBlockSize);
+
 /* aset.c */
 extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
 												   const char *name,
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index e0c4f3d5af..ec9df258b9 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -18,6 +18,23 @@
 
 #include "utils/memutils.h"
 
+ /* These functions implement the MemoryContext API for the Bump context. */
+extern void *BumpAlloc(MemoryContext context, Size size);
+extern void BumpFree(void *pointer);
+extern void *BumpRealloc(void *pointer, Size size);
+extern void BumpReset(MemoryContext context);
+extern void BumpDelete(MemoryContext context);
+extern MemoryContext BumpGetChunkContext(void *pointer);
+extern Size BumpGetChunkSpace(void *pointer);
+extern bool BumpIsEmpty(MemoryContext context);
+extern void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+					  void *passthru, MemoryContextCounters *totals,
+					  bool print_to_stderr);
+#ifdef MEMORY_CONTEXT_CHECKING
+extern void BumpCheck(MemoryContext context);
+#endif
+
+
 /* These functions implement the MemoryContext API for AllocSet context. */
 extern void *AllocSetAlloc(MemoryContext context, Size size);
 extern void AllocSetFree(void *pointer);
@@ -106,12 +123,12 @@ typedef enum MemoryContextMethodID
 {
 	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
 	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_BUMP_ID,				/* Also glibc malloc'd chunks > 128kB */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID,			/* 111 occurs in wipe_mem'd memory */
+	MCTX_UNUSED3_ID,			/* 111 occurs in wipe_mem'd memory */
 } MemoryContextMethodID;
 
 /*
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d808aad8b0..f247e300e7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -333,6 +333,8 @@ BuildAccumulator
 BuiltinScript
 BulkInsertState
 BulkInsertStateData
+BumpBlock
+BumpContext
 CACHESIGN
 CAC_state
 CCFastEqualFN
-- 
2.40.1

v3-0002-Use-bump-memory-context-for-tuplesorts.patchtext/plain; charset=US-ASCII; name=v3-0002-Use-bump-memory-context-for-tuplesorts.patchDownload
From e846417591711e65418a0e90cdd34fa800e8bc41 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 22:23:42 +1300
Subject: [PATCH v3 2/2] Use bump memory context for tuplesorts

---
 src/backend/utils/sort/tuplesort.c         | 52 ++++++++++++----------
 src/backend/utils/sort/tuplesortvariants.c | 38 +++++++++++++---
 src/include/utils/tuplesort.h              | 21 ++++++---
 3 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 97e8f93b93..9f515deff9 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -194,6 +194,11 @@ struct Tuplesortstate
 								 * tuples to return? */
 	bool		boundUsed;		/* true if we made use of a bounded heap */
 	int			bound;			/* if bounded, the maximum number of tuples */
+	int64		tupleMem;		/* memory consumed by individual tuples.
+								 * storing this separately from what we track
+								 * in availMem allows us to subtract the
+								 * memory consumed by all tuples when dumping
+								 * tuples to tape */
 	int64		availMem;		/* remaining memory available, in bytes */
 	int64		allowedMem;		/* total memory allowed, in bytes */
 	int			maxTapes;		/* max number of input tapes to merge in each
@@ -767,18 +772,18 @@ tuplesort_begin_batch(Tuplesortstate *state)
 	 * in the parent context, not this context, because there is no need to
 	 * free memtuples early.  For bounded sorts, tuples may be pfreed in any
 	 * order, so we use a regular aset.c context so that it can make use of
-	 * free'd memory.  When the sort is not bounded, we make use of a
-	 * generation.c context as this keeps allocations more compact with less
-	 * wastage.  Allocations are also slightly more CPU efficient.
-	 */
-	if (state->base.sortopt & TUPLESORT_ALLOWBOUNDED)
+	 * free'd memory.  When the sort is not bounded, we make use of a bump.c
+	 * context as this keeps allocations more compact with less wastage.
+	 * Allocations are also slightly more CPU efficient.
+	 */
+	if (TupleSortUseBumpTupleCxt(state->base.sortopt))
+		state->base.tuplecontext = BumpContextCreate(state->base.sortcontext,
+													 "Caller tuples",
+													 ALLOCSET_DEFAULT_SIZES);
+	else
 		state->base.tuplecontext = AllocSetContextCreate(state->base.sortcontext,
 														 "Caller tuples",
 														 ALLOCSET_DEFAULT_SIZES);
-	else
-		state->base.tuplecontext = GenerationContextCreate(state->base.sortcontext,
-														   "Caller tuples",
-														   ALLOCSET_DEFAULT_SIZES);
 
 
 	state->status = TSS_INITIAL;
@@ -1184,15 +1189,16 @@ noalloc:
  * Shared code for tuple and datum cases.
  */
 void
-tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev)
+tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple,
+						  bool useAbbrev, Size tuplen)
 {
 	MemoryContext oldcontext = MemoryContextSwitchTo(state->base.sortcontext);
 
 	Assert(!LEADER(state));
 
-	/* Count the size of the out-of-line data */
-	if (tuple->tuple != NULL)
-		USEMEM(state, GetMemoryChunkSpace(tuple->tuple));
+	/* account for the memory used for this tuple */
+	USEMEM(state, tuplen);
+	state->tupleMem += tuplen;
 
 	if (!useAbbrev)
 	{
@@ -2400,13 +2406,6 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 		SortTuple  *stup = &state->memtuples[i];
 
 		WRITETUP(state, state->destTape, stup);
-
-		/*
-		 * Account for freeing the tuple, but no need to do the actual pfree
-		 * since the tuplecontext is being reset after the loop.
-		 */
-		if (stup->tuple != NULL)
-			FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	}
 
 	state->memtupcount = 0;
@@ -2414,12 +2413,19 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 	/*
 	 * Reset tuple memory.  We've freed all of the tuples that we previously
 	 * allocated.  It's important to avoid fragmentation when there is a stark
-	 * change in the sizes of incoming tuples.  Fragmentation due to
-	 * AllocSetFree's bucketing by size class might be particularly bad if
-	 * this step wasn't taken.
+	 * change in the sizes of incoming tuples.  In bounded sorts,
+	 * fragmentation due to AllocSetFree's bucketing by size class might be
+	 * particularly bad if this step wasn't taken.
 	 */
 	MemoryContextReset(state->base.tuplecontext);
 
+	/*
+	 * Now update the memory accounting to subtract the memory used by the
+	 * tuple.
+	 */
+	FREEMEM(state, state->tupleMem);
+	state->tupleMem = 0;
+
 	markrunend(state->destTape);
 
 #ifdef TRACE_SORT
diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c
index 08f9f16262..42293b5095 100644
--- a/src/backend/utils/sort/tuplesortvariants.c
+++ b/src/backend/utils/sort/tuplesortvariants.c
@@ -674,6 +674,7 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 	SortTuple	stup;
 	MinimalTuple tuple;
 	HeapTupleData htup;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tuple = ExecCopySlotMinimalTuple(slot);
@@ -686,9 +687,15 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 							   tupDesc,
 							   &stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -705,6 +712,7 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 	TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tup = heap_copytuple(tup);
@@ -722,10 +730,16 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 								   &stup.isnull1);
 	}
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->haveDatum1 &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -743,6 +757,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 	IndexTuple	tuple;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
+	Size		tuplen;
 
 	stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
 										  isnull, base->tuplecontext);
@@ -754,10 +769,16 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 								RelationGetDescr(arg->indexRel),
 								&stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 }
 
 /*
@@ -770,6 +791,7 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	BrinSortTuple *bstup;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
+	Size		tuplen;
 
 	/* allocate space for the whole BRIN sort tuple */
 	bstup = palloc(BRINSORTTUPLE_SIZE(size));
@@ -781,10 +803,16 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	stup.datum1 = tuple->bt_blkno;
 	stup.isnull1 = false;
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
+	else
+		tuplen = GetMemoryChunkSpace(bstup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -833,7 +861,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
 
 	tuplesort_puttuple_common(state, &stup,
 							  base->tuples &&
-							  base->sortKeys->abbrev_converter && !isNull);
+							  base->sortKeys->abbrev_converter && !isNull, 0);
 
 	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 1013c64dab..e7941a1f09 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -98,6 +98,15 @@ typedef enum
 /* specifies if the tuplesort is able to support bounded sorts */
 #define TUPLESORT_ALLOWBOUNDED			(1 << 1)
 
+/*
+ * For bounded sort, tuples get pfree'd when they fall outside of the bound.
+ * When bounded sorts are not required, we can use a bump context for tuple
+ * allocation as there's no risk that pfree will ever be called for a tuple.
+ * Define a macro to make it easier for code to figure out if we're using a
+ * bump allocator.
+ */
+#define TupleSortUseBumpTupleCxt(opt) (((opt) & TUPLESORT_ALLOWBOUNDED) == 0)
+
 typedef struct TuplesortInstrumentation
 {
 	TuplesortMethod sortMethod; /* sort algorithm used */
@@ -109,10 +118,11 @@ typedef struct TuplesortInstrumentation
  * The objects we actually sort are SortTuple structs.  These contain
  * a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
  * which is a separate palloc chunk --- we assume it is just one chunk and
- * can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
- * column in Datum/nullflag format, and a source/input tape number that
- * tracks which tape each heap element/slot belongs to during merging.
+ * can be freed by a simple pfree() (except during merge, where we use a
+ * simple slab allocator, and during a non-bounded sort where we use a bump
+ * allocator).  SortTuples also contain the tuple's first key column in
+ * Datum/nullflag format, and a source/input tape number that tracks which
+ * tape each heap element/slot belongs to during merging.
  *
  * Storing the first key column lets us save heap_getattr or index_getattr
  * calls during tuple comparisons.  We could extract and save all the key
@@ -367,7 +377,8 @@ extern Tuplesortstate *tuplesort_begin_common(int workMem,
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
 extern bool tuplesort_used_bound(Tuplesortstate *state);
 extern void tuplesort_puttuple_common(Tuplesortstate *state,
-									  SortTuple *tuple, bool useAbbrev);
+									  SortTuple *tuple, bool useAbbrev,
+									  Size tuplen);
 extern void tuplesort_performsort(Tuplesortstate *state);
 extern bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 									  SortTuple *stup);
-- 
2.40.1

#16Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: David Rowley (#12)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 20 Feb 2024 at 10:41, David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 7 Nov 2023 at 07:55, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

+++ b/src/backend/utils/mmgr/bump.c
+BumpBlockIsEmpty(BumpBlock *block)
+{
+    /* it's empty if the freeptr has not moved */
+    return (block->freeptr == (char *) block + Bump_BLOCKHDRSZ);
[...]
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+    char       *datastart = ((char *) block) + Bump_BLOCKHDRSZ;

These two use different definitions of the start pointer. Is that deliberate?

hmm, I'm not sure if I follow what you mean. Are you talking about
the "datastart" variable and the assignment of block->freeptr (which
you've not quoted?)

What I meant was that

(char *) block + Bump_BLOCKHDRSZ

vs

((char *) block) + Bump_BLOCKHDRSZ

, when combined with my little experience with pointer addition and
precedence, and a lack of compiler at the ready at that point in time,
I was afraid that "(char *) block + Bump_BLOCKHDRSZ" would be parsed
as "(char *) (block + Bump_BLOCKHDRSZ)", which would get different
offsets across the two statements.
Godbolt has since helped me understand that both are equivalent.

Kind regards,

Matthias van de Meent

#17Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: David Rowley (#14)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 20 Feb 2024 at 11:02, David Rowley <dgrowleyml@gmail.com> wrote:

On Fri, 26 Jan 2024 at 01:29, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

+    allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+    if (minContextSize != 0)
+        allocSize = Max(allocSize, minContextSize);
+    else
+        allocSize = Max(allocSize, initBlockSize);

Shouldn't this be the following, considering the meaning of "initBlockSize"?

No, we want to make the blocksize exactly initBlockSize if we can. Not
initBlockSize plus all the header stuff. We do it that way for all
the other contexts and I agree that it's a good idea as it keeps the
malloc request sizes powers of 2.

One part of the reason of my comment was that initBlockSize was
ignored in favour of minContextSize if that was configured, regardless
of the value of initBlockSize. Is it deliberately ignored when
minContextSize is set?

+ * BumpFree
+ *        Unsupported.
[...]
+ * BumpRealloc
+ *        Unsupported.

Rather than the error, can't we make this a no-op (potentially
optionally, or in a different memory context?)

Unfortunately not. There are no MemoryChunks on bump chunks so we've
no way to determine the context type a given pointer belongs to. I've
left the MemoryChunk on there for debug builds so we can get the
ERRORs to allow us to fix the broken code that is pfreeing these
chunks.

I understand that allowing pfree/repalloc in bump contexts requires
each allocation to have a MemoryChunk prefix in overhead, but I think
it's still a valid use case to have a very low overhead allocator with
no-op deallocator (except context reset). Do you have performance
comparison results between with and without the overhead of
MemoryChunk?

Oh right, you've taken this into account. I was hoping not to have
the headers otherwise the only gains we see over using generation.c is
that of the allocation function being faster.

[...] The smaller the tuple, the
more that will be noticeable as the chunk header is a larger portion
of the overall allocation with those.

I see. Thanks for the explanation.

Kind regards,

Matthias van de Meent
Neon (https://neon.tech)

#18David Rowley
dgrowleyml@gmail.com
In reply to: Matthias van de Meent (#16)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 20 Feb 2024 at 23:52, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

What I meant was that

(char *) block + Bump_BLOCKHDRSZ

vs

((char *) block) + Bump_BLOCKHDRSZ

, when combined with my little experience with pointer addition and
precedence, and a lack of compiler at the ready at that point in time,
I was afraid that "(char *) block + Bump_BLOCKHDRSZ" would be parsed
as "(char *) (block + Bump_BLOCKHDRSZ)", which would get different
offsets across the two statements.
Godbolt has since helped me understand that both are equivalent.

I failed to notice this. I've made them the same regardless to
prevent future questions from being raised about the discrepancy
between the two.

David

#19David Rowley
dgrowleyml@gmail.com
In reply to: Matthias van de Meent (#17)
Re: Add bump memory context type and use it for tuplesorts

On Wed, 21 Feb 2024 at 00:02, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

On Tue, 20 Feb 2024 at 11:02, David Rowley <dgrowleyml@gmail.com> wrote:

On Fri, 26 Jan 2024 at 01:29, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

+    allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+    if (minContextSize != 0)
+        allocSize = Max(allocSize, minContextSize);
+    else
+        allocSize = Max(allocSize, initBlockSize);

Shouldn't this be the following, considering the meaning of "initBlockSize"?

No, we want to make the blocksize exactly initBlockSize if we can. Not
initBlockSize plus all the header stuff. We do it that way for all
the other contexts and I agree that it's a good idea as it keeps the
malloc request sizes powers of 2.

One part of the reason of my comment was that initBlockSize was
ignored in favour of minContextSize if that was configured, regardless
of the value of initBlockSize. Is it deliberately ignored when
minContextSize is set?

Ok, it's a good question. It's to allow finer-grained control over the
initial block as it allows it to be a fixed given size without
affecting the number that we double for the subsequent blocks.

e.g BumpContextCreate(64*1024, 8*1024, 1024*1024);

would make the first block 64K and the next block 16K, followed by
32K, 64K ... 1MB.

Whereas, BumpContextCreate(0, 8*1024, 1024*1024) will start at 8K, 16K ... 1MB.

It seems useful as you might have a good idea of how much memory the
common case has and want to do that without having to allocate
subsequent blocks, but if slightly more memory is required sometimes,
you probably don't want the next malloc to be double the common size,
especially if the common size is large.

Apart from slab.c, this is how all the other contexts work. It seems
best to keep this and not to go against the grain on this as there's
more to consider if we opt to change the context types of existing
contexts.

David

#20David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#19)
4 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

There've been a few changes to the memory allocators in the past week
and some of these changes also need to be applied to bump.c. So, I've
rebased the patches on top of today's master. See attached.

I also re-ran the performance tests to check the allocation
performance against the recently optimised aset, generation and slab
contexts. The attached graph shows the time it took in seconds to
allocate 1GB of memory performing a context reset after 1MB. The
function I ran the test on is in the attached
pg_allocate_memory_test.patch.txt file.

The query I ran was:

select chksz,mtype,pg_allocate_memory_test_reset(chksz,
1024*1024,1024*1024*1024, mtype) from (values(8),(16),(32),(64))
sizes(chksz),(values('aset'),('generation'),('slab'),('bump'))
cxt(mtype) order by mtype,chksz;

David

Attachments:

v4-0001-Introduce-a-bump-memory-allocator.patchtext/plain; charset=US-ASCII; name=v4-0001-Introduce-a-bump-memory-allocator.patchDownload
From 50ecd3fc7095ab8ad3a82d1796ec161a5ef5617b Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 21:49:57 +1300
Subject: [PATCH v4 1/2] Introduce a bump memory allocator

---
 src/backend/nodes/gen_node_support.pl |   2 +-
 src/backend/utils/mmgr/Makefile       |   1 +
 src/backend/utils/mmgr/bump.c         | 818 ++++++++++++++++++++++++++
 src/backend/utils/mmgr/mcxt.c         |  19 +-
 src/backend/utils/mmgr/meson.build    |   1 +
 src/include/nodes/memnodes.h          |   3 +-
 src/include/utils/memutils.h          |   7 +
 src/include/utils/memutils_internal.h |  21 +-
 src/tools/pgindent/typedefs.list      |   2 +
 9 files changed, 865 insertions(+), 9 deletions(-)
 create mode 100644 src/backend/utils/mmgr/bump.c

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 2f0a59bc87..9401f8d7a1 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -149,7 +149,7 @@ my @abstract_types = qw(Node);
 # they otherwise don't participate in node support.
 my @extra_tags = qw(
   IntList OidList XidList
-  AllocSetContext GenerationContext SlabContext
+  AllocSetContext GenerationContext SlabContext BumpContext
   TIDBitmap
   WindowObjectData
 );
diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index dae3432c98..01a1fb8527 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	alignedalloc.o \
 	aset.o \
+	bump.o \
 	dsa.o \
 	freepage.o \
 	generation.o \
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
new file mode 100644
index 0000000000..f98a203a0c
--- /dev/null
+++ b/src/backend/utils/mmgr/bump.c
@@ -0,0 +1,818 @@
+/*-------------------------------------------------------------------------
+ *
+ * bump.c
+ *	  Bump allocator definitions.
+ *
+ * Bump is a MemoryContext implementation designed for memory usages which
+ * require allocating a large number of chunks, none of which ever need to be
+ * pfree'd or realloc'd.  Chunks allocated by this context have no chunk header
+ * and operations which ordinarily require looking at the chunk header cannot
+ * be performed.  For example, pfree, realloc, GetMemoryChunkSpace and
+ * GetMemoryChunkContext are all not possible with bump allocated chunks.  The
+ * only way to release memory allocated by this context type is to reset or
+ * delete the context.
+ *
+ * Portions Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/bump.c
+ *
+ *
+ *	Bump is best suited to cases which require a large number of short-lived
+ *	chunks where performance matters.  Because bump allocated chunks don't
+ *	have a chunk header, it can fit more chunks on each block.  This means we
+ *	can do more with less memory and fewer cache lines.  The reason it's best
+ *	suited for short-lived usages of memory is that ideally, pointers to bump
+ *	allocated chunks won't be visible to a large amount of code.  The more
+ *	code that operates on memory allocated by this allocator, the more chances
+ *	that some code will try to perform a pfree or one of the other operations
+ *	which are made impossible due to the lack of chunk header.  In order to
+ *	to detect accidental usage of the various disallowed operations, we do add
+ *	a MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
+ *	various disallowed functions raise an ERROR.
+ *
+ *	Allocations are MAXALIGNed.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "port/pg_bitutils.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
+
+#define Bump_BLOCKHDRSZ	MAXALIGN(sizeof(BumpBlock))
+
+/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
+#ifdef MEMORY_CONTEXT_CHECKING
+#define Bump_CHUNKHDRSZ	sizeof(MemoryChunk)
+#else
+#define Bump_CHUNKHDRSZ	0
+#endif
+
+#define Bump_CHUNK_FRACTION	8
+
+/* The keeper block is allocated in the same allocation as the set */
+#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
+#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))
+
+typedef struct BumpBlock BumpBlock; /* forward reference */
+
+typedef struct BumpContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* Bump context parameters */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
+
+	dlist_head	blocks;			/* list of blocks with the block currently
+								 * being filled at the head */
+} BumpContext;
+
+/*
+ * BumpBlock
+ *		BumpBlock is the unit of memory that is obtained by bump.c from
+ *		malloc().  It contains zero or more allocations, which are the
+ *		units requested by palloc().
+ */
+struct BumpBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+#ifdef MEMORY_CONTEXT_CHECKING
+	BumpContext *context;		/* pointer back to the owning context */
+#endif
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+/*
+ * BumpIsValid
+ *		True iff set is valid bump context.
+ */
+#define BumpIsValid(set) \
+	(PointerIsValid(set) && IsA(set, BumpContext))
+
+/*
+ * BumpBlockIsValid
+ *		True iff block is valid block of a bump context
+ */
+#define BumpBlockIsValid(block) \
+	(PointerIsValid(block) && BumpIsValid((block)->context))
+
+/*
+ * We always store external chunks on a dedicated block.  This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+	(BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
+
+/* Inlined helper functions */
+static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
+								 Size blksize);
+static inline bool BumpBlockIsEmpty(BumpBlock *block);
+static inline void BumpBlockMarkEmpty(BumpBlock *block);
+static inline Size BumpBlockFreeBytes(BumpBlock *block);
+static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
+
+
+/*
+* BumpContextCreate
+*		Create a new Bump context.
+*
+* parent: parent context, or NULL if top-level context
+* name: name of context (must be statically allocated)
+* minContextSize: minimum context size
+* initBlockSize: initial allocation block size
+* maxBlockSize: maximum allocation block size
+*/
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+				  const char *name,
+				  Size minContextSize,
+				  Size initBlockSize,
+				  Size maxBlockSize)
+{
+	Size		firstBlockSize;
+	Size		allocSize;
+	BumpContext *set;
+	BumpBlock  *block;
+
+	/* ensure MemoryChunk's size is properly maxaligned */
+	StaticAssertDecl(Bump_CHUNKHDRSZ == MAXALIGN(Bump_CHUNKHDRSZ),
+					 "sizeof(MemoryChunk) is not maxaligned");
+
+	/*
+	 * First, validate allocation parameters.  Asserts seem sufficient because
+	 * nobody varies their parameters at runtime.  We somewhat arbitrarily
+	 * enforce a minimum 1K block size.  We restrict the maximum block size to
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+	 * regards to addressing the offset between the chunk and the block that
+	 * the chunk is stored on.  We would be unable to store the offset between
+	 * the chunk and block for any chunks that were beyond
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+	 * larger than this.
+	 */
+	Assert(initBlockSize == MAXALIGN(initBlockSize) &&
+		   initBlockSize >= 1024);
+	Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
+		   maxBlockSize >= initBlockSize &&
+		   AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
+	Assert(minContextSize == 0 ||
+		   (minContextSize == MAXALIGN(minContextSize) &&
+			minContextSize >= 1024 &&
+			minContextSize <= maxBlockSize));
+	Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+
+	/* Determine size of initial block */
+	allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+		Bump_CHUNKHDRSZ;
+	if (minContextSize != 0)
+		allocSize = Max(allocSize, minContextSize);
+	else
+		allocSize = Max(allocSize, initBlockSize);
+
+	/*
+	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
+	 * the context header and its block header follows that.
+	 */
+	set = (BumpContext *) malloc(allocSize);
+	if (set == NULL)
+	{
+		MemoryContextStats(TopMemoryContext);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("Failed while creating memory context \"%s\".",
+						   name)));
+	}
+
+	/*
+	 * Avoid writing code that can fail between here and MemoryContextCreate;
+	 * we'd leak the header if we ereport in this stretch.
+	 */
+	dlist_init(&set->blocks);
+
+	/* Fill in the initial block's block header */
+	block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
+	/* determine the block size and initialize it */
+	firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
+	BumpBlockInit(set, block, firstBlockSize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	/*
+	 * Fill in BumpContext-specific header fields.  The Asserts above should
+	 * ensure that these all fit inside a uint32.
+	 */
+	set->initBlockSize = (uint32) initBlockSize;
+	set->maxBlockSize = (uint32) maxBlockSize;
+	set->nextBlockSize = (uint32) initBlockSize;
+
+	/*
+	 * Compute the allocation chunk size limit for this context.
+	 *
+	 * Limit the maximum size a non-dedicated chunk can be so that we can fit
+	 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
+	 * block.  We must further limit this value so that it's no more than
+	 * MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks larger
+	 * than that value as we store the chunk size in the MemoryChunk 'value'
+	 * field in the call to MemoryChunkSetHdrMask().
+	 */
+	set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
+	while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
+		   (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
+		set->allocChunkLimit >>= 1;
+
+	/* Finally, do the type-independent part of context creation */
+	MemoryContextCreate((MemoryContext) set,
+						T_BumpContext,
+						MCTX_BUMP_ID,
+						parent,
+						name);
+
+	((MemoryContext) set)->mem_allocated = allocSize;
+
+	return (MemoryContext) set;
+}
+
+/*
+ * BumpReset
+ *		Frees all memory which is allocated in the given set.
+ *
+ * The code simply frees all the blocks in the context apart from the keeper
+ * block.
+ */
+void
+BumpReset(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_mutable_iter miter;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	BumpCheck(context);
+#endif
+
+	dlist_foreach_modify(miter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, miter.cur);
+
+		if (IsKeeperBlock(set, block))
+			BumpBlockMarkEmpty(block);
+		else
+			BumpBlockFree(set, block);
+	}
+
+	/* Reset block size allocation sequence, too */
+	set->nextBlockSize = set->initBlockSize;
+
+	/* Ensure there is only 1 item in the dlist */
+	Assert(!dlist_is_empty(&set->blocks));
+	Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
+}
+
+/*
+ * BumpDelete
+ *		Free all memory which is allocated in the given context.
+ */
+void
+BumpDelete(MemoryContext context)
+{
+	/* Reset to release all releasable BumpBlocks */
+	BumpReset(context);
+	/* And free the context header and keeper block */
+	free(context);
+}
+
+/*
+ * Helper for BumpAlloc() that allocates an entire block for the chunk.
+ *
+ * BumpAlloc()'s comment explains why this is separate.
+ */
+pg_noinline
+static void *
+BumpAllocLarge(MemoryContext context, Size size, int flags)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#endif
+	Size		chunk_size;
+	Size		required_size;
+	Size		blksize;
+
+	/* validate 'size' is within the limits for the given 'flags' */
+	MemoryContextCheckSize(context, size, flags);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+	blksize = required_size + Bump_BLOCKHDRSZ;
+
+	block = (BumpBlock *) malloc(blksize);
+	if (block == NULL)
+		return NULL;
+
+	context->mem_allocated += blksize;
+
+	/* the block is completely full */
+	block->freeptr = block->endptr = ((char *) block) + blksize;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* block with a single (used) chunk */
+	block->context = set;
+
+	chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
+
+	/* mark the MemoryChunk as externally managed */
+	MemoryChunkSetHdrMaskExternal(chunk, MCTX_BUMP_ID);
+
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+#endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* add the block to the list of allocated blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
+#endif
+}
+
+/*
+ * Small helper for allocating a new chunk from a chunk, to avoid duplicating
+ * the code between BumpAlloc() and BumpAllocFromNewBlock().
+ */
+static inline void *
+BumpAllocChunkFromBlock(MemoryContext context, BumpBlock *block, Size size,
+						Size chunk_size)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#else
+	void	   *ptr;
+#endif
+
+	/* validate we've been given a block with enough free space */
+	Assert(block != NULL);
+	Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	chunk = (MemoryChunk *) block->freeptr;
+#else
+	ptr = (void *) block->freeptr;
+#endif
+
+	/* point the freeptr beyond this chunk */
+	block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
+	Assert(block->freeptr <= block->endptr);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Prepare to initialize the chunk header. */
+	VALGRIND_MAKE_MEM_UNDEFINED(chunk, Bump_CHUNKHDRSZ);
+
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return ptr;
+#endif							/* MEMORY_CONTEXT_CHECKING */
+}
+
+/*
+ * Helper for BumpAlloc() that allocates a new block and returns a chunk
+ * allocated from it.
+ *
+ * BumpAlloc()'s comment explains why this is separate.
+ */
+pg_noinline
+static void *
+BumpAllocFromNewBlock(MemoryContext context, Size size, int flags,
+					  Size chunk_size)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+	Size		blksize;
+	Size		required_size;
+
+	/*
+	 * The first such block has size initBlockSize, and we double the space in
+	 * each succeeding block, but not more than maxBlockSize.
+	 */
+	blksize = set->nextBlockSize;
+	set->nextBlockSize <<= 1;
+	if (set->nextBlockSize > set->maxBlockSize)
+		set->nextBlockSize = set->maxBlockSize;
+
+	/* we'll need space for the chunk, chunk hdr and block hdr */
+	required_size = chunk_size + Bump_CHUNKHDRSZ + Bump_BLOCKHDRSZ;
+	/* round the size up to the next power of 2 */
+	if (blksize < required_size)
+		blksize = pg_nextpower2_size_t(required_size);
+
+	block = (BumpBlock *) malloc(blksize);
+
+	if (block == NULL)
+		return MemoryContextAllocationFailure(context, size, flags);
+
+	context->mem_allocated += blksize;
+
+	/* initialize the new block */
+	BumpBlockInit(set, block, blksize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	return BumpAllocChunkFromBlock(context, block, size, chunk_size);
+}
+
+/*
+ * BumpAlloc
+ *		Returns a pointer to allocated memory of given size or raises an ERROR
+ *		on allocation failure, or returns NULL when flags contains
+ *		MCXT_ALLOC_NO_OOM.
+ *
+ * No request may exceed:
+ *		MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
+ * All callers use a much-lower limit.
+ *
+ *
+ * Note: when using valgrind, it doesn't matter how the returned allocation
+ * is marked, as mcxt.c will set it to UNDEFINED.
+ * This function should only contain the most common code paths.  Everything
+ * else should be in pg_noinline helper functions, thus avoiding the overhead
+ * of creating a stack frame for the common cases.  Allocating memory is often
+ * a bottleneck in many workloads, so avoiding stack frame setup is
+ * worthwhile.  Helper functions should always directly return the newly
+ * allocated memory so that we can just return that address directly as a tail
+ * call.
+ */
+void *
+BumpAlloc(MemoryContext context, Size size, int flags)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+	Size		chunk_size;
+	Size		required_size;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+
+	/*
+	 * If requested size exceeds maximum for chunks we hand the the request
+	 * off to BumpAllocLarge().
+	 */
+	if (chunk_size > set->allocChunkLimit)
+		return BumpAllocLarge(context, size, flags);
+
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+
+	/*
+	 * Not an oversized chunk.  We try to first make use of the latest block,
+	 * but if there's not enough space in it we must allocate a new block.
+	 */
+	block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
+
+	if (BumpBlockFreeBytes(block) < required_size)
+		return BumpAllocFromNewBlock(context, size, flags, chunk_size);
+
+	/* The current block has space, so just allocate chunk there. */
+	return BumpAllocChunkFromBlock(context, block, size, chunk_size);
+
+}
+
+/*
+ * BumpBlockInit
+ *		Initializes 'block' assuming 'blksize'.  Does not update the context's
+ *		mem_allocated field.
+ */
+static inline void
+BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	block->context = context;
+#endif
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+	block->endptr = ((char *) block) + blksize;
+
+	/* Mark unallocated space NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockIsEmpty
+ *		Returns true iff 'block' contains no chunks
+ */
+static inline bool
+BumpBlockIsEmpty(BumpBlock *block)
+{
+	/* it's empty if the freeptr has not moved */
+	return (block->freeptr == ((char *) block + Bump_BLOCKHDRSZ));
+}
+
+/*
+ * BumpBlockMarkEmpty
+ *		Set a block as empty.  Does not free the block.
+ */
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+	char	   *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
+#endif
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(datastart, block->freeptr - datastart);
+#else
+	/* wipe_mem() would have done this */
+	VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
+#endif
+
+	/* Reset the block, but don't return it to malloc */
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+}
+
+/*
+ * BumpBlockFreeBytes
+ *		Returns the number of bytes free in 'block'
+ */
+static inline Size
+BumpBlockFreeBytes(BumpBlock *block)
+{
+	return (block->endptr - block->freeptr);
+}
+
+/*
+ * BumpBlockFree
+ *		Remove 'block' from 'set' and release the memory consumed by it.
+ */
+static inline void
+BumpBlockFree(BumpContext *set, BumpBlock *block)
+{
+	/* Make sure nobody tries to free the keeper block */
+	Assert(!IsKeeperBlock(set, block));
+
+	/* release the block from the list of blocks */
+	dlist_delete(&block->node);
+
+	((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(block, ((char *) block->endptr - (char *) block));
+#endif
+
+	free(block);
+}
+
+/*
+ * BumpFree
+ *		Unsupported.
+ */
+void
+BumpFree(void *pointer)
+{
+	elog(ERROR, "pfree is not supported by the bump memory allocator");
+}
+
+/*
+ * BumpRealloc
+ *		Unsupported.
+ */
+void *
+BumpRealloc(void *pointer, Size size, int flags)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * BumpGetChunkContext
+ *		Unsupported.
+ */
+MemoryContext
+BumpGetChunkContext(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+* BumpGetChunkSpace
+*		Given a currently-allocated chunk, determine the total space
+*		it occupies (including all memory-allocation overhead).
+*/
+Size
+BumpGetChunkSpace(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
+	return 0;					/* keep compiler quiet */
+}
+
+/*
+ * BumpIsEmpty
+ *		Is a BumpContext empty of any allocated space?
+ */
+bool
+BumpIsEmpty(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		if (!BumpBlockIsEmpty(block))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * BumpStats
+ *		Compute stats about memory consumption of a Bump context.
+ *
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ * print_to_stderr: print stats to stderr if true, elog otherwise.
+ */
+void
+BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+		  void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
+{
+	BumpContext *set = (BumpContext *) context;
+	Size		nblocks = 0;
+	Size		totalspace = 0;
+	Size		freespace = 0;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		nblocks++;
+		totalspace += (block->endptr - (char *) block);
+		freespace += (block->endptr - block->freeptr);
+	}
+
+	if (printfunc)
+	{
+		char		stats_string[200];
+
+		snprintf(stats_string, sizeof(stats_string),
+				 "%zu total in %zu blocks; %zu free; %zu used",
+				 totalspace, nblocks, freespace, totalspace - freespace);
+		printfunc(context, passthru, stats_string, print_to_stderr);
+	}
+
+	if (totals)
+	{
+		totals->nblocks += nblocks;
+		totals->totalspace += totalspace;
+		totals->freespace += freespace;
+	}
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * BumpCheck
+ *		Walk through chunks and check consistency of memory.
+ *
+ * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+ * find yourself in an infinite loop when trouble occurs, because this
+ * routine will be entered again when elog cleanup tries to release memory!
+ */
+void
+BumpCheck(MemoryContext context)
+{
+	BumpContext *bump = (BumpContext *) context;
+	const char *name = context->name;
+	dlist_iter	iter;
+	Size		total_allocated = 0;
+
+	/* walk all blocks in this context */
+	dlist_foreach(iter, &bump->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+		int			nchunks;
+		char	   *ptr;
+		bool		has_external_chunk = false;
+
+		if (IsKeeperBlock(bump, block))
+			total_allocated += block->endptr - (char *) bump;
+		else
+			total_allocated += block->endptr - (char *) block;
+
+		/* check block belongs to the correct context */
+		if (block->context != bump)
+			elog(WARNING, "problem in Bump %s: bogus context link in block %p",
+				 name, block);
+
+		/* now walk through the chunks and count them */
+		nchunks = 0;
+		ptr = ((char *) block) + Bump_BLOCKHDRSZ;
+
+		while (ptr < block->freeptr)
+		{
+			MemoryChunk *chunk = (MemoryChunk *) ptr;
+			BumpBlock  *chunkblock;
+			Size		chunksize;
+
+			/* allow access to the chunk header */
+			VALGRIND_MAKE_MEM_DEFINED(chunk, Bump_CHUNKHDRSZ);
+
+			if (MemoryChunkIsExternal(chunk))
+			{
+				chunkblock = ExternalChunkGetBlock(chunk);
+				chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+				has_external_chunk = true;
+			}
+			else
+			{
+				chunkblock = MemoryChunkGetBlock(chunk);
+				chunksize = MemoryChunkGetValue(chunk);
+			}
+
+			/* move to the next chunk */
+			ptr += (chunksize + Bump_CHUNKHDRSZ);
+
+			nchunks += 1;
+
+			/* chunks have both block and context pointers, so check both */
+			if (chunkblock != block)
+				elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
+					 name, block, chunk);
+		}
+
+		if (has_external_chunk && nchunks > 1)
+			elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
+				 name, block);
+
+	}
+
+	Assert(total_allocated == context->mem_allocated);
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 1a615becae..adc94b8133 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -39,6 +39,20 @@ static Size BogusGetChunkSpace(void *pointer);
  *****************************************************************************/
 
 static const MemoryContextMethods mcxt_methods[] = {
+	/* bump.c */
+	[MCTX_BUMP_ID].alloc = BumpAlloc,
+	[MCTX_BUMP_ID].free_p = BumpFree,
+	[MCTX_BUMP_ID].realloc = BumpRealloc,
+	[MCTX_BUMP_ID].reset = BumpReset,
+	[MCTX_BUMP_ID].delete_context = BumpDelete,
+	[MCTX_BUMP_ID].get_chunk_context = BumpGetChunkContext,
+	[MCTX_BUMP_ID].get_chunk_space = BumpGetChunkSpace,
+	[MCTX_BUMP_ID].is_empty = BumpIsEmpty,
+	[MCTX_BUMP_ID].stats = BumpStats,
+#ifdef MEMORY_CONTEXT_CHECKING
+	[MCTX_BUMP_ID].check = BumpCheck,
+#endif
+
 	/* aset.c */
 	[MCTX_ASET_ID].alloc = AllocSetAlloc,
 	[MCTX_ASET_ID].free_p = AllocSetFree,
@@ -117,11 +131,6 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
 	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
 	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
 };
 
 /*
diff --git a/src/backend/utils/mmgr/meson.build b/src/backend/utils/mmgr/meson.build
index 9dcf990cdc..dd43a6844c 100644
--- a/src/backend/utils/mmgr/meson.build
+++ b/src/backend/utils/mmgr/meson.build
@@ -3,6 +3,7 @@
 backend_sources += files(
   'alignedalloc.c',
   'aset.c',
+  'bump.c',
   'dsa.c',
   'freepage.c',
   'generation.c',
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index edc0257f36..c4c9fd3e3e 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -146,6 +146,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), BumpContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 7fd41d20ca..4f811641ec 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -107,6 +107,13 @@ extern void ProcessLogMemoryContextInterrupt(void);
  * Memory-context-type-specific functions
  */
 
+/* bump.c */
+extern MemoryContext BumpContextCreate(MemoryContext parent,
+									   const char *name,
+									   Size minContextSize,
+									   Size initBlockSize,
+									   Size maxBlockSize);
+
 /* aset.c */
 extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
 												   const char *name,
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index ad1048fd82..29ce027e21 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -18,6 +18,23 @@
 
 #include "utils/memutils.h"
 
+ /* These functions implement the MemoryContext API for the Bump context. */
+extern void *BumpAlloc(MemoryContext context, Size size, int flags);
+extern void BumpFree(void *pointer);
+extern void *BumpRealloc(void *pointer, Size size, int flags);
+extern void BumpReset(MemoryContext context);
+extern void BumpDelete(MemoryContext context);
+extern MemoryContext BumpGetChunkContext(void *pointer);
+extern Size BumpGetChunkSpace(void *pointer);
+extern bool BumpIsEmpty(MemoryContext context);
+extern void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+					  void *passthru, MemoryContextCounters *totals,
+					  bool print_to_stderr);
+#ifdef MEMORY_CONTEXT_CHECKING
+extern void BumpCheck(MemoryContext context);
+#endif
+
+
 /* These functions implement the MemoryContext API for AllocSet context. */
 extern void *AllocSetAlloc(MemoryContext context, Size size, int flags);
 extern void AllocSetFree(void *pointer);
@@ -106,12 +123,12 @@ typedef enum MemoryContextMethodID
 {
 	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
 	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_BUMP_ID,				/* Also glibc malloc'd chunks > 128kB */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID,			/* 111 occurs in wipe_mem'd memory */
+	MCTX_UNUSED3_ID,			/* 111 occurs in wipe_mem'd memory */
 } MemoryContextMethodID;
 
 /*
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 95ae7845d8..8077a70d88 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -334,6 +334,8 @@ BulkInsertState
 BulkInsertStateData
 BulkWriteBuffer
 BulkWriteState
+BumpBlock
+BumpContext
 CACHESIGN
 CAC_state
 CCFastEqualFN
-- 
2.40.1

v4-0002-Use-bump-memory-context-for-tuplesorts.patchtext/plain; charset=US-ASCII; name=v4-0002-Use-bump-memory-context-for-tuplesorts.patchDownload
From 068f2cba7722f3459c9f38543016befabf215791 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 22:23:42 +1300
Subject: [PATCH v4 2/2] Use bump memory context for tuplesorts

---
 src/backend/utils/sort/tuplesort.c         | 52 ++++++++++++----------
 src/backend/utils/sort/tuplesortvariants.c | 38 +++++++++++++---
 src/include/utils/tuplesort.h              | 21 ++++++---
 3 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index f50a9c1a8e..7c4d6dc106 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -191,6 +191,11 @@ struct Tuplesortstate
 								 * tuples to return? */
 	bool		boundUsed;		/* true if we made use of a bounded heap */
 	int			bound;			/* if bounded, the maximum number of tuples */
+	int64		tupleMem;		/* memory consumed by individual tuples.
+								 * storing this separately from what we track
+								 * in availMem allows us to subtract the
+								 * memory consumed by all tuples when dumping
+								 * tuples to tape */
 	int64		availMem;		/* remaining memory available, in bytes */
 	int64		allowedMem;		/* total memory allowed, in bytes */
 	int			maxTapes;		/* max number of input tapes to merge in each
@@ -764,18 +769,18 @@ tuplesort_begin_batch(Tuplesortstate *state)
 	 * in the parent context, not this context, because there is no need to
 	 * free memtuples early.  For bounded sorts, tuples may be pfreed in any
 	 * order, so we use a regular aset.c context so that it can make use of
-	 * free'd memory.  When the sort is not bounded, we make use of a
-	 * generation.c context as this keeps allocations more compact with less
-	 * wastage.  Allocations are also slightly more CPU efficient.
-	 */
-	if (state->base.sortopt & TUPLESORT_ALLOWBOUNDED)
+	 * free'd memory.  When the sort is not bounded, we make use of a bump.c
+	 * context as this keeps allocations more compact with less wastage.
+	 * Allocations are also slightly more CPU efficient.
+	 */
+	if (TupleSortUseBumpTupleCxt(state->base.sortopt))
+		state->base.tuplecontext = BumpContextCreate(state->base.sortcontext,
+													 "Caller tuples",
+													 ALLOCSET_DEFAULT_SIZES);
+	else
 		state->base.tuplecontext = AllocSetContextCreate(state->base.sortcontext,
 														 "Caller tuples",
 														 ALLOCSET_DEFAULT_SIZES);
-	else
-		state->base.tuplecontext = GenerationContextCreate(state->base.sortcontext,
-														   "Caller tuples",
-														   ALLOCSET_DEFAULT_SIZES);
 
 
 	state->status = TSS_INITIAL;
@@ -1181,15 +1186,16 @@ noalloc:
  * Shared code for tuple and datum cases.
  */
 void
-tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev)
+tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple,
+						  bool useAbbrev, Size tuplen)
 {
 	MemoryContext oldcontext = MemoryContextSwitchTo(state->base.sortcontext);
 
 	Assert(!LEADER(state));
 
-	/* Count the size of the out-of-line data */
-	if (tuple->tuple != NULL)
-		USEMEM(state, GetMemoryChunkSpace(tuple->tuple));
+	/* account for the memory used for this tuple */
+	USEMEM(state, tuplen);
+	state->tupleMem += tuplen;
 
 	if (!useAbbrev)
 	{
@@ -2397,13 +2403,6 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 		SortTuple  *stup = &state->memtuples[i];
 
 		WRITETUP(state, state->destTape, stup);
-
-		/*
-		 * Account for freeing the tuple, but no need to do the actual pfree
-		 * since the tuplecontext is being reset after the loop.
-		 */
-		if (stup->tuple != NULL)
-			FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	}
 
 	state->memtupcount = 0;
@@ -2411,12 +2410,19 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 	/*
 	 * Reset tuple memory.  We've freed all of the tuples that we previously
 	 * allocated.  It's important to avoid fragmentation when there is a stark
-	 * change in the sizes of incoming tuples.  Fragmentation due to
-	 * AllocSetFree's bucketing by size class might be particularly bad if
-	 * this step wasn't taken.
+	 * change in the sizes of incoming tuples.  In bounded sorts,
+	 * fragmentation due to AllocSetFree's bucketing by size class might be
+	 * particularly bad if this step wasn't taken.
 	 */
 	MemoryContextReset(state->base.tuplecontext);
 
+	/*
+	 * Now update the memory accounting to subtract the memory used by the
+	 * tuple.
+	 */
+	FREEMEM(state, state->tupleMem);
+	state->tupleMem = 0;
+
 	markrunend(state->destTape);
 
 #ifdef TRACE_SORT
diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c
index 08f9f16262..42293b5095 100644
--- a/src/backend/utils/sort/tuplesortvariants.c
+++ b/src/backend/utils/sort/tuplesortvariants.c
@@ -674,6 +674,7 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 	SortTuple	stup;
 	MinimalTuple tuple;
 	HeapTupleData htup;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tuple = ExecCopySlotMinimalTuple(slot);
@@ -686,9 +687,15 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 							   tupDesc,
 							   &stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -705,6 +712,7 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 	TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tup = heap_copytuple(tup);
@@ -722,10 +730,16 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 								   &stup.isnull1);
 	}
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->haveDatum1 &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -743,6 +757,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 	IndexTuple	tuple;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
+	Size		tuplen;
 
 	stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
 										  isnull, base->tuplecontext);
@@ -754,10 +769,16 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 								RelationGetDescr(arg->indexRel),
 								&stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 }
 
 /*
@@ -770,6 +791,7 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	BrinSortTuple *bstup;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
+	Size		tuplen;
 
 	/* allocate space for the whole BRIN sort tuple */
 	bstup = palloc(BRINSORTTUPLE_SIZE(size));
@@ -781,10 +803,16 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	stup.datum1 = tuple->bt_blkno;
 	stup.isnull1 = false;
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
+	else
+		tuplen = GetMemoryChunkSpace(bstup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -833,7 +861,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
 
 	tuplesort_puttuple_common(state, &stup,
 							  base->tuples &&
-							  base->sortKeys->abbrev_converter && !isNull);
+							  base->sortKeys->abbrev_converter && !isNull, 0);
 
 	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 1013c64dab..e7941a1f09 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -98,6 +98,15 @@ typedef enum
 /* specifies if the tuplesort is able to support bounded sorts */
 #define TUPLESORT_ALLOWBOUNDED			(1 << 1)
 
+/*
+ * For bounded sort, tuples get pfree'd when they fall outside of the bound.
+ * When bounded sorts are not required, we can use a bump context for tuple
+ * allocation as there's no risk that pfree will ever be called for a tuple.
+ * Define a macro to make it easier for code to figure out if we're using a
+ * bump allocator.
+ */
+#define TupleSortUseBumpTupleCxt(opt) (((opt) & TUPLESORT_ALLOWBOUNDED) == 0)
+
 typedef struct TuplesortInstrumentation
 {
 	TuplesortMethod sortMethod; /* sort algorithm used */
@@ -109,10 +118,11 @@ typedef struct TuplesortInstrumentation
  * The objects we actually sort are SortTuple structs.  These contain
  * a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
  * which is a separate palloc chunk --- we assume it is just one chunk and
- * can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
- * column in Datum/nullflag format, and a source/input tape number that
- * tracks which tape each heap element/slot belongs to during merging.
+ * can be freed by a simple pfree() (except during merge, where we use a
+ * simple slab allocator, and during a non-bounded sort where we use a bump
+ * allocator).  SortTuples also contain the tuple's first key column in
+ * Datum/nullflag format, and a source/input tape number that tracks which
+ * tape each heap element/slot belongs to during merging.
  *
  * Storing the first key column lets us save heap_getattr or index_getattr
  * calls during tuple comparisons.  We could extract and save all the key
@@ -367,7 +377,8 @@ extern Tuplesortstate *tuplesort_begin_common(int workMem,
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
 extern bool tuplesort_used_bound(Tuplesortstate *state);
 extern void tuplesort_puttuple_common(Tuplesortstate *state,
-									  SortTuple *tuple, bool useAbbrev);
+									  SortTuple *tuple, bool useAbbrev,
+									  Size tuplen);
 extern void tuplesort_performsort(Tuplesortstate *state);
 extern bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 									  SortTuple *stup);
-- 
2.40.1

pg_allocate_memory_test.patch.txttext/plain; charset=US-ASCII; name=pg_allocate_memory_test.patch.txtDownload
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 4d4a70915b..d99db75679 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -15,8 +15,11 @@
 
 #include "postgres.h"
 
+#include <time.h>
+
 #include "funcapi.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/builtins.h"
@@ -185,3 +188,222 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(true);
 }
+
+typedef struct AllocateTestNext
+{
+	struct AllocateTestNext *next;		/* ptr to the next allocation */
+} AllocateTestNext;
+
+/* #define ALLOCATE_TEST_DEBUG */
+/*
+ * pg_allocate_memory_test
+ *		Used to test the performance of a memory context types
+ */
+Datum
+pg_allocate_memory_test(PG_FUNCTION_ARGS)
+{
+	int32	chunk_size = PG_GETARG_INT32(0);
+	int64	keep_memory = PG_GETARG_INT64(1);
+	int64	total_alloc = PG_GETARG_INT64(2);
+	text   *context_type_text = PG_GETARG_TEXT_PP(3);
+	char   *context_type;
+	int64	curr_memory_use = 0;
+	int64	remaining_alloc_bytes = total_alloc;
+	MemoryContext context;
+	MemoryContext oldContext;
+	AllocateTestNext	   *next_free_ptr = NULL;
+	AllocateTestNext	   *last_alloc = NULL;
+	clock_t	start, end;
+
+	if (chunk_size < sizeof(AllocateTestNext))
+		elog(ERROR, "chunk_size (%d) must be at least %ld bytes", chunk_size,
+			 sizeof(AllocateTestNext));
+	if (keep_memory > total_alloc)
+		elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")",
+			 keep_memory, total_alloc);
+
+	context_type = text_to_cstring(context_type_text);
+
+	start = clock();
+
+	if (strcmp(context_type, "generation") == 0)
+		context = GenerationContextCreate(CurrentMemoryContext,
+										  "pg_allocate_memory_test",
+										  ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "aset") == 0)
+		context = AllocSetContextCreate(CurrentMemoryContext,
+										"pg_allocate_memory_test",
+										ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "slab") == 0)
+		context = SlabContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_MAXSIZE,
+									chunk_size);
+        else if (strcmp(context_type, "bump") == 0)
+                context = BumpContextCreate(CurrentMemoryContext,
+                                                                        "pg_allocate_memory_test",
+                                                                        ALLOCSET_DEFAULT_SIZES);	
+	else
+		elog(ERROR, "context_type must be \"generation\", \"aset\", \"bump\" or \"slab\"");
+
+	oldContext = MemoryContextSwitchTo(context);
+
+	while (remaining_alloc_bytes > 0)
+	{
+		AllocateTestNext *curr_alloc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/* Allocate the memory and update the counters */
+		curr_alloc = (AllocateTestNext *) palloc(chunk_size);
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		/*
+		 * Point the last allocate to this one so that we can free allocations
+		 * starting with the oldest first.
+		 */
+		curr_alloc->next = NULL;
+		if (last_alloc != NULL)
+			last_alloc->next = curr_alloc;
+
+		if (next_free_ptr == NULL)
+		{
+			/*
+			 * Remember the first chunk to free. We will follow the ->next
+			 * pointers to find the next chunk to free when freeing memory
+			 */
+			next_free_ptr = curr_alloc;
+		}
+
+		/*
+		 * If the currently allocated memory has reached or exceeded the amount
+		 * of memory we want to keep allocated at once then we'd better free
+		 * some.  Since all allocations are the same size we only need to free
+		 * one allocation per loop.
+		 */
+		if (curr_memory_use >= keep_memory)
+		{
+			AllocateTestNext	 *next = next_free_ptr->next;
+
+			/* free the memory and update the current memory usage */
+			pfree(next_free_ptr);
+			curr_memory_use -= chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+			elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes);
+#endif
+			/* get the next chunk to free */
+			next_free_ptr = next;
+		}
+
+		if (curr_memory_use > 0)
+			last_alloc = curr_alloc;
+		else
+			last_alloc = NULL;
+	}
+
+	/* cleanup loop -- pfree remaining memory */
+	while (next_free_ptr != NULL)
+	{
+		AllocateTestNext	 *next = next_free_ptr->next;
+
+		/* free the memory and update the current memory usage */
+		pfree(next_free_ptr);
+		curr_memory_use -= chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		next_free_ptr = next;
+	}
+
+	MemoryContextSwitchTo(oldContext);
+
+	end = clock();
+
+	PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC);
+}
+
+Datum
+pg_allocate_memory_test_reset(PG_FUNCTION_ARGS)
+{
+	int32	chunk_size = PG_GETARG_INT32(0);
+	int64	keep_memory = PG_GETARG_INT64(1);
+	int64	total_alloc = PG_GETARG_INT64(2);
+	text   *context_type_text = PG_GETARG_TEXT_PP(3);
+	char   *context_type;
+	int64	curr_memory_use = 0;
+	int64	remaining_alloc_bytes = total_alloc;
+	MemoryContext context;
+	MemoryContext oldContext;
+	clock_t	start, end;
+
+	if (chunk_size < 1)
+		elog(ERROR, "size of chunk must be above 0");
+	if (keep_memory > total_alloc)
+		elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")",
+			 keep_memory, total_alloc);
+
+	context_type = text_to_cstring(context_type_text);
+
+	start = clock();
+
+	if (strcmp(context_type, "generation") == 0)
+		context = GenerationContextCreate(CurrentMemoryContext,
+										  "pg_allocate_memory_test",
+										  ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "aset") == 0)
+		context = AllocSetContextCreate(CurrentMemoryContext,
+										"pg_allocate_memory_test",
+										ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "slab") == 0)
+		context = SlabContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_MAXSIZE,
+									chunk_size);
+	else if (strcmp(context_type, "bump") == 0)
+		context = BumpContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_SIZES);
+	else
+		elog(ERROR, "context_type must be \"generation\", \"aset\", \"bump\" or \"slab\"");
+
+	oldContext = MemoryContextSwitchTo(context);
+
+	while (remaining_alloc_bytes > 0)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		/* Allocate the memory and update the counters */
+		(void) palloc(chunk_size);
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		/*
+		 * If the currently allocated memory has reached or exceeded the amount
+		 * of memory we want to keep allocated at once then reset the context.
+		 */
+		if (curr_memory_use >= keep_memory)
+		{
+			curr_memory_use = 0;
+			MemoryContextReset(context);
+		}
+	}
+
+	MemoryContextSwitchTo(oldContext);
+	MemoryContextDelete(context);
+
+	end = clock();
+
+	PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 291ed876fc..9f39cbe001 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8267,6 +8267,18 @@
   prorettype => 'bool', proargtypes => 'int4',
   prosrc => 'pg_log_backend_memory_contexts' },
 
+# just for testing memory context allocation speed
+{ oid => '9319', descr => 'for testing performance of allocation and freeing',
+  proname => 'pg_allocate_memory_test', provolatile => 'v',
+  prorettype => 'float8', proargtypes => 'int4 int8 int8 text',
+  prosrc => 'pg_allocate_memory_test' },
+
+# just for testing memory context allocation speed
+{ oid => '9320', descr => 'for testing performance of allocation and resetting',
+  proname => 'pg_allocate_memory_test_reset', provolatile => 'v',
+  prorettype => 'float8', proargtypes => 'int4 int8 int8 text',
+  prosrc => 'pg_allocate_memory_test_reset' },
+
 # non-persistent series generator
 { oid => '1066', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
bump_performance_2024-03-05.pngimage/png; name=bump_performance_2024-03-05.pngDownload
�PNG


IHDRt�a�h�sRGB���gAMA���a	pHYs�����e��IDATx^�������wmi�A��pU,(�� �(��b��;^�~�*6T�V�]v�����& �
���l����aCH��&[�<?
;��L&S��y����mI#���$A@ ��H2t��$C@ ��H2t��$C@ ��H2)��x�(�~��'2d�����4ib�]v�}��76f�7��/��:�
�u[�n�������sO�o�������:w�l�w�.�rrr��~�-[�X�v����k��a6c��X��]q���Q#o����cw��'Y���������
�tN�.�}3g�t�����
6���T����U�>�`k���U�\��G���l�����������a�f��7������e��U6z�h�5k�m������8���m���=�<�����!k���'�|�V�^�=�k:��U��DQ���_�|h�j���%g��%6r�H�����v�*U��9��c�����\>R���^z�-ZT`^��s��/�����~��������?�����tDB�_�'�:-\��^�r��^�z.���M��R�:�e,X��z�!w#�_H���<�����eC�����g�G}d������)�������w�io�����_�~{0G��������O�����7�|�6n��M��G���^{��{�9[�v�7�d�\����2���>33�j���� ����C�[��?]?��s������������
6��;w�m�uS����#�R�B��0�/�7o��.:��D����|��A���C�D@'I(���U������>��r���w_KKK�����B�$��������;�%�}�*38i�$W*�0��K��L�������edd�'i��v��{��v�Yg���z��n��Q��&M��C9d��X/��������U:�������~��+��2�z���q���Z�:u�xy�I+��$�d��W ���t��0a��kt������Ov����?�][�R>RA��_~�~��GoL��dl��PA�_~�%����U�,T��E��Trzz��W��7)l�
E��N�P����>;�+�8��Gr�:u����)c�h�^�Ju+e�2�*���{�*�������j��/w�k����8�~���x��z����������[����3���i�2���~�N��^�_~��>-W�/*�>u�T7\����o�W��=z�*��*z����������z��I�	*��j���\r�u�Q�z�{U�-L�������v��������_��D��/^����?*or����-����'�}��\�}�Y�	"@RQQ�O?��������H�T�J���������U��*\6*E����0~@C�U���{���T�;�{�d/��w��2�~�	��R�RA��D�v�y����C�x�cx��./K�+�����������'������Ti*�.tI��>p
=�O���L~�#
�O���)U����*.��h
��4M"�X����p��f��fi���1����45��jg��W^�����{�mGq��.6�u��n���h�]���:dQ~D�B$��;���� z��7����^��������u'R/=~/M~��z"=}�t���-S$�j���3���#�Jp���[���V*ARqJ�"��0TtY��"�~}���8���]K���r|�P-���i������]���G%l�=**Z�B7>PA�\��j�����k�C7�j�?To	Z�H6e��M��U�U���:����]�}�}p�u����8q�k+���z���H�h������G��:�-u<}��w���.�ZO=�P1�Xn�#QU}�JP��O�{��`{���'�P��D�r����_��������c���HEi����z�c��w:tp=$�m���:�����������M�6�c�9�S��(��-��8��o��Z�li]�tq���F�������~�g�a�~�75:�m��i?h?j[�O��U����p������9����+�k�/T:�:����}��i��_��~^�n������sV���@�S���_~��$jk?�{������}���ZF`�,�f*���~��c���6j�(WL^i��������e���<G�y�S�����,p}����g����5C�����
�~�j��;�I'�����uG���s�n�6�x�h�h�����6g��_����~�/�
��q��J�"�~���:V���],�v�����5�*�)l�MZ�w��C�gp���T{Z_�<�>W��H��P�W���w���b����q��ou�U<�1)�9�c���G����h]��t����O4]iLpZ]�|da�!�����~�\y��!��j�=(i��������zj{���=�i]
��(��^�������$:�Q�4*��ej�)}U~V��O
�n��i�)�MoW��?��3�XVz�����P�f����
%x����x�
��F����w�������L+ ��/[��e>taW"��N7�]���%��Q�ET�)%fzR����a%�J88����MTB����?�
���G�c%��O�)SJ��	���B�������zv��t�R���S&@Ae�������(��(} %���)�����U����b�o{���K%��@����X	���n`����Q��������v��/�	<�B	>�t���q�m�>�������.0��K e�>��c������D�u���G��2����#�����������	����4E�P�����a,���;��S���:g�k�n��;�K|��������e��o��<����W�B7�:����U�������](z��L��Q����%Tp6/j[G�e���n���������)�sQ�]����L(���N4�_`:���:�{���G���:��9�~��#Z�Ju3�@I�cH���E
H���i��{�4�|��!����H���K�����h;�FK���~����m�9��kE���?�uM�q|���']3t�y\�,p}�
R��������������nh���k���n-S7=�����
�i?j}�}��gm{��M�������i��_����`z:��E�|�����h��K'%��+��R�t4�����nNS�B�K��j�����X�\�[y=�i��Ui����m��C�g�n���1V�1^�������#�Gy��JW��F(��/�����m��5������x�!=\�v��W�����;�g?�-��[������H(�^���������>��r����Q�P���C%w�w��~�>[P^1P��8��oR:)����zp��Z�]ZV�c3�|����9��J���`P����)#B%�DJd�X	�.�*��R8�b�k������	���2�����X�n]�t��`(�U"��(���+#��G�P�IO.(R����8�.pJ��e��Q�C�����&z����J �[e�t��K���F��n��Z��A�ILmE�/������nd���p���|���\B����AO?��:h���}�f!p�b������km_��X���o�M��[�m5���Z��WFA%]�$p]������nJ��5^�?����]�K���������G/���LUp��?�B]�"�?�N�	��%x�i[���}�m�q������K�w��CO������nh����O�[:�E�[�C�[7s��z��ue��i��J,�V�(��8������~	���:G��j�j���QNb=�
�i,h}D�XAmC���X�����SZ���������cAOu����������K���=�*E����L�:�c�/���O�����j�~�nH��H�K7Q��00�Q��e��h���98�	<��^J;��V���=�
�k���i}��8�7���&(m�w�Z��dp��s\��o���v�SW��7����4^��}��T���[�����F�7�FG��'��h�)���j:��=?�{��i�u<������S�O7����e����x���+��u��as7����
�w?��e���N�X�1�7��&�T�(�|H�t,p��_:����m����U�S�hX��Z�]#�M<��|�G]t��;B]�Q�1^�u,����7�:�����O����i:v���7�X��ZN<����J)?�y��h]���+�������.p���J�q}������:���Ht,��(l>B�`m]�����k}�������m#�?��k��FEK��JG�<���������cm3�gm�P%��N�d�~����#R�5�|�����!��]�/��m�%:eDpB)��h::���\u�U.��'Jx�����D��.�JU�Ue��9]h� )1�$% ���1���(����k\������a%6�T(��^,�����[E�7�8������.�8�����P�]��>������7������������o?	����jV�@�.����JZ�.V~7��zh����h��������o[	����T�B�����/G7���N%����GKw-K�����g��k��_a}�.v�����'������:�FI�p7��s������S5e�t<���?�^z���q��8���H�H���
=��rC�?e���t�(3���:k�>�v�8-G��2l*I���?�t�&G��>��u\���z:(���1E��g�:n���X�{D�h\�������D�
�����h�t.$Z`�1���8��������h�����h�{��;u<k^e�u(���[7����h����G�U��.��e��,���	Z'W���]����Z�c������n
C�1Z�2�:>������Y��xi��aQqwe`�Z���,����u,�\TZ�}���m�����y�mx��q�L�h��B������4Z�@�������������v�:^{�������G�K���mg���K�u���Q�t��5����^���P�~z)-��A����������:Fuc�W�
�������At>��\���!��� ����JD�MZ������!����i�
2n3]����:�tm��h�e(���:f�k��!������:��sD���m��������~�N�M���?���J���d��[��4Z�CyD��_�;��������T��5o`����s$�|��}��>Z?�����mq�5bI��)���s��������C���3�<����K+
�wD�����kmc*=���A��������#���$eT�?���n�uq�)��z��B%�����*?�VB���`�Lj��E7�����hz��'�Zo]<C��A�,�z�wD�cs4�~�nhBs��N�0�%���"���p mg%t�ev�'�h�f�
���b�_0���b�*S����t�)���.�������EG%�e���oZ�2~�.<���wl��U�����p�O�]�@� �6�������
u�(��q��2���DtO�)�9�����HO�un�o�n��vD�V��)��(*���?������_�~����o{7���^z��!�WAu��o�	�o�|�)��`*�
\o
�F(������8�KcD�����y��O7+:OB�w��
<j����O��n���{�=��J����Q����ej��[�#��J�Ce�EA4�U7��]�����x�t�����P�A���w��~�>���?V�n���)��m�k������4m����%[������-�6����)Q�G7|>��J�^[�`�O��V����:GR�&��)o����N
��*����E7��4�?_�����D�D+�|D4��\��F�4�:�t��<����k���P�G$�.O<�D����,����]Ub���C�X����"������fX�t!T��@��U����OJ]�E.=E'0����$�2�ZW]��	���[��F�Us��	E��n�D����
E��2%�����@F8z�n��I�Q���$�2uz�$:F
*����[���8v������~���J^����uU`P��[��6����=�o4t���E�P����q�>�9���}��h���(���\��8�'�^����(�xCWehCev�_�/^�����}�u��p���\?��m��B�J�����.�4(������
[�4F���PI��_ �?��q0;�@����&�����Y��6��k8�f
��~����v�t�����=*���U%������n�����u,�v���4!�M�����|D�����k�n�B����_���*m��xJ��_�H�����@���`~���tmQ�#���F����8Gt��
��t���
�Jd:��*����*9���z@&:U�3�o�Wq�#�Q�j�4�4�v�������M�����d[Q�q��40=A�A@^����K��p7�
t���I���{���/��$�.��1���;���@J4���U=��7�h(8�vc����bD��(��?5�v�MV���������>�Lm(�.�z��gpUR%����)��������^���l4T|T�4n��V�/���j]t��g58�glE���k��PO�i[�`}V�'�|(�'-G���������:�iU��������S����$Qu����*m�qA"�IEE%ts�Lh�K�c�CVt��U�R��E���$Q�_Q	��=��)
�����7���
J�:���2��uM�K��P��7��	x]���b����S����M�H7~�m�W�t>T:]���:�x�6�w�F ��K8�f)\� Z�����~�����N��W�Ki�|�A�}��������S��P��`��~�"�x�
���y�t�EZ��-�PV����8G�{#U�	<�K6E�(�!�U=Iy-_%���J�R%��	�D�����(�s5RU�x��t>��u�j��
`�K���h
�7�K�����1T���_,��bW��Z������&F�?U'8�+�	��,
�@�B��#���>��P�@�"���2��dE���,Q��HAm?(���F�-���B��~b]Zh����h/�z�"�46�����,W�'w�u�+>z�w��}U�~�/��Ht\�7H�o�$0��}��K-��f�,'m?�q��v������V�;����4������i��S���^��#�><���]�\�/��p�
7�cTO[�q��T�Q7��H���l������I����|�7��pq�M��� ��h�W�k��e���O��0��X�o�r�DD�^��s��s_�j��f��9�:�R&���u��u���A��sI7B��\2�_�������9�x�?�^���)JE��c`�*��w��y/.E���FQ������@�:R�+�W���_�^��Q�QU7C�M�R O%�v��w�-#��F�@@����>�J�(���X`�B���X�T=I�#�Jx�`����[�'�m���Q8p��r_�t���l��Cp���$4V�8��x�(3���<��9��
|"��Q�WtlF��/KK���K���*��W�*M�R�M7�j�T��( �MIi:�Y�O�i?��L�*�LO�u��n>��>D�P�PP��hh}�������Q��jW#������P0H��nF�1�H���(�s$�<>���TIY�GG�p���70���xi�����������`{WOKK:(qJ ����'i��QN4���@���1�&q�Yg����4B��{��uQt-C�Y��������DOdB��p/�$���(M�yjI�O���FO�t���H���,������*���P���i���_Z"^��*����
p)�������K�����J��v�i	�����8z��W�PU��@�����E�l
��6��r��$��:�Co�^�Ul�C<�_�MSu��k��b��B���kX�Qq>�W�j��{�iq`���FU����zS��*������>��Tu�P��dJ��M���(�s$^�L��7U)3����������GDcW9W}:�����jW~u+�s�W)j��*)z8�8=J�%�~�A7�%]�_.�����zY�EJi�{�b�����F�T?U�W����.�*��b�m�_�.~$>�)�?�Ui�hJ�ne��3���s*��m��+*Eq��7o��GO9bY^��l��DSZB�B�D���������`z����75��ej���2��>}��vG+���2Ej���P:��:>b���:���n������d�����MI���c�Y?�n��9]�TU�CU}�E���?�%hY�?*]t�%��=�����(�S:(*����$Y��xh���N4��0�_����<G
�(�!-C
!��T�@����\e����GDcW<W�[}m��jW
�k;���H�d&��~:*�L�?:(1~�N��5�?}(	J$�edu�
w����S�h� ���z=%��zR�@�n?p����Nv�=~0�p�1Si	������PT��AU)*�r
�������E��_��.e:t��E}����,G:�����^{�e�����3uk�W5QF;�u�J�y-mgu��c����c�����tA��3PI���lo�t,�=�f
*�7C�q���~����L<�tm�C���J/�Q�C��n�Z������A�o���k�Ni`���~��k�(�����k��%c���4"R@@���)�x�c%y�F��!�*��S�pP�����T�;Tw��R���h�j�j U���$�v������7�X�	�H�l(����������5�.�jE_��T�W�D��AEJE���'C���-��k�:t���������@J ���J��X��75X�jDj.����B�h=�]��"e���������������W�@��'?��(ZWeBE7����6��������o8��������nN�o��I���,�����X�Ei:�cq��o���� /(����}����{cC��9�g�U�,�z��:����V���_Q�p .����~��G�z��h�t=q�o�B��0u�T7�t�OO����_
G�"�I
kj�����~��~o@��AV5r�*j�C��i_����������f�(������~�~�9�f��j�#��(H����>Gb��tHA,?�s���Nr����.h��k��P���h�j�j �o��UpT��~q�EU����~�
����gT�b��a!3��H|���.A�E[O��-^������(C�^2t�������+],|�<�R���z�����Lj`&T����o�h��q��q;\X}j�Y-�k]��������j]UR�d)`��G�t����������~K��mc��|���>Cet����N`�����(>�u=�����_��&
�o�6��e�`�i9��Z��WE���0i�k���)�����.�~�R7J�J�(���g����)���P��$��x�8Q�X�
�n~�awS�>���t���W�h��*�\j������h$��KF��5�W�CJ�C�4N�Bh�
��
N'e��Rb:����j��^�����<Z:��t�c!T�3EA���$+�����AiU����:��fk;i{�����Ki��B���t��*���q���E�~�J+�EJg��������=z���Bw��J#�Y�x�c%}�$��IT:��FJ�5�~��W:-:�����k���K&��s(���FQ�#�Q����W�������������7�|s{��Y��eI�� ����X��sA�S%t~�������UQ�p�H����.��|���TzBO�U|P#%�
`�)�dE�u����KT�P
��O=��3�J�t���GA%���O)�}jWC�tS���h)S�M�����y��QA�O����~�2)z���:~i=����Z���?��.�J���Ju���/��BY��)��e�t���P&[��Xn"EW�f��~��jy��Z�f�k;j�jk_+������.�����h�(#��V�R6���P�M�Z��?��~��'��y�~�>>6%�q+��� �<�-Z�^������y�7]�}�w��S��:N4�~:�hP�J�:��d��>p�U����E�K�A���)#:~�,mk��L�Im+e���~��A�����p���m�}�U;�w��7��*m�[Z��Q�q�q�-�n�-��Jp����;�����mmwmW�t,�����_z���''����COd�
�u��>��u\DK��u"�~C��J�S�V�S��Jm��SZ�c�?�u���m+Ua���h�����u��X����D�g�9��P����:o����(x�W��^�5mw-C����	'�W��h����O�t�k��z�s.����S'w|��_�F
w�����V��oOmCmS�u~�#jSMi�$2�*(��H�����g,�:�q�m���R|�q��^]c����k�(��	w^F�uLq�DJ7�t������M������z��u����������q���>����1�����m�����6��o$��G��p�nq�5bI����O�k���W�@���?D���
�z�/tLh{��G�l�K�:����wj��S:����NQPB,R")#(u%�JL� ���\ ��G�u1=��EO�T*��*�)aR�F=Q��?0� ~"���w�b�������~�."����������/J�{���_��T`Gm����u�:�	��U����~����v�&��.����\�S�)m���UK�w��Q������2$�o����a�m�������9�j���>�t,��+m������o�6�t�F���'zB���
��~�,�7����;e6B��5^���[����b��-��k��:�hziz�m���>���.��MWM�(��F�o��<'���@:�������_w��w(]�w��l�+p�E����L���m�e�z��4�hjhi��*Q����@a�I�u�WPZ����%t���C��<	>�E��������q�MA������?��k=����	>�E���W��@�=�E7�:.�j!:�MuV,7K~':V���U��+�W-���Oi�z��q�����~���2�\m�6�����v
���+����|H����.��S�@Al�?�������m 
R+�eE:/#����]�1�������#����NA�O�o�I�t�+��|��7����]�Ci�����+���A�M���M�L�}��X�����P��������k��F�+�����@	5��=���6�1�|�s2��4p_�����	|i~�):��cl�@(Yt����`�����q�W�L	�.�J�E]�Ug��3�(�wE���n�ua�w�B��~�.�*�����BbM�xE�UXAo���t*A�E]���jF��4�g�b���2=�enT\�Tt�N���i�T�zq
.��v�&�ev��o�6���"+z*���OO����W��P�X�Wj[h\ah{�F[���E�M��?�(��{h_ih=�o���t��m��w����_�C�C����@Z��Y�k�:���~�2�g�y�+�|��&e
����S�KO�O=�T��4�~����MeN}����:����-������S�Q�[�u^k��������2�D�w�x���� ����tl�*��{a%�����������D�V)�4F�Q������c_O�O9���m���8JD@G�l�_�|�s�?W����`��:t^�	��yz��Xo�t�*�P)S
��:��z�_tc�>�1�y"�s��
���+����|H����.������*��>S������P�B���K-K�����I��
{����&��t���|�M���T�H�Ci�����!}N�T�k��7�6��,�n����#Z��a?�Z�8�7�������5��G��?���n^�v���6:O��3������ :wuNi_��+%sJ��m��+V ���U�����W^���>�,��0�����k\�P��8�����I��	,���OA����1���dC(*)��9�R*�(}��BTd_
��m
U�P�%�}���j�v�N��H(�&�v\�W�tT�G���U��8��FG�V�V	9���]�j�qG��RYo���kKC�\����U��8uc�F�U�G���.L�9��G@������!C�wy���n��N��P�_�}�5*b�+{�[�j������@���Q�9�z���T2���&U�����]����F���Q<���^��%��t#��dh ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t�LJ�6�0�i���c9	N���!.^�r6n��e{�#�Q�T�kR��h��zo#%����y����`�bo(1R��wM����$:v	�|�iO���K�v�J������Cq���[{��w����f�l�9��d������,�]"�X��[J���{�������7M��%�YMO�>������ �y�ggI0G&����&����GzC����m�x�{�d�3�:o(�r-{v/o(9/��N�s��y���?�x�����2o��o�h,_[�:fff����o?���7�d��_g�7z�/��e�P|�{�=�c�=��G���H�l����������
�8-^��^~�e��y�7�l��Uv������w%�6�����?��{����������{c�������@�>�������-[�xc��,\�����*�={�7#�(Pvv�7EOiNVV��._�5l��a��avet�(E�����C��.�}����p�G�M7�dS�N�����Z�b�=����K7�`W_}�}�����P�?����3-��C�i��
|�A��{��n~�U��,[�����n���o/��r���ow���6�FH��j7B�f������>}��cA������~r�V��2��u�����7�|s�c%py��r�
7������c��6�����,|��w���-���C�1��3r���i�^��P�y��0`�w�i��g��sm� ��<k�,���+�����������O{�����3�t�������y��������\���������>��w���P����ei��|�wh��\�_�7���y��k���"l���R���M���qmOK�=��;���S����������M�H�&���(%t#r���#
z��v�QG�io����w�y����p���.��b����l��Mv��G����_~�2�O>��N7m
�s�9�3�W�v�Q�n]���v����+��:�����)��e���;*W���#:?���kD7��)����e�F����:��~���UK
K�<�4��Wv�AY�z����?v7�
�u�]v�=�X������6m��x�����/��Z7a����G�������{�io���;V>����n����G}������������.��w��*�����n�>�`��4N�4O(J�x����Q�Fn���7�s=�P\�C��m��fs��u�����z��n����O?����^w��4��#�t���s���j���n^�V����R���FZ����n�����Z'��1Zf�J������p�	;���_~����<�j����[ik�~�\z�f�oND�Ai��q{�i?��4h� w�+�Wk�<��s��O?u�����I���M7�c��:���@I#���2�zz��x����kWjB/
+c�~�z�Tr����3���
�n<�q�y~�����f���FQO�u���Sj=������������M�2��Z��������K�����g�q7cr�������VDo��q�Fq��1n�h{��������'���������h��;�z��e���]w�u�o�zh��ktL����i���+(��+W����;VJK�g�@%�y���6�������
�n6�V�E��w�}�fT�@�N�B���k�q�^x���7�*�r����@�>�}>|�p(�����5}���k����O>q�QP����������O��.�&Mr�8tL�1b{���]v�;��������� 
E��uS����}�u�68p�M��s��J�t��o��4T�ZODG%��ou�PPY�O�A�P��}]�+�����_}�U����E��q��`�f��q|�Hq��P��@)�d��4\O�O>�dWz���SN9���m���o��n���uc��E��^���~����������n�z����V������n�z��m.PDU��Q�=�W�L��9������{��Dm�O<����~����;�Z������C�N������n���$����U���qv����cE7��q����i�����v���9�}���JV�
�n`;u��J���A4|��������},�x�>W�N�mTZF�I��It|���?�T�O�O��n�TRp&0�����-ZT�s@��:)���9�V���o��]��
t�����OCe��9�/
�:�U�L�5��%�s��g��u���i����h]t��E���*q��x���3���F@JUi��k=I�Z��
*x������j��S�r�V�P�-���FJ��U;**��L�n��������M���C7��*L7,��L���2��!�Wi�����-����-����������Z����]0���������a��U��,�6c��o��O�TA��t*���]������6����7}�{���N�:��|
���J��@�t,���+)��w�v������U]�����=��Z`���o7���uJ���&*�8�6��J���4�4���[])/���t�V�X�{���Lo����RH
�._������]�������sO��<*I�'���"��O 7l��n|4^7Wj�'����)�������iSwL7�z������M"�
�Pj���n��
���4�J�i�i�����_�EH��}��wB��z����TC0�!J��x���P��`�m���{�Q�j8�P���IWZn�.�
*1�r�Jol�����4h��B�TuM�O�^�(j#I��TB�0�+��v�T}X��T
K���)��J(%t#�L��]��DE��v�n�t����~6����JW�JU.����1h�����w�_e��W�"�o�R��t#���$�xKO z~I*��U�=9���P��^���L{��
�J��:��'��XE���hH������k��}���xRQ����U�P'
jL��)K�U���J������}��gC���@�H��P0G=v(����s�%U�z����
����aC�Q��j$U��(#�Rz��V��_O�U�]�4�n�"�Bb�TZ7���(�T7)>*��0��	?���8�y�R���*��A%!�v��%���������%Ot��P�n��cj��8H�2�TzT=�i�1c5t��
�-(����=j��K/u���%OU*G=����'��[�(T�@=z�

����X�]�n���
����W#��e���H�_��*��Aq�����B=�V���R�#�����/jj�%U[QiUKQ;.�L�Ct�i�W��-]�t��=T�����o�X�!E�>((��P�Vu������Af�U"I��0�p��Q	�K��W�[U�����"�c�_��������)M	�}1����L=���6�-��~*�����u����Lo��#���������jXTU*��y eZ��UW��71jt��w��F[71���"���y�.�����Us�
$CO�C� �`���~��M�n:C���&E���5U���F�����V��R��`��U��f�v�7�j7+Tw�:g�U�7�1������R/�U���m��ynX��{��i���������j�V�7Bu���Qu1�����d�y���������)���S ���tGm�h�y�s(�X�[Hvt���������a�M���Pu,�hR���BX�{t��S�e�u��Fm���<��k�R�n=�l8Y���{�ez�pd����H����x���]["�L�kU�SG�J����
wj�����_U�A7��?��{_\���������P���D���i�}�f���m�m��L��}��g��L=K)��cH������i��Q-�D��T�K�eQ@%��l��z�*�xV��Ri�(�������c���_��=��x�!��F��S=��S6k���
���+��&
�/q�4��~�������@�C8������@)�.�U���/����;��$�s�9�^Q���zQ-~i����2�<���j��5���iy���+��������~��<�j�2���X����N��{��S�\v1�������C����*ws�*�~��r���������w>�h��C7==��kT���O��=��R���EO����X9��]�>Q�O����������������������z��{Q���S���zw^*p�>���9���o���=�x6}GA��N:��t)=�����y5�{�	'�e�4Z`IC���A:��>P��`Z���Jf���?oT�Q�\���B(:��j/G��t�j�j?(Qw��nu�5�����Q�P��J"j_���_���*YjY4,���(T�F=��Y71���*
�|��GnZ��m�MI`!j�Bm
(��k=��
�n������&-P�6m����v�����_�����<x�N��������p}����Xn�T-I�����n4�N�z�Q�"�Y��E�B
���_��3u�TW����_�+���X{�R�
G:�t��R�UM%w�=�Xw���h[��R����z��6��U�Wj[&��*��Dtl�|�y��W���#���P��!�:�u�����.=z�p���v�m��P��:7|*�q�
7��U�T�^����������P��M�6�������WP���h)���J�)���B�A��������F���x�{���W��N
j+mT�F��3f�T�8���Y�����[
�2��)[��1����;��r���?�F���_g�t���K��*U��;�g�+Q�u���R-g�e}Q����V��cv��XT��W�^�����Ny������.��v������;@2 D
P�T�j���
�J�S{Y~�bj�
� t�@�v9[���S�jHXU��x����(���_{�5W�R�a���2��=���������JT��[z��b��!$��
�R���&�R�&�����P��
��qW5��;j��w����z5��v��`���oH8Z����p@�F:v	_���7��j�	l�7g[���U�����7�)������G-{�|oLb�7?������9�1���jCg�������M��n�7&1R����}3K������Wiu����������U��
���9w��3o���&�AB��
v����1{_�y�� ��d�r�d�$:I��@�!��d�$:I��@�!��d�$:I��@�!��d�$:I��@�!��d�$:I��@�!��d�$:I��@�!��d�$:QZ�|������eKKII�����n��F[�p�7Gl��i9Z^�&M��n�9s�xs�,''����������U��U�f'�x������e�o.P���n�
#�?������9r�5n���5kf�/v��-Z��O?mm����.��Y���K/�I�&m_��+l��i���?l��v������m�����W/[�n��i����+g��Ow�����^�+W�>�*J�`��M���`��\0���>�3f�/�`,�|������
l��A.�s�w�/����7e�>|��G�E��a�������7w�~��w6~�x�����
<�F���
�2:����\�&������U�T���P��]|��������o_��_������	�������%jRSS����>�MW����T
�(���Ok���7�l�������{���Y�j�7�Ut
����%K�K�.V�F
ol���t7^������t���z����ju����+zc�hy�5r��%t�.]�J�t���U�
��iSk���+��C�l#�����m��ynx���w���W���3s�L�)H������vs�$���s��K�)���B
����n��|.��f����e�`Y�l�5h� lc�
���_������������>v����*Zw�u������v���]���y���]���F��
U�Z���	EU��:�}t"���r�U��R��76>
���?��!C���1c\�7j?�z��.���gO��r�:u�O�U�U�*_���.:�L�q�N�j�=��k'g�}����;����7|���=��BI�U�!�����?�U�z���\�%X4�R#��_�|�I;��]	��i��Y�~�\���<�������b��
�.���������g�E3O8
0��-��kJ�D��M�n�q�F��N$�J�����-���{���N:�$�nZ�li>������t$��l�������p8���e%t"P/W7�x�=��S��W_Y��)����cg�}�kY�dj���M��_�����r�����|�vu�u����cm��.����U-K���c��^���}��v������C��e%t"�X���*\f��������m����ys�U��_���n������A��P_�z�HA�_���*H�:h�������U�Vyc����/���
�o�����Q����~r���*��o���)S\G�+*�s�a���Ri�`�g��i���rPv�)�+g�q���������
�x������������SNq��(��e5n�����^������f���J����������5j��M�P��{��
4�&O��=�`��������9(�hC'

������1c���J�f�l������-Z��O?mm�������������m�hs�7,���r�J�+���v����o���z��\��{/
���+oT�J�q��+g��Ow�5^�������N��/_n/���k�X��+��kW����5i���+_���h���=�^x�9r�)�s�G�5�\cG}����������@7n�kcGU����
����+��>:I�6t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t��$C@ ��H2t�LJ�6�0���4��������m3����R���}���6��i}��P��H����m�-Y%sZUHO��/�HP�8���R���
���[�(��	�jV%m��,oPV������d�oPf�H2t��$C@ ��H2t��$C@ ��H2t��$C@ �I@g����|�r�<y�{iX���trrr�����n����o?�Z����[�?�p�����l����y��.%wo8fYYY��_��w�m_��76���ju��q��-�9s��a_�.]���o���>������@r�4p�7T�����
��B���={��v�i��sg�����W�^6z�h[�l�egg�?�����y5N�>��c����]U���;�-c����RP��:*�����Z�v�l��5.@3k�,{���]pG�rRSw^��iZ��]��g���~���
����S'�L-�����8���>��h*U��M�^�j�����s�x����_-�����3�+����t�J��*�$�����'
�s�F��+�w��e�(X��M�6���>j��z�-^����w@g����p�B;���v���X��:+V�
�.�7o���@Q�;�S�re����l��6p�@W�J�&�h���U�������Cm�������zc���i��&�Q�F6h� �Q��7(��
P���w���?�F���)X�.]l��aV�V-oP6���:�^�f����Y�{�����M9�:��w@@>:����"2�������8q�m����S�������:�r�����]w�e_��������a7�p��+W�`5k�t����:��]7�r=h��S��Z��-b��YJ�����fK�z�7�GBJ�dee�SO=e��~��T�Z�����M5��y���|���v�5���U��)ve
�dO�`�/�`�d�v���{�:@�IH@g��I�����>�`W�j��%v�i�yS�uO��/��^ho���}��'��2W2'g���l�n��d��dgg���#����>jGqD������o��z��h�����[Wj�.n���@��]1���w@g������?Z�l�}������aCk�����?�6l(m�(9�Z�*����$��:jYm������tolh)))�����@a��)_��ky��E��Y�z�+����9�.��N�*U��C�Q�F�W�^�U����^��c����9�.����Q���v��Y��=�O�>6o�<���t�7m������������5�|��'�� v)����������k���f������M~���\�@Y�i`�h�{\���P��5��7T��;m�� 9�]B���m[��������+�H��������C0 N	+�Lm��[��*W�L��ePB'6���������-[l���6}�t�����n�AJO�5j����o�����y[�~�7��;�������g���r8�>t�P{��w] ��9��Zk���+V���U�\�*kV�\�}|��NVI��IMMX�=�������/���]u�U����v�mv�wXZZ�7(hC'6���S�F��/_n���m����u�V�Uy��U�Y�fK��+W��9����K�f���X�� �:P8q�r��T���5i��y��T��7����
(����R��k��k��{r�bw@GU�����Y�v��<5���w�Qc��]�u?���W_}U`@G��L���������&�t���[��--%%���o?����o)m7u�~��'Z�j��2�;�8{��7l��M�\;�6���/�G�V�V-�9}�n��������2?����^���_�n��{����A���k#V��6m�=��c��ukWR�z�������?�p��F�i�7v�?/^�����c-Z����~���m��]�
6X�>}����r�Ir�!n�J:i{����
d�+�����>t�P������M�6��i�3���w�������NlhC
�P�"���v���@Ba<�����woW*��Si_�x�	�V�����J�(�����d���>ku���>^VV�=��C��o_�
��_����`��]8R����/w�e��	��i����OZ�V��x�3������{��9�7%��Nl�@�*��&o������?.�1|�p�
�	'��J�����f�v�+�R�Bol�����[���]��_|�j���M���q�6p�@W����O�����/��Yg��3C��=�������>o���n�X��)����������rT
,xQ�������@���n�g�y����jol>��9���]I��SPO_*���[����E�����s�6l�������<7n�������1b���;(~tbC@
'�F�UD
�>���4IY�y�f�7o������`���s����9��*hZ�J���!�T���%K\�EM�6��v���/##�5j���={�7�UqtRSS]PG
�MU��<y����R(���w
�����I���w��e.�����k��SK�.uU�"Q�4���k�����C�J��t��c���~��r�:(X�������;�{���_�����:>+T=I%E?�p���+\w�
�dff���O9���*A���)u���*U�xcO��k�*Q��M������s�fp�6j�(;���\�<
����?
�T��}		�(��.�U�D����!�T�h���6f��%����M���Z����;v�k������k$���>�o����lj���kd���_�8�7eTB:
������V����>r�B�T���W_u�~������{Sv-���������^�zm����jjWG�U�R	���W�i����J�����k������[�����;��Im��W�Z�j�y����[��y���\���LU����:�8q����;v��{S���|����rW������=���?�xoJ�����m����T	���;[�~�\W���=���\�B�/S�N���>����J��.�Q������oI���DoJ��^��.����zxj���5l���;��7�����Oi�5j�X
:�+-�@�DV#�*����Q{<.��;j$9��A/y�5,�7��6�@�w	����j�R�JaK��_��7�m ��u��W{c���g������������n�����>:t��n��:jo�/����4N��v�}������%��6��#v���xu���uI�?t�l�M��r�P�J�����w	pT2G�t��D%Y�O�������)V�6�&� K �X���_��������Ju�����Q���k�zS�M�6�&M�dt��^Q��j_G���_�~��7�2e�k�����]qtTuH��������J�J�<���.X��K{�9>V�8����������TC��v��+�
UM;������^s%u� �
J����o���?��s�7����{��a�7�A�����U�j��f������j_ ��]�JTMH���������J�+�s��w��y�\/X
��z������Z�z��O�~jCG]��T��*��5s���3�u����O[��m���L�0�y�=����
��H�9�j������h*}��;���*\����'�J����7��
����x�{����dP�*6T���IH@G�P"R��jk��'�p�d����R��w����T��]]����K��������y��7l��q.��`��W^iGq����\x*''����+@�g�n����]q���[��]�����
(��tD�����;�Q�C�G8��o��\���:*i��
��Nl�@�$4�������N��"��~�z�����@�$4��p�B���\�2j��v���7+U�����m��%��(��tTkK
��j��y��@r ���_?;��cl����XFB:�~������
4����O���v����n���_�e}���Y�f�y��:����������+m���v�M7����������t�g�}��;��g�y�f�����V���;��a��;w�u�����oo)))��i��'�h�w�����v�Zo
bw@'''����\��*�I���n��n~}��;�����7o�J���U$*���5�>����)_���w�y�`�{��'l��u��m�����y� �g��>�����+�(03m�4��y�7&�z����[�u��v��������M1[�x�
6�F���/?����e��V�Bo�l�4p�7T�����
�nY��yC%+��Vo�C��+V������3���K��U��7(����N����������Y���]�����.�*U�xc����Nl�@����������Qd/:I��@�!��d�$:I��@�)��NNN��Z��������(��t6l�`o���=��#�����-Z���<�L�Y��{y��6q�D7
�����������/�s�=��M�f[�lq�������oU�V��u������?��������'��t>��#{����[�nv�UWY���m���6v�Xk���M�4��,YbC���3g��#,77��4bw@g�����?�Ga�?���o��RSS��_~�3f���k����w��'[�.]l����~�zo	�E���3�|;��C�~��n\vv�M�2�
�j����/����V�Z�l����� 6	k9�z�R[:��5��:��D�;�S�R%k�����J�ud��96a��~N����8Y�t��n��I����� qt�i����5�h#G��������[��)�^���O�"k�Hn��� v	�r����:�,{��G���:��z��w�yn��5k�w����������������U���O>�o���>�`����kY������E���cu��q�������j�%m\���P��5��7T��;m�� 9$,���N��@�I/W(:1��Y�~�
:�
_p�������h��d}�J�*��l��Nl(��s@g��v�����a����z?z�h7�.]����m�e��������@tbC@
�F��"�:P84��d�$:I��@�!��d�$��tf��m+V����(����i�=���v���������(*qt6n�h.�<�j����@Q�;�S�bEk���-[��6o���@Q�;�S�re����l��6p�@W�*''��
�DK���.�������Cm�������zc���ic��U����Q�F6h� �Q��7(:
��
��q�+{C�[��r�P�J�����w@G�[���6z�hoL��t�b��
�Z�jyc����Nl�@���Q��5k��T�*55��W���e	������;� �����)�"2j[g��U4��`	
�,\��n��k���U�Z��=�\���i����/��-[��P		������~j;v�Gy�-Z�M������3���+�o��.���IH@g�����_?7�R8*����O�un��&k�������_{S��:*�������������.�����kiii���$5��8�{��G�f��6j�(Wj��;��v�Z�1c�u���m"�w�}�C�6w�\���t��l��dee��u��V�ZV�|yolh�������>C�W�w@GA���b���Qm����-[������:U�T�����F�m�&M�����	k���n~}��;�����<�L�s�=�w��6t����%W;;�>������5�|�)���h2�����3q�"�z�-����l����Xs��z����Y��{U�z��g��s����7(K:
,�}��_�*������JVz���$��K���3g�}�}��gv�Yg���(��s�u�f#F� �����	�*Wj$���~sU����C�9�%PB'6�������������O>��K���WR�R%�9���l���"��@qt�l�b�?��5h����mk�
��3gZVV�7)�*W�^��k���o�m������5n���fu���[����r�����*W�)�U�����^���MX��m��c�}E�j�����!v�~�m�{yc`g	mCg��U��������_~i�-r���U��]]��Gy���Q���:�)�s.w�m�����
i���N��VBz��)P���+��b����_~��x�k���
2��t���y����?P�dNIsD��u�p�	���n��7�^�z��O>iw�q���%��<''�
@i�jV%m�����,���Rw�c���X��-##�;�0���;],��Q�W����������d���Q}@�w:��j����Ad�����M���������?�x�~��3W��,�
����6tZ�u�7T�&���7;�;��9����=��V�j�~��o��oZ�j�J����xse�������pZ�j��u��Xff�5&
P����U��������7��]w�e���������t�b�k�v�t�������g+���]�*�5k���?�l���k_g��I��C=��<�L����U�R��
T��
U�"���p�������[�v����n��'��������������M�f���w�) vE���3u�T4h��~u�Yg��Y�\���5��r�BJX@'''����/{����G���A;�����[n��K�Z�^���/��%K��s�=g��U�>	�X��Y�~��Z��ys�c�=\��^��Y3{����_~�y�����?n;v�����m��*������<y�+���{w6l�+���w�Y�>}\�'==����;����a��{������v�y����SRR�9�H	��|��e�g+5��S�V�~���W�@�4�-�
��GF���IX@g��
v����c�=f������S�Vj�o��n(�����Ndt���^��Q{9��s�����/t����?p�@���(�������]��C��7�������o����wo?~����;��sgo
PvPB'6����:����Nff�}��W���{�8��9�����F��>����Q{93g��v��Y��M���i���;w.��
)!m�HZZZ�=Xi��������o,bw@�R�J��aC�?��^����k>��� vqt222�M�66v�X{��Wl��-��i��k������ v	�ru�	'�1�c����K/����c����X����{��t���G���UB�-�~��uK>a�o��� ��5o���7([��<6t[��'a�"+H3z�h6l�u���j�������k����O�:R�re;���l�����
����k��'���������R��k����o��.\���Y�f�!�b�\s�m����#���_�*k�j��j����'�h���n���@����NY���6����g+W����;������{����w�����j�*���;l�����egg��!C��SO���~���������y��v�]w��
���(Z1���� ��M���7{cbW�bE;��C�B�
���K�e���cO<��
0�
��um��C����������>ku���>���,{����o�����W_m�<��N��+ps���[�����'��V�Z��,pm�?��{�9;��s�x����
m�DF:��9��*T
,�=������kSG��J�������Q��/�����Q����Q����jO��~�7%z_}��]|��.@��O?����S@G�#���t�����] G�������Nl�DF@@81t��_�J��Y�������\`U�T���^
��p�
��3���3�T2����v%u��N����)���?��/��v��}�W�^.p�Y�h�k�H�V l��w�����q�]��6r�H�^�_z���Nl�DF@@8	���,R�25|��SO��4:t����3g��}���*T,��T���[o�O>���|�MW�L�	��8q�u���M���,##���������o���T�
%��Nl�DF@@84���.��-�
����v��.��t�R������)���Sj����v�AySB�:�[���V�j�������������e[�����(��QI��2���Dm�(�R�n��V�={�kG��N;�4�
z$ZQ�8�T�eS�U���]kW]u�M�>������g��S�L�R ��w�q�<5o���X�V��7���
@���[o�e����7%_4���S[9�����/�������U�BU�R�Km�k��n����h�	g������g�~�P��e�7T��X��*Y3v��
��f�}�P�z�i?ol�6sc.����zRR�g��v�G��A�\U���o��T�e������8�7N>�d{���KU0�(dgg�����}�����`@,
�(�>6m�4���{\�B7n�<-Z�p�
��lU��5k�+�3n�8[�r�����-��b|p���J��:]w�u�Qb�$�z�K���5n��iSo���.�5��w��C{8�J�|��g�Qd�KKK����K��u���A����Q��h@8�nY��-[�@���\z��.2|�p�{����n�s�{iX��~�m�Yu���O?���)��)W��k���_���W{cw�@��[�qd?���8W�
�h����k����3f�<����CqA�:��|�r�Ps(~c��5r@�w/W���.���K/��aS�L�'�|�n��fW��k��.���/�=K�,�G}�<�@����b���K���Q(������
 ]"�m���6	�:�����U�����sm�(�#�eK=a��7/d`I�4�4�C�l+t��]���o��wwU��vP`)�t�w�7��\%�
+R�+�������+������v�9��`N�:�xQ�*6T���*W�)�EdJ��QO^j'�����]��,����
<�N9�k���_*T�`=z�p�w�����]F��V���w%���s(��@%ez����E�!c��u��������
�{���o��V�n]�y&L����9��\���Rc�7�t��!����#�<�:t����9r���$(��D�Y�f������j��ic��u�o������m���}QR�X�&��?���:�����z
k���k����o������
�m�	D:��
��hC@8��H2t��$S����[�j�{eeeycPX	�l����|�M{��G,33��[�h��y���g(�������4NB:��/��/���=�\�6m�m����l����U�Z����k�'O����?����z�@�����������n���UW]e�+W�����c�E�6i�$[�d�
2�f��i#F�0zK(��:�7o�~���8�{����}�����j������1��=�X�g�}���O>��t�b��O����{K�����o�P�Xf����mw�+���2-������?�r�!V�~}7.;;��L���[�je���w��[�V-[�l��vv�2)�����qC;Lj�?n�����J�0�LKX��������i���t�A�X�M�����"��N�J��a���G+���9s���	\�9�7v�d�����U�&M,##��
[\���A�SZ���ze�����kX�7._���6Is���6_��[�*�y|�S����m�uv�Qy%����:
��i��F�e��#G�}��g���s��W�^���F�5^
$�n�����d���m���{:|�4���r���{u|�{�/gn�����#5��{��L���-;���<��m��%�sl��l�S5�����
�Y��;�����b)�C�+�U$���z�:�����Gq�
��z��v�y���k�����{���?��k�,Q�m�������i�fRl��,��V����iZ����6~v�}������Z���'��[v�C��I����rm�����Z��W������6|�p�y����_t�E�qT����.������\�?W�W�jX+�2���o��N
k��Ec���vH�T�����\6mQ��y}�]��&�g]��v-		�H���])��{�.��r���O�[=���v���Z�:u��@���c�j��Bz^�:k�i8������"��VK�)�]	��(�~���9��KX@�����Zf@��J���g��Q�����f�7�����La@��:[�l��'��_~Y��>��e��-q�\�8/t�A��{����l��g�vs@g���6`�;��c
������P�%0��~s������z�R5��^c��t��:*T����������K
7n��j��i^x�����Z7M:w��z��r��"-5�*���l��r��N�J��������) 0�~�4��A��}�/14��e���R��]s�5v�-�l]r�%��/����n��M��^{m��O=��M�<����J�3g�|��n9@Y���L�]5?x�xE�U,�b�����]7������+�{�
�����R��~>�B�9�,77��
f.�����5j�M��z�������_��}�]������O�F��='�Vo�u=U]���]���u>(��l�u���6�l�����#����|�T;`�4[�&���&�-���������?�6m��
���9��Cl����n�:o,P6��G��4ma���u�m�4��}9��Cy��+�3�4Q�uKX�G�sjVI���r��J�v])�*b�+V����o
6��{�222�);S�����e���R=�j����Om���]$:�� N�`�
��������-k\~�����i�7���[gzC%k���xC���K�T�\���g�0a���7����;v�5o���V������h3�3A0P����X������?���={��_~iYY;����c���n���W/Y��t	�J��c�)����f�@��;�#]�v�[o����9��c�^�zv���Z�.]�_����\��Gy��I�v�`Mq[
���{��IOO�o��>��;���l�����_�R9�����u�#F�6t4?8�U�JH��!���F�$��tDA�O<����+����i�\0G���9G}���Pe�6Yk��zI���f�=�c�N�r�>���q���������s�[�l;�g��]�c��o��r�e��z}\�|�~��O��J��t|
�T�^�uO��cG��J�*�T���f�7d�a���/�X�V�o�Hs�U���iZ0?,�i7=�cO}�cW��j��f{��mj@Lg�R�S���'������_�.go��J�H	�Hff���<�k��U�������������7fs~�u���N�}��{i�J���/}�c��k���f'�b�j���m����)���H�����-�_��y3l��^m�?E�@�H�L��;�~��z��a��U�
*X������=�\�������M�7f���Y��^��H��5N���wV�5;5��;-�r�Y��l��f�*x#BH�y������J��t�N�j��~��������('3�
�mT��f�+�^��5n
(��w���d���\;���]�8g
�����>�Y��n}1-�.7g�w���>�Uj�
@r�;����m����-Z��n��&���?\u*��	�5j���U�[�]Nn�YN~�8*E�����uf��u
"�:��
Kw���~<�-����fg�rf�����KR���iv��)v�C9��o���e���$��:k������;�8���l�=��'+���`��)t�>)�p�Tk�_^�8}�M��5�>�������^�j/�����9b����Tk��l�w
0��,w���5yo 	��Qi���,�g�}l��v��@$)�����-(����1T��Y����S����
S�����o��_���c��f�����-w+�zHqt222�I�&�q�F���������1�\)�V��um��l�sy��k����4�c�ek�+�
��loD89[v��]��J�*��'�h������'{c ����R!�������4/��[�"���2k��7"H�z)�5�����p�n0��8��j��Y�$���#{��r6l6[�$�W-U�rR+��U��@�w@G�v�j�^{�����^~�e����m��M�T�:�yo��o�bm���}������6��\{��\k��7M�m���l�mI^p�Q]�3�N����^�4��7rl�zs���Cn�o��;4��~��*�g?������mS�����R��2
 y��Y�j���=��/_n�]v���_�U�R���^'�p��X��e^�n��!�J��{a�5��bG���#��m5���yi��&�����)y�[�����:9����b7<�c�^�e+���tK��Q;o�
����8�:�b���c�_�e����+}S]oW>�.�5�wP�����8(0s�����N��t�b��
��r�9�n��J�������Z�Y��y�����n�Y�F�;��o��
���g��
���.�����Q�Lq�h_��`��KI�J��`$�����K\��Kx@g��e��G�<����K�.u�s�����eQ�m��6l�`���=��c�n��ji��U�Z�^��o��n(�hC'6Y��yC%+��Vo(m�(�RBG��>}��=��c����/��w���k���]@G��pRB��?tA�:���?n{���7%���w��6~�x{��w�s�������Nl(�%t�w	���L����l�=�t%pBsD�5]���+}��;���rf��i�����M�zcC�t�7w�\�]R�z�JKK����=�h��������o,bw@�R�J��aC�?��^����k>��� vqt222�M�66v�X{��Wl��-��i��k������ v	�ru�	'�1�c����K/����c����X����{��t���G���UB�-�~��uK>a�o��� ��5o���7([��<6t[��'a�"+H3z�h6l�u���j�������k����O�J���N�(�%t���:�������W_}�cD�7o��o��n��F[�p�7����NVV�=�����cG���ol��M������y����U�V���o��@l�9r��{����uk;����|�����U��SO=e/�����S����kS�N�� Vq�������4�F�r�o>�`o��T%���/���;����nKKK��eCinC'k����k���	��q�7�h�T�j�kc�\d�M�����
��hC@8q��Y�n���9�uI��iSolh���C����?���k�����9�����|�Y�s$w�:���n� 1�(�J����x�BKOO������QP<T2'w��]��wk�w@�R�J��aCWJg�����V�^m���w��s���Y���)�!@�����M�6m�����_|q�����W^y���k-Z�p�P<���U8�~H��T������z��6`�����=������_n
2�M�����{�Ygy�@����������
7�`��7fg
�<�����Y3oP���^��w�7T������C/W����p�5v<i�$9r�M�:�k��U+�����m��*T����:���
�$4����DF@'6t���n���_��V�ZE��	������];:M�4��U�������:��J���z�@�%$��Z[�~��u���y�[�h�7%Off�-^�����
���o���P��tf��m���s�*���:
��TZ���n�v����_��7��;���9����-X���{�9�����n���������KRS��#��G}�j��i�F�r�v��:k���3fX�.]\�&�}���:t�`s���
JGo@�&��NVV��[��j��e���������n�3�~P8qt�Q9+V�(�����m��en~}��;�S�J���m���6i�$o�������������\EZ����?�9����Q������N:�����z�6l��+W�^�u���[]��w�y�;�0��@��i`�hj\���P����
��:c����d�+�
���N[��<��:�2S��7�P��<n���xCH�yKs���[��y��13��_T�T0;l�t;�M9kZ?��(�����Ad��~�-_��.��B�����>k�'O���:��W�n�^{�m���h�Z��>	�[Qs$�r��-Z��7�DP0��k����Y����[�}��[�$BB:)))v��g�g�}fg�u�+�#+W��Y�f��n����#��s�u�@iU|����>g�m���6���9[�Jn����@"$��U�M�6�F���7�s�=�N�:���]U�"K�*W
���<����M��e�����j��K6,�!�?�w��v����vF��6u�/���;�u����#zY�J5�9v�|�J�c�`���OvH����v7[����������X�%sBQ��o���
�H:����Nd����r�]�Y_7|d�C����s%v��<*cx��O��K��q�t�����*�Q�F���J��~�����>@����%��f��,��>���k^��=|T_7
�+��'b7g�o���l��l��M���U�\ek���x���o����%'��3n���,X�z����,{������NsU��~�i7�F����e�<��Y���r�{�X{����K��i�(p���	���j������m��d��1�>Y���=�����t�v�
�o��u�RzE�Q0��q��g�O(�`�����}��[�P��Q���W_�F���A�l���.����/�g�a|���9�z��i}��!��T�������z���T��U���f�7����iZ$=��`�jj�,�j[s"������vX�m��%6��Q�X���hm��J��:��)�����Z��������~k���s�X�����l[�t�
>�5������_|�D~���l����������mk3�K�TH+�n��d����y3P{u�j{�){kmXn�/���
O����e��p%x|jo'�������Y���K�{C%'����7�F�=QV�*o���^M�4��w�n�\r�u�����Q7����s�x�4����-���i�����������r���������-����6uVm^k_��cC5*T������U8�~P����_��~��W;�����#���%����m��uv���n���V�Z��ys�?>���B*e_;�����
��V��7�~��
��&�a�����9�3���������Q�&##�����8��5k��~x��]
��t~I�u���]C��N������������k���rr����zc�
)���QG���7�������6e�k�����W~���f�l�2�[���/_��CzJ�e�*��r��N��U�1y����)U�
�q����n��N���X�[m����S�p����g�iWx�7w^����|���m������!%-��������]���'���?��cWB�m��V�^=7�z����Om�����IW�J���T��Q�{g�p��V1���Wco�Y����|Z9�#�����"5S��l�N~���4x���i/����m�?������1��7�6�5lk��U�o�L��@t������)����;u��J�����7nl]�vu���N�����k���5k��5J���4�C���?'���k���;��5Wp�u�������M���?�����]��t[�q���}�}������k[Nn����R8��]�>wP������������z����(%��MU�z���]Vr�A���>jGy�{���J�������#����4:�f3o�l��?�s?w]�_~�v��=\5�g~z�M�:�jX���V�b~����*Tuj��jU�����}�_nu�D+%Wu�H��T�Pa{oW>����W���
/��NKG�m��������JG��u���-w��r�P�J���U�J�1���.4?�,�����A�����i����P���%-!%tU�Xq�`�4k��`�R/���S,7l0��rj[�E�:P��/�S�R;E]�@�B@ ��H2t��|�r�����e���������n��F[�p�7G����Y����-�V�Z��G���/,''��sG���O��M�O<��}�]��e�7(��D��?��K/�����g+W����;������{����w����P0����������K����_�������v����m��f6��Kvv�
2�N=�T7��?��Cl��	v��g�]w���g@�D@��6m�x�F�ip%k>��3�1c�����`�{��]	�h���O.�R�jU����m���6z�h���o�����#�8�h|����<'N�;����7onS�LqA���������k��6x�`1b�77(��@Ui:����w��V�R%7�B�
v��[�^�����������/��q�\PH�:���,55o��U����{�Y�n����T���E�Y�>}�U�Vn�����.���{�yV�Z�MeU�:�J�t�R�P�C�Z���y�[�~����MU��,Yb]�t�5jxc��������7�Xff�g����������J�(��q��.H�l�����vU���;Z�6m��@M�6�v���R<*1���t�v������u�����~����o��/U'J�F|��7o������`����-Z��S~��p*V�hw�}���=�`B�v��q��.�J*)���n��������a�5r�i��lKH@G�{���
���{������yTe��SpE%e4h`�+W����v��V�~}W�������J9�;�~��W;����o�
�*�J��R=B	���:j4X���x�������v�-��}]s�5V�Jo	�WVV����[�X�W�?����4�z�JKKs���?��U�|y7v])�*�+V������O>��+ERV��M�o�����	�<�P�U�^{�+a�6��=���m�6�.���`��4%�y��:u�7�x���7T�u���]�[���J��<�
�i�z�6�J���&zCy��w�7T��i��B"���������N��.��^�T
H������O5j�]z��.���#�X�=B6� q������;����0y���\���BU����:�{�;��c|�7%�_BG���������h�J�J��p�
�������s��������>���;[�~�\��~U�@~	ua��P2:
��
��q�wl�i�q�{C%������<Y��yC%+��Vo(O�����J����������Dl8?��]BG�UR���W^I����U�\9W�L��^���#rT�J�#wk�z���;��K.�:u�������cp0G�z-_�<���CVoW�l�;�����zy���k�>���������n_~�e��J�$C�G���%nf��������m����y���R����ov�iT�F��u��1l5+�z:���\��KZ��N�o��JG����Y�j�+Yr��'��E�\�W��~���t�1��|
0���_�-�tk�����=���@�2���o�>��T2�o�����O�Yg�e/��R�A�;�����`����M�2���i���rPv����3C��5k�xc
V�zu��K2t]�n����cO<���"��5�+Wv%�����������y��WA���UW]e�{�+�T�^=oJd&LpA2w���<*���V�{���#G���<(9��m���6:�6t��O�<qtv��������c�7�f������m��9��EW��m����y�Q��.]���k��������]�V	���4��3�����b��8j�g��������`J�������"�S6p~������@�~�����f��6v�X7^�S����`N8����v
C]�����>��CWUk���.��*a��K����k����A��W������GK=9���zt�%t"��Nl(�X��)8?��Q�A�K��{u����,bG�$s@G���Q_�4��W��h_�g;J�$:I��@�!��d�$: ��u�-���-����5�\������S�
������`�����p��Z��-Y��w��&����En�g���l�n�)�����5k�=��Sv�����W^i�V����L9r�-Y����j+>�JN��1��%,��|�r�i���]w�u��+�����]�'++�>��;��cl��Q����}
�F�Z�*�����%$��R8
����Kv�QG��w�i]�t������Y�����_��o_�;w�7�JH@�������~�`�G���o���4i�M5�R���w�}6x�`�1c���R:�w@g��M6y�dW2���/�J�*ySv���j]�v�N8���Y�~�7@� �
eE���7���]�9�j����V�rek���-[��5��8�x�0��5����]`5*M�|�O�����N>�j�9��|6��j������U����O���[#�����i~�H�5>�<��-[�!@<�����>����]�W�H���g&Lp��s�^�_zCf�?�2.�Ru m�k��WJ�
n��9Aq���]k�N=�rV����<f��d�^�*��U8�����	�l{�YJ�r58�d�9�D'��N���s���`�{���\���,Z�����o�f��N�:��(b���5?�W��G��������l���K��i���u��=�r7n�
/<a��}�6���m3�R*eX�CZ�y�������m�����n�v�o�G�\�At�[B�\w�qv�����?o�[��~����?�l�W�v=Z]s�5��eK9r����s�.��������YZ�&f�[,��y���6�M�y����nj9+V��_~������M��e����-I��xrs��m��\��@c��		��������J��\��{�1W�j��Iv������>��+�s��wS�
(&�9���n���\�*�c��v�7��p��m���5}{��}�1y�6v%m��X��gN�j�y��o����h�^�87�I��*U�a@<�(r��U��{�q]����v����n�o��f{������'�x�j���}@����!X�Z��7Tx�w�
Gc��/�M#�u��|1�6�5���5��z���j��
C_r�|i��i�R4�m��$aI�v�V�~};��s\p���?�A����_n
6t��\W�&Q��e]e)i����w-w�F7^
*�
'ox~�mx�9����2�</���mr�������)���
%�t	P$�..��M�%br"T
��L���*��hi{4�M#����>���U�v��N���a���ky��n�������Yv��(!;<[���v���6���
[v�?k�[{�������������O�N�;��H>	��J�J�t���5z�u��W��U��O(2i��7*_n�K�P�R�U��lKj��6O%��r8
�T0���"��������Q�'w��Z����u����6����+��m�t�L�*\K(��������*�����|���x����J��;��u�OB:S�Nu]��r�-�'�1c�D|-^�x��c��G����fi��x���{��������QOV������?����J�>V��Cm��q;s${�?�R�����ZZ��.�^��KK���H�-Y�7?�rJ�r�V����{
�����$�x��Sy{������ag��gn�Z��m��z����f�-�������7_�0yq�wk�QT���$�Z*@q�;����i��
�9s��E]d�|��-[�������/5�\�F
o	�Lj���o���]��*�x�U��j���
������Y��I��Z������.u�+_v�U8�8��7��N�l�?i���Q���������P�K������^\n��n[�Oq�5���w���p�"�Eg�'�.:��N{u|�{�/gn���ry��vj�r�zC�=36��~����}��v������t��)�ns�}9+�>�9�5eA~�����;��~?�*9�S&zC�%!���j��v@2�;��n�:�9s��r�)���Z�v��N�:V�V��/sR��P���n�
m�q�6�6����n�h�_f�/��U�����n����ciu�m�[��O��@K)_���;�������2`�Km�����6���m������v/
k����8����������*m��YF�9-��|���4�q�J�m�7n������w��j����1?eY�m�9�q�g*��m�j�����������&n��R1e������U8��`yQ]�m[�\�?���}g��Lzz�5h��u]�t���O�����-?�������nO�?��-����~��[s�u!T�k�9]��Tec��]�/W���8�������7��*���a�T�����>�4�q�l����bC���G�}???0�|]�k�;-�#
����i����I�+���m���!�rf�.��)���g��^v{�������S_��}��}O����O�+lR���T�;�1�l[��9�@��;KY�re������Ym�����HM��9��Xz ���P��s��m�?\)�m��k�i8����-X�c�����xw�k\��y���y��}�Z�r)V�z�}rs^�9��a7�T�2���T�w+�l0M~[_G68��<�,KMI��~�^���q�J�������`+7��G��j/����K-g�5?�:7j�7S@��q�#�S���;����(aqt��/o�\r���=��y�����P����m��n?
{k�-
J���\��4�M����fGm{-�'�F������)��6e���s�l��,[������v�Q��'�k�m�
1R�_V���m�
ZY�r������/o���5N�B9j��l������C��9��?�i/��2�+�au���;<4�G�\0g�����v)�	z������W\a����u�Qv���;�T�X�=�P�P��7(:
��
�,���Q;�O�������������5.D�B% �S~�&���3���5��w�!�ccf��od�}5+/���U��r�����U�m�f}O�`�����_�\��q�T�o�
V�Z�=;.�>��AF���������{������A�l�.��N�������3+'��1�����{�*P���������]��w\������m������w�q�f�5��-.��Y���	yUJ��u���k�}c�L������O�K��n��{��D����l�S�&RE�."*�	�"@i�����)�g=�,��{�� MGG�*p �{/���n2����l&!��d�����L��Lf�2�~���5�����7���>W�9]������[f����;]*��2���5�;��6�R���$"��%_�\'N���?�X����������7�|�t��5�����Krr��5
&�/����������MON��� ��7DI�����	�d����e��TM�
jx���{"�x�,����$���)�`
��9���O`i^����g����/�s�N�KU����)"���)}.�^����J�`%
�9}�&?n�,�`$��zO�7�����$�s
l��o���c%!!Az���j���|��2x�`)S���BT:��:��5tB�:����^��U�f��iP�.o�������S������rA0��;��i��jm�|���)B:5����[5�2��B'�/���K�{6f����?S3����e��_��Y7�)e"���5���c�U�������\P�&;�#��V�c�'��e���q^����a�x�5���������d
�����tX)J]�8����=�W�!7y�\Yy�7I�����V62^�T�J�i�W��#"�X���Nbb��Z�JZ�h!+V���?�\���i�����!"�B�Q�*��F^��qL�M��4�@�}�:���1=����*���V�i���:@����H���)kS�����;J_��B��O�Ws�p����8���sAV0�#����O��/v�Si��y��
��Y����6�T �s���e����������xo��"�t����$m����u��DDD�:�-�����������C�u��!"�Gs�j�e0�M�L�k�l���r_�(����j����
�r��
�E?9���V/�����n��[�v�����d�no� U��%�xT~)a|�%9�����~��R�lu�j��v�����
n�+#�����z�wUk��Ga6���_Q��f�o������1],������Z�jIjj*G�""�b�q
����?;U?6�;D�=��T���~rfu��=��5{]�<����}��7�g�!��9?]����V��p�h�V�&���!�-�p��k��Ho`����EH�#�x�)���S�(o��J1$V_��e_�
A�	�����&n�n,A��T��Ub��NO�f��|�����~_��fw�u�2����D��fVEm����Q����N\\�t��MV�^-��V�?�DDD�������?�AS��K)j������,rJ�WS���@F�BYL�N����f���v��O�K�W<���F�L\����c#���	Y��.U��W�j��8��Q�������HTD�H:b�\��V#�e�&jd,�AW�F������������1��6|"�������d������+����}��*�fV�A�+"��E�:p�-�H�~�d��Qj���r+""*6��P4��Am������H��`��"l����Ck�L�9��^7UkfD�~r�e]�\z�ZC��9��U�������Z�d������f��ng�I����:��5�H�3U�_)��.���)�[s�fX���,{�}U�K���U�<QQ�s@���3����Mu����G���`�-[Vl6[��W�^r��)cDDD��5�J����f	
�9Q#[}�e������w�_��W�����E��WUb+J���R%�S��Y��O����\���U��>u�V����
�C�*5�T�$�<[����A�d��FJ�z��)��M�J�*)D��`�<4���������?QS���������������7S���Uq��^?��
W�k� (3�|A\(�	�3�CDDD�@�5���V(��!""���/}�l�FDDDt��������J��:�y���j�����;��Q�B���n9p���0o�^�pa���-]��<��@DDDt�9�S�Ly�����y���e����m����8+���-��n��u�Y�����hi���t��%�	�c;"""*����`���
��D��B�$''g���<�2f���=-��[n�v[������r�VU��k2�=��<���s>]d��n���p�2����)ZfoFDDDt��s
"""*]�s��9��4��?v�3C��w#�t&���e��5�
j2�e�4��%?n���u�'�%��H����3����;;����"�fE^Mc�e� """":DDD���EK\c���_S�clry
��y3=��5���niZ�&���0R�S������T�w:vZ���"���4���!6GE���!"""/�)�����mDjV���
�`iXH��6Y���@���l$����a�&7��I�j���U�%��`�""""":DDD��v�tB�r^��r6�r	:�#�����KEG/��_��}�o���56��l�o����""""a�!�[�N���'={�y9r��9s�����-�����k�"no�8��3�������::O^�^���{�tY�b�sDDDDdv@�����l�2Y�pa����������r���.����]��9_��������3���-�l���Y�Q��3��r��)4h����_���/�������/_^,e��1R�J�n/�sEk�S�������sE������G��\�dG�c����wsEk��������?q	�1:�*�������qZ\���B����n��S�����F������Cs��^O����m���/�k2�m��x�.W����O��9+4���-����e=i&���Cq������Q��&�7��-3�H-`�
b��C�u[���D/�O"��v
���k��1c���G������`]���1�{�R��TpS���*-{���-.N���������L�Om��yL�,\-�'��H1�����Q��-�����t�&�L#A�����80��5���B��3��*��J����vA0��C0����h�f^02���T��-�2HDDTD��s"Q���8c�vI��#5���U�b�RU�<�wI�2W��&�+�������]$�s��M3�B���]�3�kTKd�QO�t����I���H����o�d�a������(G�f�������������:DDDEA�$s���HT��b����ys%u�$5aiXHl��$��[�}��8��6RC-��[��36q����c���kd�i\�&m�����n�q@�]�4yc�&����e������d���@9K���M�oiSC��V)��Ps�}�������&�]{��k��Yfv
����/"��""���v���A��HD��"�t����H��������^�=���q��O7R���O
��s�J��~i��*t0�<C�?3�.�+���C.i��K5�za��O����,\/r:������ml�;�������-�g.����/,������P�Ye'3�!�B=�a�e,�<�!��
o�������v�;)�x��I��EKOw�9#E�<���0�,�dl�U2~�d��^D��$��>�:vX�^j�f�V��1�Q����Qv96��&�#���r���A�����]S�2��
���d����QI/^S����jo��UX�P�����Dt�	9�S�bE�1c��0ODDDapk>�{��z���P-����b����F�� ����P�y)��E']u������:Rf����O��i��8�v!��e���D������QF�w�Q�����=�]�d�L
�_����.������Y������������y�J�M�#V�r�|����f��T��[���N��/�cP�[��)E��Q!
9�c��U ����(z6��r��5f!�k����dl�"���4R/���������(��]���@�������1R��=�'���"E�aY U����Q2ut�l)�����p����U��L��������N����g�	��s��9�v�[�����n���[f�):"J�a���{U5�����{U�gX���rl����-k�n>P�5�%�����'���%��H�����������g�x���J9Fd�����^�����N��O��w�a��,:B��O$�)���a��\����1^�4�6�����!q�6�aC�LY��&�#
�|y2F���r�U�r:E�u����j� B��k��ON��7���l��x}�W���w�)��&��s�$��i9�����~]�L'�vS���.	4��������z+����9�����T���e��V��[���J;#���c�k$%#U���T���{�`����e�V�5U�yV�w�;������c@����(D��g��D�t�EG����s{�J�:���pD��"����R)���Re�Z�0a��+W����J��+%~�(����"�$o�=D��������kU��3C�=������e�<��
�\���4��y9���@�[��i�o^������0<w�w{T�><B�\$��T#��i�I�=��%!����$5^��-WC5��un��� �>��eVfz��������������SF�W�
u���3������#�T��=-�Z��
dB�+��_�E�""�"`�GHD�*�+�@��HL�846RD����(q�6e�s�zIz�%Iz���)�?����)��r��q���z8"Bl�f�#D�}�$Y�(�O�:��4�{��	l�>�l9xa�kq~�N���-SR����M���C��h��v��x��/>��c�����1w.�%�No-C4�B���3�H5����z�w�tr�����+�yv��Y��<�r���Q�������$o
���e�f�IDTZ1�CDDT�v����W,��5j��
!1=z�v��Zq�H��_K����un0�y�����h^���w����9���t���y����~���CT��nM��`M��
���M�=��4a`�,y*^��-1�"-s�����N�����kWsE��~�1W|�4���2�W"e����������fk�Z���M<(�w.0RE./_[��"*�l�#��n/�sE7�Vh�_���U��@��>G7�M(\;�Nc�h�0����P����	95���J$�W�|�DT��^������H��o��2���>w���J�;�O���}Fbn�Q=�O��#�B�-��r�xA2��s�=`�����u];���Sb���g�@�����)���Du>�K��������--�F��D��Snylz�w��w����!3Ve�fV��/t�\��]nh�����K���<����iQ�����93�gj�yyq�DY|`�z=��7�Ld�<��Ywl�J��Zsy����xHF���J��3��Un��Y&�1W���e�j���>"��*�['��{�KD��1E��Cy���[f����;]Y5�
Z�h�k.w��m"��%�����#�����G~��B�ld���~�����4�x��J��9�������+nN�+'nh�&����F'O|]N�lA0�^�����>�`dlX/�������k�ds�
�q�o]rZ��WG�h��Lyo�S��-S*���S#o;�z���G�a��K&9�	�����X��V��v.�	����9����>O��z�+��tQ����1P� �s���e����������xot������t&��d-�)���<$4QA���$��u��A?6�b�_��x�:�Z��<A�Q��vK�K�j���u.US��Ph��s���������)U�H�SAb�u�<+/����1iQ���j�����f�#)'�W�����Yt��c��A�����*hxot�����*J���9�2^�Mv1 �B���V.���8���(�\aifQ��]�6�	�����P�hiR���	v����a�([��{���j��x�M������J\��JT��G�4�x��"R�|�������!�w{��iY��j�����&�1�H���Y���r����fVEm�����7t�����^�-C���-�._��R'Q���m�#N�M����)J�t������9��6�2���2�]dP�4���[�F�-�\�P�����F�Z��[`�U���2/�04�mB�o�-B�icZvh��I;'���&���%#����/�"����2����������v�:���j��g���c�U?;�t}�6YM�0Z����j��W������W�7���*;h~E�""�b��R�$����Z3��f��N9��!R���F�B�f'��T-g����)�X������Y�&�����$5]��:e�e��+����������~���o���m�jz��J(�m��nI��)M������������|�YBcNTG�n��:K�����f�U���7��Ir����C�*1��=�iV�����4��<��A���9�O������������EDTZq�+�|�Q�r�Q�B�Qt��g��?C����7�
/�?q�jD$,�$�e���>5t
,�x�����Z(�����R�Dz������=;|e�����c����&K0�;#2�
���i����e�aM���-wu��������E��!�X��5n�-�g�W����c���_Q��
u���O�����^7)4�n�n��CDDDDE&�&N��B>��W �c1���i}Y��=��5^�EK�4Y���55=6�&���L�7�	f�%�i��]���.u��tmi���������?�V�H\g����^����&���w����H�KZH����+"���""""*��-��W ;��?�mB��>����e8E4�h�w{���YE�Z�w��G�yy�;�l���&
k����b��k���I����*���?{hg%gh���Y�O�r��������&��O�9��lh�5:DDDDD~T�"��;��?H�Ek��0u�J�I�mb�m"[^�Y�xm�	��d��S$=]������.��&	�l�0t�G�/�����U&V��>���qR?Q�o���hc����W��B�e��	g�@�W����M�u���%6�(?1�CDDDD��}��1'u];�:R�s�K�������,����7����U����$�������;�%%���!�����3�ST�����<i��a��6�U����$Z�P9qV��gD2�#Y��~�*���@!-i������]�L�����{NL� �h���:G�������I��������/i��J��Ij�<��,�@���l���J��K%m�<��^����!�5+�=�7�5j�`�*f�x&���?������������HXh�U���D�3T����W��Z��#e��8Y�T�����Si
}�&5�����nl���G��e,���=��`��N��%Zf��Z2���T���A���~O���CY;�K5}s�D��/�m���\-C���&,�:���I)C��6�Y���:|�x!Q���3]2��4R�2��4�,�@��r��fm��������~i��D��(�(r�b�9���jc&;�?�n��s�M�~�3�U�A����n���]�6����QV?K�-*5��@�H���u�G��������?9�)*
�|��
��y{���!�������l���5�P���M��S����1X�:���iW���l�Ou`���Yj���RiXHn��~j���o��sk�����4E��r0���;�Y�����$E�Jt�����,4�K�I��jP;FKOw�9#E/;��H��U���*�W�W�&��kT
��Is}�z���O����cmr:Q�����9tB��f��r`�nchq�M�|>B2;��U�A�.�d)a��������q�m��m������h���!S���P���e�_9=�m��l�6Nl���K���/��{}�}'C�h�9�_c��h�s��h*�b�P���G���]�e��_�	�H��@r�&5��,=�Z��]�3�(SMv��r�7cO"��W�X�5"L����DDDDDVn�(d	����5�rf�L�6&[\�D]�Z�g��s��F��=��g�%��j�y]�Z"{�z�1�#�<}����H�����_�\II�|�&�Z��ZE#Q_�V��1O��K�:��&��dg�&{T��#
���_�6EH�������~�DK��kf(�2��d��n9��gvC�r5T�]��)���O�aY �lS���rM�fr ����s��*�����`����"""""z�������~�D\ZK2��d���H5����7J$��g^���M�4��������&�i��lMZ]aS�}�<�����m�\�����Y�^�\r��&�z�%"�����������I:���G;<}�$Z�0��g'�`�iX�.�M�������e�S�2��8������z,�_8��6~�3N=��\�K�� �J!s��l���GZv����e�K��J���&U����t��0�_�!""""��:,}���sx��MY,�mP�+�\��z����BcN$6Z��!v�^�&rI�]R�����j�M�d�zMN��6��snz�%�LwK��l��S��9����*n�o��21�����|�M�#E.�j���3���NIN���H����y�&�v�.��f��.��zZN��"'�_W(�����{������g���L����>��2�T��Q����
u�8G������IK�������s���!""""����-�;���L[t������+������^��i-t��
�@X����E
�H��]��t����LSc�}��e�csj����
�/�_?�P5pL�mW��l���u���3d*��w�3[����TM���3����2su�|��)�LMZ�}��M6pI����
��}x����H����
�~6���$��x��:�F�[F6;zZ����R��L5a�8��6���Id����K�1-�w�?�ASXI���v�Ze/����.�OyG
t�L��g�������f�������]�������Ac#E�Q��HT��yF�A��R�'�m,�Fw��H���)�b:�T���-RP�e�����=�m�.M�[:A�~���f�����s�1��_�����f�
E�����~,����R�}b�����6�<}N=�1����5�I��aY 9nc8k�Q�[��d�+��5�����]��)���I�c.x�n������v����)��u
:DDDDDVv�����+��v�����G�����!#$�Go�wN-��C$���%n�p�:�m �q3q4�R���J��K�TK�����PT�"�6��tc�~o�'�5���Y��Mi=�mN&ib�a(���T���p�s
�%��E}���p�[-YU�4�h�k<����55=6�&���L�7��q��mR�kR��M:4�I������t�9������!q�Q���
��)�m*Y)��y
��	e�.5[K���j����~6R��KQ��!""""���W�����K���D;�*q�����#U����>P��^��DT���[Y�f���]�	����Wq���!*J���rYo1��)��D���K�E�zU��������l���[�jk^��:JF�T�7���9:z�S�S�*+���!���A�,Z����sUZNr��?�Rf�cRe�j)?�?F��N�~.chw
���3��v@�f�j	�s�y�a��������Y��O���+�������h�*q	F�~�I�#ZU��H�_��DED��L���6��V[����_�l0R�00�CDDDD���X���VN�+'nh�&�#
PJ������^����� �6��lcJ~�M9ycI~�#��h!�S��\��v��M����)J�t���r.US�`@�H���X����q�m���C�#����C����1�s�DF�d��F�DW��]��@���!�/D]�N����3�]R>�HM��h��e���7����
A����,4������CG�Z4	�lei�y����ftT�fX�6h����;��c.Y����itl�O�-B.+WS���Ck�L�9��^7��.����|Y9������F}en�wex��������q�JW�������������zLT`�!""""����-�d��;�\��/�C�kx����d��N9��!R�������T�5z���g��	�M��6�NM�]�.����sc���>J�z��������3]wy�
2!j�����.F����K����:e��0�45j\�lc����[��36q�;k���'����hB�6hv���:����avY�N�4�g�����-{��<���=��k��	���c���-��P��5�C����jR���/�2�[Q��U�*1��=�o��{w��V*�V��������*��u��""""�TyD/'���_��Fi���"*<�����)SML��/��	�H3���Y�������0��G��xm=���MI���z���V��^�
�@�K��Vn�����T?W�t����H���f��*�mb��S��9����=&_�9��"S0b�D^a����~sZ7���C�ru��Vyk�hI��uN�W��v���Y*�~?Z���OM��FO3���
�J�/����M2Ro�4�77N��_�-����H����Qvl��Q���>���B�F�~	Qa��U�,+���a�X5�K�I��Mh*������`iV���6u.S���������2�]�
�Lvr�&��E��e��'D��X���hg�l0C���3���?!t��������O���p�!*�����C(��5����LYc.g�Cv�����^1A��}��9'�(}�7^�����t"�Z7t�:��������������C'���P`n�Y���.���kWx�!"""""�B�)��R����@��]Fw�!������[����F��R>x[��4�#D�-�;nx�Z"{�z�1�#�49zF���FX>z0��=�����|��I��~��Yh���1,X���J;t�t��	y��W������&�5��c����{�5B���n�,[�L���/�*U�r����7�(_}����U$"""""*=l���Qwr�1@6q���X+�#���D2�r��Fj`������O
��FF�H�:6i��&/Ow����:���5iu�M-�}�D���-{�x4�m�r��6��T�l����3"|���vhrK[���D]*6G�m���a@'������O>)�O��������_]���+�V�R����\.�L�2En��V���/��+�����JV�\)w�y��7NRRR��������J���b��"�3]l��b/W�H��+����I�z�p��RO�������8.k �JU��KoJ��k����b�\E�Z^+U����a��v�^�r'y���U�����h�g���z%�t|�%�tI��"/��ep6Y���59m�������
F{��t��&�����d����|��]�veR���h�����\�?^^}�U��������?��C-Z$�6m�I�&����e��	��M0�����������&M�����U����~��7J�����w������X����������#$��w�&���"11�h��Hq�k %�C��k�9,�zr���q�$���$��|������}��d��I����e=FD�-����V����T����Qv96��&�#���r���A���c
����u�1"cn����F��o2���/+{��8Y�+*��������U���n�Mz�!�E�V-��s��3F���#?���J�M8�Cs�Y�f��}�����V�ZKD���'/�����YS�s���Gs"""""�R�n�Z<}�2���Q�Q��L�����h���e7`�$|���
�^�����qI_�P����2��R5~4�S��.��=j_�/U5y���oqS����:�@��#G�H��=�bE��k�C���+��g������G��Z9]�t�6m��4����K���U-��!"""""*m��s"�K���D;�*q�����#U����>P������+���l,���tr���&;w�T��{��YU�VMZ�h![�n���:����?�P���
.��*..Nj������m��JDDDDDTz���������}��
���y���I������^����� �mA���]���c)Q���m�ET���:?~\�W�.���F����+�%�\�j��<y�H
,��aw��-�j���W�:DDDDDT*���
`u��P�^�
vSP�GD3DE��dff�@J��U�L�2Fj����YSM���<o]lT�F�9�������6�wC%:DDDDDDTr���.�xC226[��s6Mg���S�N��A�T���3gJ��
�%^��c
w��M�����q���g�5���:����k�9"""""""*�0`��5tr��Mh�������	�����e���6p��yq�\j>;���CDDDDDDD�k���R�;V&N�(��/�N�:K��o�.P��L�J�=�[��?G��G���[o�Q���������S�N��j�Q��:9���QC��������~��i�4i�D�B��p��Q��7o��<?{��J�B�����Z�Q�FF*�V���}��*P�`�9s�����Q�~��G5��C��F�
g���~n��u����G��k��M���DDDDDDDTz1�������;d��9��SJJ�JG8�'O�w�yG����0�g������S���?^��['fK���w�SO=�j�`����������J7��4gz��e���*���A9p�����E���{�I��m��=V�\��2={���o�p���;����
���j�DFF����k�c���7� """"""���� �8qB>��c�5k�l��A
'��wo3f���[�X�+����?p���3e|�,Y"�	��#���oV5y���������c@����������a:DDDDDDDD%:DDDDDDDD%:DDDDDDDD%:DDDDDDDD%:DDDDDDDD%:DDDDDDDD%:D��-��-��n�M��+'6�M�w�.3f�����k����S��W/����d���Fj���w�����!QI�p�B�W�\i��|0v�Xi�����u���G}4��W�������[o��3g���M�4��i��1B�/��.]��{��')))�Z������7+U�$���W�Z\s�5m�4���/�h�������o�5R���@�y�_���>|�X+g���j}l��5��%�C>-H�vR^0�CT��B6a�����e�����iS����l��A(�F�R��d���r��w��-[���c��U����KRR������U����_W�{��!111��oH��=���7��������!h��

�3g�T���?�X��RG�-��~��������6:t���W\q�|���j_�>�l�����c���r��!*.�����c��>�3--M�1�OVs��?�����W����W��r(��������[���k��_~��K�����.C��)S���������p�\*ET��F
�����[=q�	nR�����Y�f���[e��j�I�&������#G��u�|>�[*I6o�,�����e���%K��_�E����s�=�j�}�����nl�������7qm�5���^zI=@)N�7�����������z����+.��k��Q������������:��k'�:D�#�I��5���ZUu.��y��G�A��y��Y�ND�����w�'ph��`L��U��B��SI��>���������\GGG������Ps����*��B�|��G(�=��C���y}�������/����?�(P��k���M7���7�-��f�A��!
�4�}���^"h��}{������nv��%��S���4�4;&*��!*��N��B�j���VU�TQ���'O��nO���;U�Y�i~��W.h���!X���j!�l����_����j"��h\\������p�~�mn}��m����#*(��h�������7�,���W7������?�����{�V�W+u���i Cl�EA�����l��2��@���S�J���U!����	'���p|W_}�Z�����Zk�(Th��`���;wV�/+�3L�>&''�4l����aCU��S���Qp��qu
���G���S�'����	�>y��;'�����.��7�(m����~�M]C�����������R�a��0��@y-P~F>��|����ZD�A����}�J��m�T��+�{F��A~��{K�>G-����o��];cI����{�U�wL������� ���������yqa@��C5k�T�����.��C9�WS�V-�c���Ty��d��A�������'��[n���Ms�n�T- \��A���V�ZI�
T{i<���1���3����������W�o����k_$�������6�����O?
l"������&�|���l��X
C�Z��ps�_B���\s��3�W�X8��*�N�G(#����8��yz###����[���cG�E`�!EA��W_��)�-66V5�@P����C��w�/�����l��M��I|_C�y����k�V�u4�B�A?xf���������?8�@�.��j�5�5������K���P�9r�zl�%~��iXf
��X����q��'>7�y�>}�=Q����k�����
�Pt70f���+<����-Z��S�;�~5j�|���N��+���p/��H�r����!�@D����[���[k��G�/D��#��\.m��u��#��M�k�����Z��=q������'�hz�P-KNN��{�9��_�~�������
��g��*�j���j�~��7�*M�Y���6j�(M��4p����o����w����]��%���o0�����;w.+����d����x�t����w�t����4����_|��Z�����5{�l�[�n*M����B��6��sN�E^D�D���SU���-[������}��>�0o
'�����)S���,����U��o�5�l��\#,X�]{����7m����Fn��k������kZN���>��n�-+`_k��U�u���sU:���W�^��'�Tk����d��~�o���c���#Gj��7�hj����Z������n������<xP����K/��z�����P��#���:1q�D����eK����=k������������#GT��q������91��4z�����k�����!1�����	���x?�Z�.\�����M�6�t^;/N��CT��?����:����GDD��hf�'�0��~�'zL����)#
OP��L7�&�:�~qQ��ys�<��q�O;�A,�������-���P#w��3G���J�o(U��x���ysU��/�����������7d�_���9j��5j��*��@s�P�o���K��C�7���G<�H?�GN���h��&+hz�'�f
=h����CH��U}�<9G3	�
|����dr�]we]7r��o�����"��vM�B�@��f��{�}���f>�:�:uR�i�;X�q��A/�I�&M���a4I4MA�N��b�0�&`�q��i6A�{����s����4�����{T/4
KLL4�����fjpb������}�Ts#4!�����������w�
��t�l�\�pF?w84��5;?���"ob�h�h���b�:|F�o�����y��81�CT�!���'��`
.0�jV�D�i���!w���n��oV��M�����mB0	7������d�x��:nfs�c��l��/��r#�U������p8��Fuv��Q�USq���������6��6n������{o���Bn�p#�f!�
5��0� ����>nP1��,nJsN��1�J9F�����u[�5j���@a�(?� �� �'��
Q����@��������Z���3�<� ��������p,�~"/���!��8�g�P����n!���E0��������7 ����}��������� ������Vx0�2
���Q����j���%���^
����#��b::��C,�}�_���|��C���AW�S#��a�y�u�<y��l��A�C�}����&�����yqb@���G��6�w�q��d�� �����x��6��������,��f��7x������Mn�O����|�0o�<�>OA��7�|S�c�?��S�G~��'�q��������c��V4��7�f���'z�e�-�?y*X��[����N��T�dvV�#��&�|�'���B_�X���d�1���SO����b��I���(,��������j�!���:����[���NN���P�L��G`�z�6=�%�`.l��On�����`��)�&&�{��"j��vj.�w}s��s��Z�$
j���H�A�T���B���w:�����?��j�a�:<��/�u���x0j����v"���6�|�}	�7�9�������v^��!*�����R�`����A'ox�,�tZ�q�f���4�<��� .n����KT���v��z�4��;�X� �f�x��n��9+nB�A�S@4��M�?<��;@��`��o�'�7�G�T��D�7����MwN�����GA��GQ�(l��,���Ncq�����f6g0�c���	�?����uh���!�������S���	:;����A	
u��"������0j����&&,��oF�A����#���
��������D�@P�X������,���]���Ox0�Z*�!gn�_B���u�D�����x�(o"���&^;/>��b��b�v���-�M'n��`k�  ��8\��A��*���DnD���J0����7�9M���M'�8P�M���x��^����D����������tv�`���'�����U�Q�EA
��]	F���91�F��S��'��a����!j�s��k�XC�1V(�����a�T�A����;���Un�c4�*Q���0����'���q���3����I����&HQ�l�Q�	�P��(>h*c��K
�:��A�`
�S����D���A��r�� ��^������'������k����y,P�4'�d
���yqa@���P	��4�}��6�.�l:�8�m�V��a��O1p��@�BB��`4�z�������?�?�p�I��|E7��gW=�l�JM�p�-j��U���7��iF{�@A�@�#��s�f�"��=&�����A�(�Ch6��B�u�����z����m�q� %�6�/�PP�k���a�<a�����+�hJ��8p@5�@��`��\��x�����8������7�L�p��49
t��w�l��������a��v�?(���k_8V8>���v��N���.���9W�x��80�CT��	~��oN�VT�
 �3FkA.'�a�
���n<Q�T����<��2\�PS'���L�x7��<�����
%}����[�F��3V�����
ORuBIT�A�i�s�)+T)7��u�]g��.�|����hv�����*/zjH8�7���}���PhF!���J�P���Z&h����	��
'�<dElh���_?U������:��
����>�YY���"X���<�<l������>���?
��d����y������V���;�fDy��P�,����X�&��w~�����X�	�"������k�&������h���C��?H��k'�7�=����_������w��s����:D�nBQ%�^xA572�&��O?U7f�������%�Ag�������'����W�'b�R���xp��M!���F4��#�@@n�y��OGQ�5/pNa��UJ���Agu�x��K\�������M���qSl@�� ���nj��Pi�3&Q��v ��)c(�������(��}������m�'b
'��	&���3T�����Y$q��Y�IQ(x���" pb~/����M�P@2������T���z��~�Y�e^����cAP������!C|�#
h�m�<�`(;x����M\���OkM�;�����q5�� ���6�F����~��u��M@�B�`��y�@�=�{�<����,����=���^��,����E��x`��~�5��z��o��~���d>����9A:���W^����v^��/�bG�����������j;v�����_ T�~!��/_n����'Oj={�T�x�����-[����~��<x���B�Q��I/�������W����M�9 99Y{�������MM��U���_M��i��V�s���:>�������Zi���v��qcm�������5J}�V�Xa������&M���,���g�����m�f�������|��Z�k�����L������o_M��T����k���Z�h��y�z��~����D�C>?~|����:e~/����&�9���L�:U�����jz�0���<�t��)S4��el�������w������w�6���o
�e�����Zff�Z�}��SO=��a�yC~��;����/Q��={�����g�~Y����5�
s3n�8�Mv�����=��3*?`B��w�x�d����D������h~f,��5L���k�������E?���MS7����:./��rH��`����C�����
$��'N��sQ	d��MZ�
�v��i{��5R/�j�*�[�nj�X��p��x�bu1�^�pC�g�c-/\qq�r�"����1{�l---�X����|o7n���3&�F��`
�������K�.Z���������M"���;�p�BcI����������>7��;w�����@�/����w�}Y��	|�����wy��.�4�����#G�T������o@v�E��������#�h���F�/�?��<�u� #11�X��]�5k��}�Cv�	�n���������u��/�c0�L���Q}_���
qO�5 �l����f]��]G~���e��k'���]������8X������v^\l���G&"*���a����.U�<���DTtP}�
������tQ��&�h�2g�5�
�vRa`:DTh?�(WG�
�c8"*Z�p�w���7�D�:>E'��
�Q��k't��@!������]�~���5Kub�����r:��_����|�v���f&�����0����A�����3l2
^;��1�CD*--M
��k0�2j��{��YC�Q������Q;0�:�F1b�5������;�U�VjdF�����^���s�(;DT�x�����(�p��V�lY������?��N�:K���j���.��0$2�Oe����U�\9��:t�P5�2�+&���k'6v�LDDDDDDDT���Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	��Q	���X/����l6�6m��"jiXF%���{��w�����)�k����~Q���;w��;��S�l�b�Qv�!"""�b��rIff���`�o�^4M�g�}�H!""*��!"""""""*a�!""�|e�a�8 �>����[W��+'��v�,[�L�n������������K�.j�5j$c���?��C�������>}��x���x0a�������tc-_xO�7��b=&�������UW]%��������o�]��{���h�"c-Q��c	��p.qNqn�>�5�9���oa�����������T��Z���{O��@p����7b�Y�j�����u6m�$|�����I�^����S��;q�����+r��W���������y�����s���I8����(�0�CDDD���}��2i�$�]��4m�T���+�_�L�0���5��/�N�:�?��9z�����C�v��
�����
l���O������?U e��A�v�Z���kT�|��2d�9v�����93g��:�c�5
�8�`���Cz�!��u�
�?~\*V����s�9R�	��c��"
����!�%�)�-�1�5�y�~����~3��;(�L	����_�W\�>6�G�V���
���
�������,={��O?�4+HW�B�����-[V�����]�V-��s�]E���[n�'�|RRSS�{�< ��������m0�T�L�]��M�6jW�
�<�s>�����~%"""�7S�NE	]M�{��v�����n��v�Z�u���^8�����������^������7�h.��X�i������c�����v��yc���7N��=M��c����s���C��q��qc��9rD2d�Z���j����M[�n�V�N5}���Y���L��>����m��i-Z�P�1b��� ##C�+---�3�9���0����]-{����6&�C�)�-�1���O�����Sn��*�r���7�T���)$''k&LP��s�sm�6L��%K�d��k���w�����<O�F���;d'33S{��g�{��a��l��Y���5k����b��?�>�/X��W^�:���""���:DDDT 7n,/�����WO�Fs�k��V���z�fDIIIj^/��ZP57��2����cU;B/8���e��u2g�����<��sR�Jc��%�\"/����
�c��y�Jw�\���_��}�������n�)���/j���PDD�5g���2l�0�/8�/�fBS���{��c=>�?��c����|pP+P;��PK��Z�h�^�����5K�5���)����< T��s
iiir���wk��y������;��RQQQr��a����j9�j3Y�7��5������>�~�J�OV��'��l��{��{A8����(�1�CDDD���O��#
m��Q:������x�
���ViVHC��p�`���?���2U�V5�x��QC5�A�������0�B;���	�	M�����+�4Rs�f9��U3^y�_�^�w�n�T���M�����#���U+��W�~}�y������/�y���_n�z������1���\�Y�F>����@��������o.����?�Pn��~yF�������������p��[�V�Z�l��""������
��P��&P������d9x��,Y�D�4A����5EL����Y0Gg�����j� �R�re�,����j�L�2E�P
4��@0N�<�����Y��
��%��.���S��@����o�u�Y��?/���W�GFF�O<���A�	�� ��s�W�~�q����` ��M�4��~X~����;&F�}9��Am3�p�Q~c@����
:�
�'��z�@�������TA�@A���Q��"�RX��l��A.\p�2+#j��������Y��W�c��z�j��:s^�t�j�����>����/U���0Tn�5|PK��GQ�B��o��j����?f��`�cl4aC��h2��Y��=DDD��"""*�EP��(N(H#@�>c0�4jr ����~J
�Y[��@5�
��l����,)�	5\ &&F�zA����f��tL�����WM����d
M��DM�P��\�hPS�NU����A���;w��m�T�7h���jx���`�����>�����oj�7��p�Q~b@����

��
�(4�0��7f2�|�h�������U���M�P0���
>j������g�a��JA�5���h���M�|����m[�	�G����4�����TG�f
fM�P��l����C����?�������>�L�����j���Jh~����U-Z�&�<{���V`�����*����>�m���8DDDy��491�������T����m������k����B�As��P���.���O��FZ�����:�]w�JC��e����t��hILLT�	��Z{��p.�<)��O8G�����g�Vi��#;�p~���}�3�G���?�������o�Z58:�C�#������zK�~��W�i���>zA�*�F�N��
}��
�g��1R=�w�g
�	�?�;�Zev���������U8����(�1�CDDDb��e��/d
��B3jc�O4YA�*(4[k�,^����	hz��v^�������o}�	�@S/�=���
��%�_�?�Af����'g
��~^`{t��c@s��{�K<�#8�j!�ek��!���y�����93jh6IDAT�y����k+��B3(�;����@jN������^[�/:��!(gn��-�����Cp
�K/�T��I�d���>����
������B�#t��||�f��������7�����3���tB`����R���aj='��""�|�_|��������SQ��z����n�ZKHH��v���i�F��-[V��ZFF����m��U������0��~�a�g��Z�V��?����J������{����2�;vh=z��y��;��#�_�~���G��=�n�6c��>�i����G��
�k�c����m���E�N�<i���{��_��{��Z�:uT�s��������<o8�� �z~233�����tL�����_|�E������������\`{��4g�������<�{!m��!������5���Y���r�=�\�7��v�Zu��1`�����7Z�b�J7?�������{�4|���4b��>�s>����:DDD����={�h#G�T�[L���6n����uy�����>��=�<���*����2�-�(�#����&�����}���ZZZ���/+��zl6���k�g�����Tm��YY�g���/��������������3����y��e�>>��xXa��������'l��<wX�����~x���g<��V���u���m����i�&cI�����Qf�S�����'��������s��&��_�����(?����CDDD�/��f���j� sD&*�	M��N�*�
2R����4`�^"""""""��"""""""��"""""""�E���kH�+�IEND�B`�
#21John Naylor
johncnaylorls@gmail.com
In reply to: David Rowley (#20)
2 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Tue, Mar 5, 2024 at 9:42 AM David Rowley <dgrowleyml@gmail.com> wrote:

performance against the recently optimised aset, generation and slab
contexts. The attached graph shows the time it took in seconds to
allocate 1GB of memory performing a context reset after 1MB. The
function I ran the test on is in the attached
pg_allocate_memory_test.patch.txt file.

The query I ran was:

select chksz,mtype,pg_allocate_memory_test_reset(chksz,
1024*1024,1024*1024*1024, mtype) from (values(8),(16),(32),(64))
sizes(chksz),(values('aset'),('generation'),('slab'),('bump'))
cxt(mtype) order by mtype,chksz;

I ran the test function, but using 256kB and 3MB for the reset
frequency, and with 8,16,24,32 byte sizes (patched against a commit
after the recent hot/cold path separation). Images attached. I also
get a decent speedup with the bump context, but not quite as dramatic
as on your machine. It's worth noting that slab is the slowest for me.
This is an Intel i7-10750H.

Attachments:

bump-test-3MB-jcn-1.pngimage/png; name=bump-test-3MB-jcn-1.pngDownload
bump-test-256kB-jcn-1.pngimage/png; name=bump-test-256kB-jcn-1.pngDownload
�PNG


IHDR]Te�~ IDATx���yx�����W6d��bO$d����Z�8T���Pt�o����V�*uNi��UJ�k��:��q�--j+B�%v%	�I$3�?���i�R3�����.�5s���}�gF��s�s�.��"��4WGwP�@�0���.�@�0���.�@�0���.�@�0���.�@�0����<yRO?��j��%///5n�Xs����b�$;vL...��3n��B�������0�L&j���2�����K����EG��m/������j���A����cv{@��������v�����Eo��������g�i���2��5j�._�,Iz��wQh��5kZ��������O��6m�h���z��e�X4e��{���g���$I����~����1C[�l��?�(OO��{�PB�����O?���{�ZU�v��s�N}��'�BW�����#�����+W��k����_��/�(Ij���j���2e��s_���j����y�.]����^xA�w�V��-��&�
/���}�j���Ef�t��YIRVV�$�����:_~��._��g�y���n���K�.�n�x�b���i��%w��d2I�����8�(B����U��
-�r������3M7g�������w�^�����TTT�z�!��[W�����������/4d����{�C�7��f���+??_W�\�������C={�THH�}�b�=BCh��A�����i�$��LW\\����U�\9�����7�T^^�$���s�������5f�}���0`��N��7�x��~���;���[S�N��Q����<y�<<<���!u��AM�4��������s� ;;[�{����~�����N�:����|U�TI�O���9sT�|y}���z��W��?h���~��233�v�ZEEEI�"##u��	����+��b�������SOi���z���o���#��*���u��	��7O�7��
��A;�J+B�:}��bcc�����[�*,,��n���=zt��QQQ���������[o���G����[�������u��Q���������:}�t��T�^��I�-Z��_��W�����_�������@q8��n��?�������C��o/�n�I�&��3g�(44T�t���Bc���%��I����j�*�\�R~��]�Y�L5h�@�����m�^����bQ�=T�\9}����\�r�1qqq2dH��[�n��������.]����U�����l��Q~~~�Q��u��O?����k���7n�]��������P-�5/���K���o���y��{��"�[�j�:u�h����|���
&www}���?�F���+�b��>|�^}�U�)SF��5��u��w�}W...Ej��5K��mS��������$���G�e�I7��x��i��?_'O��;��c��d;����ER���9c�X,��?������Zz�!�����i����>��RPP`�u��u����-�k��xxxX���,�g����f��b�,Y��"����f�&--����m�����b�X��������b�^��%::����l���z��b��7A��L��.�@�0���.�@�0���.�@�0���.�@�0���.��t�k�����������q���j���*T��������!(����-�1B�/_V�
n;.33S����7o�����}����GUdd�Z�jeP��4q���g�yF��-����m����i��9����$)""BaaaJMM5�MP
9U������qU�T��A�����������m���5P�9���?���S�������U�n�"�e6�-sqq��b1�E�Y��5��IG���(������W���5y��B3_�]dYBB�bcc��XBB���)��k��������-[�6m�8��������Y�f�RRR$I��]S�>}�p�B0�������L&�L&%%%i���2�L3f�$i�����y���i?~\]�v�nc2�4~�xG����������rrr�]�w�^���={r"<0���t�d�.�@�0���.�@�0���.�@�0���.�@�0���.�@�0���.�@�0����n�-;���k���������K���5�|=����9s�F���v����+zvi��}��r5����u��R�F����/�A����+��t=��3Z�l�����
(Jm����tt�)���Vbb��fs��			����g��.u�o���e��R{!t�Attt�e			���u@7�kyW����m^�y���Hw��@�Sj/��`�R�


d2�d2���������d2i��1�n
8�RyN����rrr�(EJ�L��] t��`B�] t��`B�] t��`B�] t��`B�] t��`B�] t��`B�]p���k�.EFF���GAAAZ�re�c7n��GyD���
��y���6N�rss��[7
<X���Z�d�����{��=���={�>�/��R/������[tJ�	]���2�L6l�\]]��U+u��U���E�;vLyyyj���$�V�Z
		���n�N�:����B�BCC���RdlHH��U��U�VI�>�#G�(**��^@����l%;;[&����r��);;���2e�h���z��'4z�h]�tI3f�PHHH�����2��E�'$$��y'�g��.u�o���e��R{q�����������333���]d�O?���}�j��
j���N�:�N�:�F����_�����E�OHHPll�m_���wE�.}��u�7o�F5���.8&J�9����b�X����_
6,26))I���j���$�F��������+���������(yxx(..N�����q������I��5k��w5n�X��>���������I������.�]�V��/����F���K�*  @�4u�Tm��Y���Y3����w��
V��M��sg�3���83�9�K�"""�c��[���wo���<���y�#�p��.����`B�]p��K������-6�;$��BkU�y]nz}�7��z������R���v�#>>^������g�}����������[��~t|8������O+q�O����G�c��Ij���S�./���a�"""���`}����$����c�*((H����#�<�o�����w�}��-[*$$DZ�b�$�g��:r��:w����x��&[b�����\���G��/WLL������C���j��=Z�~�RRR������D}���j���233��{��������iS5n�X+W��������K;�%�7f��}�����'-��n�w��QU�^]�����%Kt��9EGG��w��$m��I~~~����$�n������V�Z���b/�.`�-R�����E�h�Byyy2��j���V�^����+44T��7���[%Ig��Uzz�����+���/~5���Ep���[����k���
��l����u}����cG���k��y������;�����^�z�������F��c���.�J�*�S���f�f��!]�zU}������|���+<<\�E���}{�9sF�������l
2D{���������������f3�t�"�x���{"���M��z��W/�X�B�����������h��A�������$��[W����P���/_.I���UBB�����c��l6�W�^j���\]]��o_u��QS�N��q���������S��]����[_�u�e=z���y�$I��-+v�-Z���o-]����(�]���f��\��4{��0CB�����cGI7�nZZ���� #v���A���mk����0��H�`������X,�����i��)//O�?�������{���3]����������?�Xo������������5i���-8�!��pss����g�=��c�$��"��l����3]��uS���������l��JLL����n���v]�>��������,u��U��������L&{��D0���7)�t������BW����}��b���f-[�L-[��W%��B���Su��U9rD,��!CT�vm]�rE����p��~�a{������l)~��I�[[�;vL����~����������]	�$���[�5k�5jd]��Cu��E����q���E�p���y����$�~����\���������������������/^�z���j��=z�


t��a�����I}��gJOOW��e����}���Y���������j���/I����d2�4g��o�^aaa3f�


�R���C��Q�4`�u��A�
R�>}T�~}=��C
���v�����H���(((H+W�,vlVV������B�
�Z���L�b�^(�RRR����}��i���Z�z�m�{xx(''G�����y�����^xA�+W��-[��[o���^�t�������v��6o���;w*!!���()��{�1���O8p������U+}�����i����l����\u��M�Vff��,Y��C������?l�0�-[Vg����;�n�:����f�P�3F������WLL��l�rW�=������0]�~]�z��>?y�d��O>��$���K�;w��}8�!g�U�\Y�
��>���e2�4l�0IR�V���kW���k��������/Z�v�N�<)���:u�h��]v���������b��:~��]m���%I�	��>�����+Z�����>��3]G�Qll����T�f�B���[�����CEW���*%%���}���z���;w�������0���;6����������.]R�J��A�b�X�]�|��������W�R�?\�Hv���2e��5k�7�x��WHm�����"W�/W������������'T�bE������Cj�������W��)11����LHH�cO9��so�;v���!������W���{�o��o���e��Rp>��#M�2E/^Tbb���}U�VM���:x��6l�-[�(==�����������Z�~���}���{�������?o�&�///ee�E���)oo�"c���d2�4b�IRxx��������	]���E�OHHPll�{���.�=x//��4k�L]"�l[4#[:���%�mx��o5o�\�j��Kmpw39p[�,�����^������S��WW�F�������{�[�nruu��3��G��UK?��Z�n���uX�zu5n�X�.]���A`��5x�`��3G&L�������5s�LY,��R�������"c�������*;;[����tc�����n��@)�sO�������:th��&L��	n��o;zZ�SC������������t%$$h��������9]QQQ���P\\�����q�F%%%�����5k�X���]��:t���_~Y���JKK���~z�Y-P��6�=H�>�5q�D=��s�\g�s�<<<�v�Z
2D����j����K�*  @���
<X������j����T��*T��W^yE�;w�Y?�e��u�6?999:}��|||��-������������[�y��5�a���l���4v?�����^�z�|��j����V����`m�����(1��f���
*���s�v�������h��!�v���wP"�=t���G&L�RtssS�N���E�bo��l��j���m��Zv��5����V���wP"��D�	&��G����k����o���]��0���G���u����.///���+����d�]�?� C2�����~�K���O�������,???I7�	��G�c����`��%I���S�
����M���k��J�*�Q������������K�*�d3��rqq����V&M���M�ZC���;���[�x{��9],��o�Y�@���j��������{`�E�)((H����8q�:t��O>�D���w��e��
		QDD�V�X!IJOOW��e�x�bu��Q���z��'���*n��G��|���={�*U����Ok��
���P@@�������K�z���#G��s������$EFF*99Y�����W����_�����wW��+��t��?�����W���)S������������`g'O������u�VEFFj��������O?���L���h������~��g5m�T�7V������s��i����r����������~������t��5]�zU/^T^^��������+&&F�������bcc�r�Jyxx��/��^���K�.�[�n���W���u��I=���j���6lXlo������������_?��c�Oggg+--MU�V�����kyWt9��Mk�����WM��Pr%''+$$���~��!z��%I�6m�����w�.I�[���v��U�VYo\=`�I�������u��qedd�q�'�|R...�����'��s�F�7��|��QU�P���7m��*U�XoX�fM���*!!A
6,��?��{/>��cZ�h�j����W�j������?�t%��Y���{��5}�V���c��J��/�b����...���={V��������������fX�n��TPPpW�U�R��x��EZ�j��f�$)//���8���S���-�T�����o��a��SSS����'N���K����"""��k`��zH��������^�������������������n�<�n�:M�>];w�T``��f������������;Wh���rY+��H/I���S��=5p�@=��S.�L��-u��A<xP��Y���|IR���u��%&&J�q���!C�g��g��e�.�J�*�S���f�f��!]�zU���rssSFFF��:t���/j��
�n���=����(��g��������]}���^����wk��]�����Q���{�T����4��2$K��mI����T�v��]�z��3g�������z���F����E���JHH����5v�X��f���K�7������l��W/�X�B�����������h��A��q������;j���7n�u�
*h���z����s����]3f�P�6mn;�G�X�������3��~���[9r��L����k�Y�f������d|�$$$(66����s���/�l���A�����;�C�J
�N���k��z��,����W:/W�%��0�������8���]eKrss�>		�����.]��G	5����m��)S��z���e~~~j���~��'{����k�R���0]RR�N�:��M�:�������f����iS�e�����D7��+W��-Z�����N�:3f�V�XQ��������z�����uk��_�Rzz�&L������������t��M��ust%��CW�������/��B������Z�l�
�{�%�]/���)33S&�I=z��_��W���S��-@�c��u��	���h�������45i�D���:t�&N�h�]�8v]qqq������>+Iz�����o_m��Y{��������e��	�Dv]��� R����+���O���5n��KF�R�n��L�2����<yR'N�P������_�n�_������5k�(44T��MSLL����q����t���_�����=�?��O��������$o��]�:v��u��u��=�v���n�h��������T����p�[�N�����?^�IDATGFFj����������}�l���srs��_��L�kD����n�}||��W_)''G���W@@�$�r��Z�b�&O�l�}���K������QPP�V�\y�m���T�V-
>���PZ��f�;VAAA�W��y�}�����a�EDD(  @��������_�x������U�j���*((0�%���o���Q�����{�1��'77W��u��������%K�h���:|��m�7n�<==m������~�z���(--M����>���"�rss��O��1C����E�4t�P]�p�:&%%E�����o�V�^m��������(���2�L6l�\]]��U+u��U����n��_(55U4�S�O���u��y-Y�D���Stt��y��"�<==u��	EGGK���������G�Z��3F������WLL��l�b���+�9K���C
+�,44T������;V���Z�j�-���t�v�x��5==��D��6�	%U��
�z�j��;W&LP������o�u��E�.Z�H�V���l�t�67K�����q��u��q��8M������d*��\�r������1c�h��Q
		�m����BnJHH�cO9��ngYYY�l���.e����r���t~���:^���]���>H�����Mn�n����z���9����w�s�yt��Q;vT~~�����������s���[�N��O���;(��,//�Bc.^���5kJ�.]��J�*���iB���W�+�gff��������������uoN�VBB�bcc��mv�ui��;��W�����;��~~$��iMw77����y��jT��Mk������L�2w���$�H�E;�+�P�������~�����>�H�����}���+<<\����.�J�*�S���f�f��!]�z�P�)S�����JLL����o�K��	]����9s�,�������Wddd��+W�TZZ����+���Q~~�����i�&C��$�_����)v�o���O�{�VRR����+wwwU�PA��//�]�^��b�
���G�����A���?�����W��F�)##C�{�V�n�l���iB������4z�hm��EIIIz��w%Ik��QHH�����\J��7����'5o�<G�@��D��{����e���r]@@�u����[_�u��=z�������$
:�^[.�����Z�v��/_.___�9RK�.�^l�������������tIRDD�v��q�u{��-v�I�&��%IN4�P��@�0�S���
'�I��?G��`�z�"]����Y)����0W�m=�����������o��h5(z!h�E������:�q��5�}	]�8���.�@�0���.�@�0���.���<P��~Dg3�mZ�IPU5��o���{�.�7W~��O�����mI�`w^03]�����z�?16�;��fU��c��0���2*�6f������&��X��^�g��xp�;�6�J���}�w[I.�/�/B����
(A]`�G�����k�e�����T�k��]�����������r��b�&''�Y�f�P��g`���q������n��i��������%K4t�P>|�����L���j��q������k5i�$m�������iBWrr�L&��
&WWW�j�J]�vU|||��yyy�3g����'I���PXX�RSS�n�Ns"��C�VhYhh�8Pdl�*U4h� ����t����m��v�J���9�|��6��D�����y]�N����e2�
-+W����oc�S�N)&&F��OW��u��OLL��\�bt			w�)��}.b�����6�y�R��l\3����o��}�����i���/���$�]���%�������m�K�.���n�����{u���?�#G�(!��Mk�w?��j���$����?��T�i�~V'.�j�R���_����_�]���?�MIII�v;h���?�	]^^^��*��833S����n�����w���<yr�������.�,!!A���w�);��4��?����9����� ���H������M��7W�mlZ���HI�lZ��G������G�i�����h��S���w�sr�fnZ"��f�������mg��7.1��5%�/_^3m[�~���}�������Hv]���SX��6�kk�/��������c�=&���k�����8�9]���:x��,���������a�[���}��w��O>����`+N����������8���k���JJJR���%Ik��QJJ�$���k����.\�6ml;cp+Nsx���Ck����!C��+��F�Z�t�$IS�N�������D?~\]�v-Tc��z��w�=�a.-�2�����k��d��&tI7.��c��[���w��q��=��b��J����})�m=8�
]�G���%�Zm���MRc���`#t(��e��,�m����.�8���%���.�@�0�^<0�������WW��(����������l[��,���$p+^0���.�@�0���.�@�0���.�@�0���.�@�0@�
]�v�Rdd�|||��+W:�%��Je����U�n�4x�`effj��%:t�>�����*��+99Y&�I��
����Z�j��]�*>>���'U*C��C�VhYhh�RRR�pv.����&����oj���Z�j�u�����������Bce6�-sqq���m�t2#�6M�F�+*g�m]��U.�.��iq�������n~rw��i���]���iMw��Y.���$��C����������6����&o�*6�)I�/�)7���lUv�U�,�6�)wW��m�$�|����m���\����e���f��d����$�d��b��U���\�����u������gJ��������������K�.6��4wGw�^^^���*�,33S���E�FGGY������X�����zp�Y=����D���������:x�`�������a���
8�R����������8���k���JJJR����pR�2tyxxh���Z�|�|}}5r�H-]�T�n
8�RyN�$EDDh���n��r��~��[��-�.�Y=8��|N>���T^2�`���0���.�@�0���������K����~��=z���l�Z�1�|����g�"��L�"����w���r��:�$%''�Y�f�P��WdLVV�j������;�CH������C����_+VTLL�N�8!I������cU�Z5U�XQ�?���=����l]���O?�-Z��������g������n��/_���^QQQE���7Ok�����{u��	��f}������������q������k�j��I��m[�q����������$M�8Q������u��iU�PA#G��$M�6M;w�����u��Y��QC��
sp�p6��R�������$�d2��G�����$�A����oT�^�"������S����_^^^��������:�K���i��9�����w�Sjj�u�_|���T
8�QmB���?�9s����G�������>,Ij�������J�*���C=z�(��@�*�:w���+W���@YYY��i����?;�-��/�2e�Y����(==]?��5b�egg;�KT�RE�
�>OOOWjj���m+I������c������r)::Z�������Ok�������$�S�N�����MHH��B���ro�����_�J�*�j���]�����p�/_��l�����u�V���K�k�����J�S�N)&&F��O���e��15j�BBB�n
W�5T�LM�<�����{O�7o�?��OtgF�*������sg�5J�.]��K�d2�4b�G���x��������#G�l����������~�zG�V�}���j���^x��=Z�����+==]���spw����]�xQ���S��me6�%��7q���Z�j����W~~~�����
�q�;��j��!rqqQ�r���O=��s�n
�����:u������e�E��t��}�z���e���M�6��+W�TZZ�u�+++K���JKK��M��n���'��c���Q���������]�f���3gT�Z5=���ruu��M�nyh�_�t�b�j�R�
�j�*IRAA������I�&�w2d�������������{�)&&��m�J��]S�>}�p��B�K��N�>�c�����cz�������X�B&LPvv�


�`�U�VM��U�|�3g�h���.�
�������u��`��~���|�������� �C�2�L�:u���Y#��d=���^PDD�������0M�4���N���:~���v�*��d�3~�xG�������u��u�r�����/�n�:���j��y��m���+W�s�x�����q�X,G7���1�`B�] t��`B�] t��`B�] t��`B�]�x��w5x��"��e��Yv�8�����#G*??��m�]1�8���g�_�~�����5k���(�<yR�d�X4~�x��[Wz����|�f�&M���������'�������^}�U�v���={v���;���(��WOu����o�)I�������K�^�u(  @���rqqQnn�$i���


UXX�F����<��W`o�.�����i��
����c�5m�TS�N�$%$$����RJJ�RSS�g�}��g������*11Q�w�����e�X4}�tI����o}���JII���{��;�����+���Y���S'���i��=��k����
�y���t��1;vL;v��a������k�j�����}�8�S�Ni��y6|���]��k���*W�,I������~k}�}�v�L&yzz�E�����o[��/�P�~�T�|y���h�������$�q}��Q��e������EFF��Z�j��q�v��!ooo}��g��������Y��[�j��Y���k�j�����������5k���{F��.��U�P�����G��_~�E&L��C�����c��i�����u��yU�X�����O.\��{���
���&N�h
L������5~��"�~���9R+V�����������;W�TPP��U����G�������322��i��I��m��Y���T�R�����f�����'O�\�r����.sww�K/���^zIiiiz�����m�"�1b�z���v��Y����k��I7n�]�r(Y8�8�o��FG��t�����(I7�X�F�����}��i����z��mku��U+V���k�d6��h�"���H�����l�2]�|Yj���RSSm����M6l�$��UK>>>2�����\�R�����d�_��-]�TW�\�$��?_�/�co
83]�����������N�:Z�r�$��������p�B5k�Lo����~�i�n���Z={��?��?��O�X,������%I=z���={��AY,=��sj��e����y�1B#F������GyD[�l�����t��Y��_��������������z��Gt��u��__�-��;��b�X,�n�}����:x��,X��V�����E����*(]��"��qx��.�@�0���.�@�0���.�@�0���.�@�0���.�K���&����/N����IEND�B`�
#22Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: John Naylor (#21)
2 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On 3/11/24 10:09, John Naylor wrote:

On Tue, Mar 5, 2024 at 9:42 AM David Rowley <dgrowleyml@gmail.com> wrote:

performance against the recently optimised aset, generation and slab
contexts. The attached graph shows the time it took in seconds to
allocate 1GB of memory performing a context reset after 1MB. The
function I ran the test on is in the attached
pg_allocate_memory_test.patch.txt file.

The query I ran was:

select chksz,mtype,pg_allocate_memory_test_reset(chksz,
1024*1024,1024*1024*1024, mtype) from (values(8),(16),(32),(64))
sizes(chksz),(values('aset'),('generation'),('slab'),('bump'))
cxt(mtype) order by mtype,chksz;

I ran the test function, but using 256kB and 3MB for the reset
frequency, and with 8,16,24,32 byte sizes (patched against a commit
after the recent hot/cold path separation). Images attached. I also
get a decent speedup with the bump context, but not quite as dramatic
as on your machine. It's worth noting that slab is the slowest for me.
This is an Intel i7-10750H.

That's interesting! Obviously, I can't miss a benchmarking party like
this, so I ran this on my two machines, and I got very similar results
on both - see the attached charts.

It seems that compared to the other memory context types:

(a) bump context is much faster

(b) slab is considerably slower

I wonder if this is due to the microbenchmark being a particularly poor
fit for Slab (but I don't see why would that be), or if this is simply
how Slab works. I vaguely recall it was meant handle this much better
than AllocSet, both in terms of time and memory usage, but we improved
AllocSet since then, so maybe it's no longer true / needed?

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

xeon.pngimage/png; name=xeon.pngDownload
i5.pngimage/png; name=i5.pngDownload
�PNG


IHDR-�	�� IDATx^��`U����4B����(�tPQD@@����^��u-�*�
XAA�(��  �#��I ��;��OrC	�����o���3gf~g�;_������(@
P��(@� �1�i���(@
P��(@-�����(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(���k��(@
P�j����(@
P��(��������O�H�"���A��a ))	�N���[�D	8���[�O�u��(V������y��(@
P�6�������c��u����Q�vmm�����>���m;��q�������r�.���z!�=z�{�F�B�r�FB��U������i������y�8p3f����{uX��r��I�i��^{�y�E�K�/�F�����q�UW�k������af��18r��4���C��U�=�5k����~BBB�y��E�*U��];��S6���6\��(@
P��$��"7��7o�7��K��-�'OFLL�/~�e�2eP�n]8��;vL����Q�[���""�f�����c�8�\sM���������q�������e��>�7o���U]����q������S'DFFb������O��K��u�����,�`h�&���k������+����0���?���3���'�����r���+P��(@
��@@CKvPRi�6m���J]A��"a@���Y���K��Y3���+W"11QWCf���F�eZ�
#!�e���$�5j����fd� �~�z���G�&�"����xP��+��=o�<�.Ti�m?a�H �}U�\9�]�I����#����\����r
P��(@
B `�e��]z��TM��R�hQ}��-"$���7�pJ�*�����l��?��C��E*7�^ll�9m}���z��_�����th��H�j�2��ao���`h9t��v�[d(��
�Sh��cR	�aj-��1�>)@
P���XhY�h��$��o�
d�	��&99Y�Z�j����&�����1e��-[V���������M�.Z�k@*-o����/3`���@�q�F|��7��!��d^K\\>��}2�K�,Y+-����v&� ��.�,c�O��R�R%=<L�T�d�L�=���\���(@
P��@	,�H�y$�&M��7�����"AEn�%�h�B��*�TNd�����M��j��+��~������Kh�j�_|�����c��	+V������#�"�*�Y�5k���[7����/���[n�C��\}�+.2H�����=�L��I ��|��>y��(@
P�@�B�������g-2�\�b�c�}�YW��,_�\��y'�&,]�T���W���U��K	-Ra�J���x�~���<�Y���9s�u�V=dL*�a�=wF�&A���g_h��e�t0�@���|C�2������/V2�l���z8��}�W0�\���9(@
P��@H	]h�IO��|�����"O��PEa���z����m�!��E��'��}	02?� ����C��G-��,�l��&sZ��a2�K	-��'�q�<���$�������	;_~��~��s�I�!u5�`)@
P��(��@�����?Y_6y��A='D���P��U���%/���dB��s%�KnB��T��1�2dK���;W2/����_~Y?�x��������*2�FBW��m3B���
��"��:t����d�&�Z��S�N������<��(@
P���E �B���D&���O2/��1�%7����#���7<��O
����	-2�]�"���/�RH��l��]Wv��/��xIh��56���%s\��br�R1�E*-2�K�/*T�/����J#��3/9�v���?B�d.��d�(@
P���E �B�����{y���"7�k���C��Ic9�i;v��X�{O�Vk.�i-2OE���W^��;6�/�C*+M�4�O�z������bd|���~�* C�.�������=-�KF&�y�e��-9��}��A�,�G�e7&\.X�(@
P��@�Xh��R��x��He@�}����!7�Re�j���!S�4.y4r�V�r|z�<V���^�U���.��R=�c�� /��'v��H(��e�p$�0�c� �����d��<L���O?E��%�0/y���KE�������iW�+�&�@/�+�g��:�U�ZU����E�OK�,��N�u��6R���52�_���"���#�/�����(@
P �Z��"O��-�N����������E],7��&z��!����/^�X����g���y��"!`�����*�@����'����-R�	�r�E���!��Xb9�$/m�yI�2������	1Hd���%szd2�E���=e���6�y�MnL�(@
P���I `��
�3f�����B
P��(@
��@X�yg�TdR<
P��(@
P <�*��G��,(@
P��(@��-�(@
P��(@��`h	����Q��(@
P�-�(@
P��(@��`h	����Q��(@
P�-�(@
P��(@��`h	����Q��(@
P�-�(@
P��(@��`h	����Q��(@
P�-�(@
P��(@��`h	����Q <R����[���m!`$%�0r>!���\���!q><H���x���E�s��s��30��x/p�mvD:��-4~.�����
�CK�d�"�I`�1/~_���x#���{�z����	�3�o�R��F��i0<-N'";t��Q�l������a�T�\��W��:�X7�������$���W������\.pd���8t��+�"E���9,>��[��KNK���V���M�+s}LG�����Q�D	��[7���|7m���������u�\��@~	0���4�C
h���xoV2��_�IU;���2E��N�>}=�����:y�O����������;�FL?u#�t�����o��������{/�V�V0RO���%������*{��`/qm�����{�n���o���[Q�hQ;v�f�B��MQ�N�Kj+�+�Z=6�|�J�
6�\���2��������w�!>>�����=�����?K�.E��}ux�B
P �Z��Gx<s����B���>��Th�1K�-L�����S�W����������H���Ew}��(4�~��0��Kjj*&M��}�������h[�Z�H>�BKs y���YT�������C����_�c���3�<�2e����@mTT""�g���+?����/z�7Vk��-��z��^/&O��5k��������N���[�?�������������"(n-���<
���B�/���_����c��A�Q��������^��N�	�T�����B���r��W�G�x���1j�(���f�����n�����\����"�r��"�#������r�-���'������O`���x��70r�H]����r����4�n���n�	g�������O����~����P��j����;���O�e���<7�_�EB���?���k�����Q�ti�������?�			����c�=��+�w�}(U��^o��e<x���$5�H��H�����S�L�>��
�UYO�X.��d���������g�}��M�4���J���~�������`�|��W�Z�c��b���([�,��/	>�?�8:u�[P�Gh���k���#�B�C��ge��U:�H8���/�u~��	}�5k���?~�x}�K�OLL��Y�j�.�:���J
P �-������?�Z���J�*��J���M��>�oD���4j�{��Ell��i]�d��M��]��o��o����"�En�f\n�%x�x���f\�H���j�����w���f��o������z2�I����J����fU������:���'����#���?��s)��B��/��dh�����Cq��W��w��a�W�^:`H�������_�������m������g����V�dIT��m�����������d��&�G\^{�5}-��������!��7��Q��u�p"�'s]�����-���>�l���z�yY�Zv���_|Q����d�f���Z�:����.�L�\L�6
&L����w����\+_~�%��]��GW6����#��(`F������.Y��E*2A�2�Gn�d��q����1�
�T^��i�o��f�]�v��Wn�n��T�X1hB���y��������M�L��Z����~���EnB��_�a<���x��W��J�Fn���\n�[�j��_��h�BW6lxI������{���$�%��+�*��,KuC��A����2�B�i��E�;w������+��B	v��]�t�7�2�O�]*�'�8�<���J�!_N�lR��mr
-b)��+W�c�8q��Z�UrL�$�H5F�E�-/�?B�i����+H��(~��'Z�Q��w���������D����'�K`����$~.�-�����)P��Z�!7����k��F��[��u�a?2�g����F�{���!7q<���L~-�5/T��-bd8���?P��d��K/������^��m����.�!C�d�9_����"�y�z �*&7�Z��SB�Tgd�U������^����"�	�����������������O��H��]u��J�����GyD���HeJ�����d���$})�GB��q��}��'8y��V�������B�"�|��R���{����$�&
	2�d8�����4��E��L���ab�m�6}mH����/���ps�]w��%��u"�E*.�}�@����5z�h=,�(@�@0���
0����M��A���������
��8p����!Wr�.�)~������/����X�BW2�J#s���rw������KY�1�En"��1o�<}/�%�R�CI���GR���L��"D*0r�.��o�$U%	=��N��o�������tr&*����K�d���"�'�S���q���p6�+	@�%(H�|���ux��W���j$aT������Q�&}._KM��IG��b"����i[n��X�;Y_����M��t��Y��K���M�5	}�[���I��J�T*d�������������?�97�&��\9F9o	�Ru��K�!o�=}�tl��E����(� 6r��}^��w)?G\����Z.&��)@�
�#�����6Y�l�"U�)�#aE~�,��m��h�
������I���T0d>���^�
�o��&�Ro��Z��Q�E��H�G~[.��d�����d���Td�/�I�A*,�$�I��@2'C�	gr���#�Kx�*��.�
-rN�R!����*"��I(�?e"�	n�o����JH���T�8��8I�k���~��+���\7�|�>�^���`2Dn���sV�2!���O�2 e����6$����1�t�k����%����kN��R�	�r�r=K �9Br�K����r��u�6(s��B$�yH��'��@	��l/���>(@��
0��V��Q�~������z��������K6�b���"Q��u�<��	%|��|���������C�}��?�#�6$�
����������u1R��������M8K�G�W�i���zR]�j�4y��T��	guyo����yl�����{Z�_��\uAe�yS�X���w>O���y��_W���K^��L��7��*M�p:�?�xI]�
g~�Xh�!�K��\[_b��$s.d��T�	d���</�����8���MDV���}����q=yR��*9F�^�p����������=#����Y���v���C�y�BnH
P TZB��x��(@
P�( -��y��(@
P�U��P�97(@
P��
�CK�h�&(@
P��BU��%T{��M
P��(@�"��R@:��I
P��(@�P`h	���qS��(@
P��0����iR��(@
P TZB��x��(@
P�( -��y��(@
P�U������$������(S�L��^�{���ap8(Y�$�����			8r��v;�)��E����P��(@
P��#��"a��o���?�����c�����X�c��A�&Mp��!4h���u���7��a&22R�=h� �*U*|z�gB
P��(@
 `���vc��)X�t���	6�
7��;�����~��<�������!e����e���{�S�N�V
P��(@
P �Z�2"�c���p�\�������}���6m�T����W��>�]w��U�Va��:�H[O>�$*T��G}4����B
P��(@
,���	!9��]�v�s��X�|����q�F������?�����;w���?�����/C�7�7
P��(@
P |�:���:��?�����[����8p �,Y������<���:�<��S��������.�(@
P����dSN�����_#�B��c�aa2�>&&7�t���]���+?���
��G���`�����E��	�=z���[�(@
P��&��"�'N�����#��n���x<z��<%L����9S�N��7������X�bzR����A�E�zx����q����y-\(@
P��(@��hh�y){����fC�F�th����9sP�bE��__�S�={6�#�	3�K��=�o�>�#�%�T�V
�j���(@
P��(>
-���3�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��R��(@
P�~`h�#�(@
P���`h�J��������`�W��{>R�n��C�3
���!�o�{�l��(@
��CK��,�+O��l��LI IDAT�s_A�'9O��f�XW���enV�:�(@
P�J����2	,?���i������|�(@
P���p�I��_���y���wl�����'��$m��O3���`h�Kw�
P��
�CK�h�f�2�_�hX�6���M'�c����^L|��Z�J��T�����}��j����9��zG�����Od������(@
P�����ZHA
�	�B�/�����7���{�A�Z�q
�^����d��U���D
�N9�g���y�:=po��1l�KX|�����Zx�Q��(@��0����k�����/a����L�^5:#�����T�S�F�*mQ)�,&m����q���;g=���D�/^���@�Be���OZ
���S�(@
P��k\�j�
��V��r����H�?s�U�����7O��D����������^^��,o��%����:=����x�� ����K��H�����@x��(@
D��% ��i�
d��L�>C~���C���Y�
��45#��|��U�b�aT\v'>^�
:V�N��w�����Zd����%X/(@
P�A)������
�@����������������J��)��j(����O��7��&��u".���)|�e&z���H{N$�B������j|��Gl=���%P���R��(�-!�u<p+���'��������6��-�3v�C������.�\�*�9-�m��#����O�_��d5��a���bo�~\W_�Z�o*C��6)@
P�k����^���
���cE\���AT)\��S��{$�S1����O����"��u{�U��H�����I�k��sv;�����kl8�M���.�W�>(@
P�]����_<�s�{O��������s���q�����������(@
�^��%�V\�d-r������p�u����?{��p�)@
P���@@C���K�v�Z�\.4o��j���n?�$�z�-8p �{��G��}Q�zu<����z�����+c��(Q�����`��Z�:sVZ��e��(@
��@�B������Ca���:�,Y�#F�8/t�u�]4h����m����s�=�S�n��f<��������^�z���
�~�9��CK>As7�(@
P�Z����b�����������'/��"�5kv��K�)S����?��;v`��a�����=�N��R��-�B(@
P��@�	,����M�6�O<�U�����]�����*�8q_�5j���N�:�u��m���[C>��KEF��q�@^Z�*��(@
P���u-2��M�6zx�,2�]�v2dH�g+�����[<��#(V��^g����_�>�=��~X���K���OJJ���B��	�>�	#��y��L����L��(@
P���U2�"��],�H�(Z�(^y����B*/Xn���l�'N����`"�)�!c2�E��C��Q�F���K||<N�<iU���0Xwj+^X���g��5�X����(@
�U@���������Z�����"ad���9r�0U�T��5kP�dIT�ZU������W^z�%�/_^o���zb���q��q=�_��t����>l��	pxX�p�.(@
P�!!������'����6�
�\s
z���a���O�e����"���������=��^W	62q_��$''�\�r�RS�T����A�CKp����(@��-��R��y��*���=���(@
P� 0������'������(@
�CK��	�(�-���)@
P��@-�4(�I����(@
P�>����QZ��]S��(@VZx
P��-7��(@
P �Xi�oq�/�Z��{xp�(@
P����<��ZxeP��(@��`h	�>�P��%���5(@
P��A����2	0��r�(@
P��'��|}�#
�CK��k
P��(�J�
\\����F\��(@
��+-�-���CKPw��(@�*��R@;����C�
P��(|-��'<�
0����(@
P�90����@&�^�(@
P �Z��OxD`h	 >wM
P��Xi�5@��0�\��kP��(@��`�%�����`h	����Q��(P@Z
h����`h��A
P���O��%���G@���s��(@
P �^�$������(@
�CK��	�(�-���)@
P��+-�BU�H9���`��0������y�r}���^�H
P���
��bV0��Ou8�`�t����r���E���T_���=:����3���Y�vZ�f��(@
P �-y��
�����9)X����%&����>W�/�q����[-���c*C����(@
P��t��K7�-�^���������c"���*d��i��%_��3
P��(t-A�%y?�/�J������\n��3�-���2YS�_�������Xi����(@
P��@>0��3����Z�ln�m���3>�/���gw��mZ�=���v����j��!G�N"����Vo3�z+��JS����h~9�������fh���d��(@
P �-y��-}�E���
G����(^����<�u��s]$|�M�hT���E������$����Um�����{�����1�L�
\hI�Y��{1y��6�lX������hY���K��To~�����Z�p�uvD�<��������2�O��f�M�������y@�(@
P@0����pNhQ���'��c�/Z�p���I8/��jb��Am]���a�V�l���*�t���������SW����&�Chi������*��=��B�<��f�����sW�f��;;�P��
Jg��Xb�]c;���L�b�Q�����+��JK�,�T(@
P�/��0����Er��o�����`�Q_�5�=�s�[%�
-]�p�ae�V�%)����^���������nPz6u"IM��y�;BK��_v���@������S�7��������
�Z�E�r6U92���
���>WU�5��u;����@�Nvt����0���P��(�-a��YC�����I8q���gT0�~iZ���Qd����b�����z~^��GyY�X���.�����������=X<��*(6�����
��=-����D'�0�'����I��q^]��U��������X���]]���-��8<,�~x*�(@
��CKugFhI��2zP����S������Ud"�wK�>]���"�|�3��Q����8���TPC�\j����E`�a/~[�F�dU���D���4���^��v%;�N� N��C7��^iy�[C}m���
�|��j�����D���|7��<5�l�����Z��'��B
P��@�	0��Q�f
-o����Sp�������n5�j�
(���#�B��?�{P��
��p�Gj�7�@'�����a�%*-C���Y�v�����h���&����.�/�a��~�o���m8txz���c�4�����&6�1���	�*sCK��T(@
P�S��0���O��!�Y�����\���S��+����C�k�@��73��:�����H��W�PVM`��eq��aRi�=9p�u0��g{�S��������sR��c��aC�g=��5��O������$������*��Zn�-�T�=����%�8P�xZI�����O��(@��`h	�����y�q�6�����j��<��un�/fC��NLT��$�ti�Ds\��{1e���q��D�'��2��v���1yr`+-�Y�xqdFY(m������e|?�Y�-a���S�(@
P �Z��;�	-��������g=�,�����)'>�H�����{Q�&���x����Z�����B
P��@X	0��Qwf-:����/!�d��g{�����a�C���l��(@
�`h1�l�f
-VC�y���g��@
P��
�CKu4C���d�����(@
P�V	0�X%�vZ��3������(@
P�*��d�.C�9t�s~���(@
X%��b�l�eh1���b��[S��(@�Z��
@�-��Z��qk
P��(`�C�U�h���:C�9?nM
P���hh9|�0�L��"E��s��(^��y�9o�<��=;��m��A�`������o��������*T�`�SH���b��Z��qk
P��(`�@�B��'p��w��{�����e�<���(V��9������T�Rp:����7F��Mu`4h�u������G}U�V��*��eh1�E-���5(@
P��J `�e��qX�`>��3;vC��K/��+�����2p�@]M��L��%�p80�|:+W������>�����G�VY}�-��������(@
P�V	,�0u���3�<�0p�w���nB�>}�9���z�*UBLL����Z�j�]�v5j-Z���'���}�Yj�|�I����]�s]��b��[S��(@�Z$�H�x�����I5E��������������L�>]Wgd�	&`��m���/��R��z�������'q��Q������c1m}a�����,�G����e�<h�>wV�������c,���f�[����(@
�U@�gd�������]�B�����r����_�a�w�����;��g��k��%����q�}�a����1c���/�O<�'������s����
�6�Z��W{-?�_��6�(���i�o8�g��=����<4�U+�?���7}e�>�
P��(�W�O.S2�e	Xh���_��'�`���8p�����~[O��������C��z�)]=8y��7�|�+-�y��]�z�j=�����U��������<�������(@
P�*�����HPi��9�IbJ��"��#��U+���}��E�����aC��9�K�������t��ac������c�����uah1��-���5(@
P��J `�ENH**��/	 
4@�B�t�D\�hQ�/_^�#���Z�jz"~DD���!_K�.Ejj*5jT��������bN������(@
P�V	4�XuR�]�s=��b��[S��(@�Z��
@�-��Z��qk
P��(`�C�U�h���:C�9?nM
P���`h�J6�2��Cgh1���)@
P���U-V��]�s�-���5(@
P��J���*����b������(@
P�V	0�X%�vZ��3������(@
P�*��d�.C�9t�s~���(@
X%��b�l�eh1���b��[S��(@�Z��
@�-��Z��qk
P��(`�C�U�h���:C�9?nM
P���`h�J6�2��Cgh1���)@
P���U-V��]�s�-���5(@
P��J���*����b������(@
P�V	0�X%�vZ��3������	�M8�7W|�EW�kx���b�E��5���L���)@
P��`h��b����b�#Z��qk��o���ND��m�N������FY�>�(@
�`h1�d�2����s~��:�1�&c�����@�����[��tl��(�W�����v�Z�gN#u�R�wn�����HD^��*��=2�5�Z.��b91w�G������m+6���~N�f^��CK;��Q��@�0��s��$?CK�
,���������M���v8�j����-� �q8�-��&���Hpah	�+��@
P |Z��o�3�$����7_�&����nG�Y�Z�����M���a����!�n�L���{>��:����Z�D������U�c���Xrx-��i�����B�h,<�_o����}P�]�8<,o���(@
P Z�9�v���e�����[������r������v�_���_����"A$��Mc���Xud#^m����l���+&������sWE�bU����=n�hz7�U�I:�a
�b����y�-A��<0
P��	0���������e7B,kh��ad��2����S��@�����kW?���}�����P��� =|�����+�@����:���6j����W�����jtF�;?n���%�=�
)@
P �Z�K:�������P,\
��������[�}�9U]eQsU"�n
���z>��HQ��5�-*
�#G���HIQ���jCK>\8�E��f��'��f������q*%��0�$>��<5�<���{����<	���+����2>�[�:�Vk�����aF���H
P�@0��Q�$��WO"���O���g��8��������aa��C�!�pf�8$�>����-��v���!u�B��?zQ���%�.P�J�������F�xv�;8��������3��e�n_&����N%�c��pK�NX{t3���	j���},������`����.��D�<�7�(@��`h�/�|�O CK�7G�������Y��T�d#�g�L-���^�b��7!^=.y1R�-F����������g�:8���-�p�p�#�9��$���?�1���5O��f��I�+t�Q�e�K��e�� b�C�Z�m��'wb���:�o|�l�����8��JVZB�z��R�(�-a���-�?���#��8~����a%q������D�#O"IUW"�n���mF���g��
�����-����R�S��+-atu�T�d-N5���:=Q�xU=��q�����7����U��p�VO��������V����#��UCuuF&���N���[��M��03�m)@
P�r����o
-�����S��8�Th��H�&���*|D��VK��%b��B��%����*4���Q�wO{*Yj*CK�]:�S�d}O�T[�,�%��c��U��0�DF�������G?�����&�/��3G���D��p%���$��q{�TU\�<�z���[�f����G
P�O��%��<�����/���zr}��C�=qI�~��������`�9|�m:��`{�<�:����y������
�����I�^��0�6y*�r�r�s������'��}��y��Hq���e]�s}��)@
P�Z�k}���@��B�KO�O�g
�{���'��c\�7��[+�V8�7�Q�n�g�N$����������~�XD������&��qxX�^?�Y�
��rIk�S&���b�-[�(@�-�
���@�y�W���H���!b�s���BT�H���1�������p�ZG���h�R�)��O#����=vD�>X��O���@���s��(-A�
�9���9ty:�
+RI�EF�$'A��N
3N��XG����������M�S�E���K<��������`+a!������(@-&��m���_p��~Wd���3=9�MZ�����P��%���5(@
�CKPt�"���?�p^+f,�
)����.,(@
X ��bj��dh1'���������9#�5������].��*H`h��@
P�]��%���s���b��[['��b�-[�(@��`h	�~��Q2���)��Z����������N]=�����0���-�+-��e{�(r-!�e90C���dh1�W����~��n�t<+�(��F�Z��e��(R-!�]>X�s���b���m��<��Q��vTU8[meh�V��S��@0��@'��Zr+��z-��r�:)���SR=�R;�p�P��
�.�\�|[�
-��[��I�\��}��I�o� IDAT���>`h�����(@
P �Z��.��Z.����Z��������3'�������.�{m$����S���5�$��������V�,)iD��t���{����e��`W�z��
����J�P<6�$�]E-����(@
�CK0��%C�%�eY����_N[?�MoSw�-:�s����,�C���
-R���
,�`�n�������J,sV���@�h���]mC��6|1������N�w;;*�N�.����k�rg�(�-��3y8.��<�e�����_N[������]��K���|k�B��S���<���5*�~������_-m�WJ*��'^tl
\V���xa� s]3�S���{HN1��/Z5��W��1c-^5l��BI��%�z�"���b�3Z���&��i���{��S:�f>�"#����G[�>�������00|��?�@�������M#�w�R��:������~%�3�/�#��� ��f�����jU�1�Xs)�U
P�a��������b�3Z��],���������r��R��yQ��^>��d��l�P��Q������f����Hp	th�}xx��>�@��6<��u��������� 6�7/~�E�J����U����S�Q0k��)�xa����A��YsU�U
P�5��P��/C���dh1����"��SC'Z�q�b��LZ�
�/����j~���t�����/qc�q��9���CW-��92�W�q:9��>��EUZU�e���do�C�y���
�������O'�j>K�i������m�<Wm30�G/����k����8����R��@H
4�l��
�w����@��5Q�|y�[���E<��[�c����r��t���/G�b�4�����o`����d��h��bc}��	��0u�-����b����E�P��
=�:��27����E��o���X�����
3�[&�O��3no���c^,W�����x��$]q�{��W����7(B�����{0���*���%������lHT�J�k�t/nj�&����k`�
*���a�N����.v�o����!r��b�E�V)@
P �ZN�8�g�y�^{-N�<���w�}(R�Hb\\�^�s��:�,\���WG����CJ�^���e��E��]��>�z��3��dh1����R���5p��?S��*/���o���k���n��w%�����P��
��8�����P�a#0L�	������n,��BK;��E��nGZ5������0U):����6��
��b`�~C=Y���D����F5�A�����������MM��!&R��������R��@�	,�|��g�7o��	0��{/^z�%4j�(111,@��uE��/������^C�2e0l�0�=:���:`�s�-��rZV���
-cf� Q������X���?�I���^ah\��v�;P8�����h�
ZG��	I����NX���E[�!���'TP�q ��+�J���J�NU�����Q�-���$&�D�����������6��Wd.�
���������+��R��@(
,�H��~��x���������=z��w���(���H�%55U�]�v��_Dtt4���
]�9u�Z�n�>}� &&&��/���b������EC�
$
*���^9NUZd>�7E��
n��x�{\���&�wo�D���������.}����{y	-RiY�3m2L`������g����~��)hzDX�g����1�XsE�U
P�E��������
���>��
�V�Z��{���q���7nn��F�l�R�#U�f��}��GyD��������nLX`�K�|~3��D�o�����ZNZ|�B��T`}o���v�Xq�<0�e���w�I��#Xv�����c��z���
~^��C��
��k?%c���V�!Q����'%��Ew5�E&�/����wE��a��81s���UZ��e����-�_'��q`����-�
�r���>������u����Z�6N
P��#`Wo=�?��,�H8�\�2^x���{��H���[n9����C�8q"j���.]����)))�B��B��C���e�w�����8rD
.�e�?E0}��9AV���^{��/?��]d�{���(�8��=l���UZ�
	;���q��kB��,�G��������h�-�T(���%cmj
�M
�Rs?~MA�jv�����x0��K;�`��*��\�����w��eXYb��i+�j�~���+*$����k��q)u�?(�0��=�:*`w�Y�����lK�������[�6N
P��#P�ti�(Q"h8`����~��I��<	����/��Z�jA�*���Vv���K�����m[���r���u8�n��!>>>� �zs���
n~�����0s~9m=|B"V�N{�,2��JI�~
��^]Q�#�>g�����-��!:���'�8����$}�~�r�[`�Q/���wY?�7�jTU8[m��u�1v�dw��\��2��/-��(@
�U `����������\RRJ�*�~����+���M��}��z�������%''���P0��������`			�'����*URom+�C���gh1�w��"����i�F���7�<�#��s����?����G�B
P��,�����-�D/��$��;Vd����{Q�P!��by�����[d	&���e��#�|�x�����+�[eh1�?-��rZ��CL����Xi�J��R��@�	4���U�.C��.bh1���b�C��l��BQ��%{-�cfh1��-��Z,�ch��MR��@(
0��b�1�X�k-���7����Z
�{Z�:9��d�.(@
��CK�u����s���b����Z,@e��(�-��k��X�k-����b�������(@�0`h	��d��\g2���c��?�P��������V������j��v*����F}Q�pK�g��@h	0��V]�hZ�u&C�9�`-����+�l�����/��/�	W��lc�(K���(C���l���S��������|/Q2��y�mvt���5����(�-!�m�4C���dh1��%����4�{����dT��&W���0�X&��CE���#���zK�]���V�'-��BC��%4�)WG���+�Wbh1��%����<k�5'����F-���+�K��x(d-�U[l��w�������eh	�+��H��`h��|�C�9f�s~-�qx��l�_���68,�!b��r�C�|�1�����B_��%��0�Z�u&C�9��-F��m���������-+-�\'l5���N�W�i��j8������a��u��$~�E�*���f�����������	(��A�r�P,���92'S��v�0���u�#��U-V��]�s�-���1�����Q�����h>����{!uC$�E�W�p�/��-�����z����-"�kW�{�`��1�Xs�����Zj���kt��������W[>���?����S�������b��)��p�����������1��k��>X3�eh	�+�MkZ�����Z�q3��������p��1��X�O��r��{����s�CpV��kV����fNE���(����MH���q�K���	���oC�5�[
Q����e�+��l#|��U=9�Qm�����`����R4"����O6|�k/������GTEf��w�����nO�[��
-GZB���aS�J�+u��m�s�-���-�8*WEt�;���(��G83~,RW-��j�-�BCC����)����b��2�Oy�-�r;��~C�6�K0�Xs���8;�@�����t=�]?I�d���Q]E�ab�	(��TB7�(c	,�N�����������%���_����s�-���.�\V1����d��*|,C�o���z"#��D�b
!��m�����(4�^���
a�#f�}po�������8�������G�H9��������i0k+�`+�
6W1d��Hhi�)����1���T�[�N%"����q��1ZR�)-��[�@�	0��Q�2���L�s~AZ�Kh����N' �_�"u�j$��v��Fv����TD�z���R�?��xPWVth��)sf1�Xs��U?
x��c����3~l5SS`��"��:'��$���7��
?���c�}����j2���Q�H=,�H�	��k�02�����k���R �Z��7Z�u&C�9��-����a�����	~��H�sR��s��Ft����W�����ad��uP��'q���j��1�����^�D|k���?�����e�k�����
w�q����������:=���!xUL�Z�-��35�V���e�����QA�]�����DK:�-'w�c�Z,�16M�p`h	��T���b�3Z��[h��)���F���x��F������w�HI�sU�#���D���q#��w*��bj�X�q���S0��A��}����p�_��b�%�VW����_�o�(*�y�����E�t��|\���V[�G[�p�����b�]M�/�'�����[�CI�b���
���v�{\'�^�>�]q������!b���&e�a����>���Nd�A��������\g2�����"#�3n~
�UO+�3��R�Y�#�u8�^�3�OD�W�	7HVAF-�
�p����P�Jj��p���?#����:I-�\"i��{�q,��]G:�:G�[c�u;�����%mP�G=���3������,�{S}�k!�_��]}[��������C��R�{W�����sx/R�c��4�������%����!�zw����#�m��Y��!������U�L�����.����W������� ��F���b�3Z������	�X�;���w���/���7^B���i���������?����i�s������6���e�u��<p�%)�{��U[
����W�pe_pI;�C'l��94���l�������>����C���������U?����rI�d��k$�A��z��M�	#�4�C�	�\�;�!e�*��j���Ac}-$�������CM�R����L�P�
�+T�&C�9x�s~9m�En����O��:��V3�]#�R(�U�M�&|u��f��[�@���
|����w��c�|��	��Q���#?���
�50�v;��o��#������U��#���CK~_�����E!y�<D^�V��J�������w]����G�Y�.�%K!i�/��(e�����J�|&K��-Z���
�>Z���Z�u&C�9��-��C�E����/��
r%b����HR��x��>�*-|���RE���6O0���������b���Y^��������<kwx�V;
�����Q�u�Fr�i)m�>5L���D��[��th�K�y���#'
����b���G����Xi�J�l�������U�7�������:�{��Yd��G�A�7G���� ����:]�I��������Tp��j��`h�J6�2��Cgh1���b�_TU8[m��u��K��\b]1�s��V�"�������T.iC�.:e�TN�P������"�����8p]}'��6�CORq8��S���~�������W���6����
��^l?`�_���VS
��E/L�]}6��1KU����CK_3p���~2���
mU��.^Z��:���$�}�����FZRu���"�h�������T��*��x�z+�
5�����j{-����~��������<1�sZ��)l#[��0�0Z�u&C�9?��Z,@Mk2#���{6u"6
�y�[���F���Sp@U$���z������,k���n�Y���h���]^5o+��X)2WYn��`x���J�Z"m��GWS��L����dZd����f,�J�
eU0�M�f����2 
'��B�z4uFh�";^��F����I�|��P�E��d��N7�'��JT�Q�=;��xA?���`h�B5@m2���gh1���b�C�����!�\*�x1s�.�`�a#0s���ez���/�WPC��:0U�	-Q�z�X�]u9u����P�A�%�LY`@������h��=x��=m2�:H�/-����c>��b !���?(_R&���y�C�e�e@>=�]%���K��b����
r��{B����z���HQ5<l$����FM�^�
.��+�@N=7��W��9�4�Z���Z�u&C�9?��Z,@�Z��q�{�{�{1k�GWZ���~�^���=^��0]��Z"T��u�S?"8FU2�Vs���I8����J�������g����=%�L����v������B�jb�C
��.�l��1��&�;P���a}��b�e��S��@��?t1�������y�*�8/��'���.�E����)K"���a/TX�7�j��$�����P��W`�V
X ��bj��dh1'��b����?�P�-w�u�H�
�F=�U����N����
�+-j������-c�Q��|�z�H����MjI0�y?�rUiY�)�u��/�kW���5
��p�j����!N��Y������P�L���M{�M*��x���"���85��.��4l��"u�*��m�/��T�������x�j����x�C����SRT%�Q���T��c+R����j�� ����9p�C��%�����\g2���ch������L�%��.
�R�����r�����%'��=^
���S�Zb���!\����^�h��[����Ij�XTZ�4���=����1ig����gh��Zd�����m�	�����Jly��}�����A��\0���*�Wdh1�G-��Z,�ch�5KhQ7����^W�P��C����6m����d����M���]�E&�����C�W��`��`�
-��A��Qz�����g�+s[?8���{�*�$�N�M.�KnBK�������^d{�xq�(��=#���^
9�1�nO����f{�`h�R����b��Z��1�X���b���"_���� �
}��;��/|��Y��=����������sAD��8=@O�>7�X�K�k\��y�~3G`�����@7Vk��-�tl<{��0�2Z�u&C�9?��Z,@=7�d���L��������x2�_h9��Z�,;i8��>�6���Re6�������`�?</������s�ZFVP���B��bd3>2��y�.i��)�d�6�v-~��Kj������{e�s���b����?�P�-V�$��r^�I�{�!X�u2�-�n��l��C�U��v�
xw���7�� Tx���~0b�^S=�����%�w��&�l�Mr�;{���Z����n��%�T��"C��>bh1�������`���owS	!��{��T�E����bGQQl��^;��^A��*M���C����-��O�!	����-�7���d������|{����b�/CK�St.�2?Y���V_�U}�?��g)Z!�0��
�%�C5��������j���j�������Z�UX������	��,�&��B`*�2,�>JZ��,���,��+�V�3a����{A���mq���W.wIV�������������D���jn��Ol������f��&C�qM�U�-Y	��Z�k,���Z�ch1�G��R�������?	,�����9�����������%g�5�_������O�����j(�z@
�u�$�����Y�-��(^,x�a~]���$��T��^���UC�+��!R�?����J[��F�����R��t��L���P�v�?������W�ek���FO������3,&��OoG�
A�n��D��H�R�����d�Y��fbh1�%o^8C��=}j��DZ��ch1����T���0FB*���?���zP�����
��k�D��j��{�@X���9��q�����{�0�M|
�I��fr������=-��*^+��1W����t+KU�P��Q@���hX^���[������[#��1n�"� IDATX��c3-���C�<�1a�\��ch������`�����F�ELx4�/�
s.���q�w��=�����R�eP�g1j���y~?C����N�������Z��dhq����?�P}Z"���&
G ���zXX�W�F�����~u�k��c:#��Y:�����M[���S�-���x8.|�&�d8C�a7�n���w��G����M{J~bO������j�<v�}ahW=��W�ns��rN�S:���w 4��.MM���/����9s�'�!Mk�PU�.��7��8���5�kB�Z&�VK�'�����"xw�h��9���iH�'������C����5:|8g�HO�#:`�����%���A���0p�05����
��m�a��{Z�z�\�1zez�^X0�����Vghq����?�P}Z:��;��?m�z�S����HX0IkV$?����|]hi�����'����c�������c�i1�.�n����|*�P�B�r%X�KN��&����QRn���+Rn�_q���j�pu\t�I�����8)�_B���|9����M8����|��9�Y{�K�*�4a�
7��2!_��C;:64a�A�DA���f���u}h�@"��X�B�BK�=��S��`rhqnB
D�M
-����p�[��\z-��U�E��������gcO�o�=zv��8Z��ch1����T���:�	�:�X����w����
k�]�z(�I���KD�v*���9-1��
��C����0�n�,�����C�2T� G�Y�lU/8����S���)�V���E�:�P���Q/D�
�T����)��������N~9�tJ���e��t��T�K]��aP/3j�W	L]g�Z��v�����������mQS���r;�`���*�|o�w�����lxX�b
PNM���(��Wt��x��
���y?����Z���[�5<��P/�|O��e�����/2��sZ��C��d-�����Z�ehq����?�P}Z����������a+���r����)�������-zN�
8T�y�!JM���5��2��v�x�����������1k�
e�Q����%��s!9���J�0,�e�����]�x���kF�����&	%��0P�����3��������Z�N�s��TO��t9�7l
�Z)/���������3��f�S���PZ������@��m�>����!��(��K�B&|��c�4�����c�P$��(�T�]�i��*�HJ��LKZ���})�xk���"[��y����o��=sUh��y�����q��)��x�^�fy-����Z�k���Z�ch1�����'/"����2����$�^�'�Gv���m��E����Z3�����9z�B�W���t���:�#hN�7�>�f�iC�3�Bu���8��1�P"Ad���e���I�ow�������1���a�����$������$TR��q�����r��~H��z���1`3(�_v��E�9-#�9t���BK�	}Th������U?�������$������P0�p+3^e�a��T@
[�V
�{���n<?��j�*���&�3���V�������pE� ���N}���QhP�&���������U�W5�/���zf�T/�>X��9<���%E2�d��wehq��Z��ch1����T���j�s�BX�Z�X����	��s���������E65X�OR�����5�E��E�S�8�-��.�m\=��[�k�
-/���;�X����j���
B1w�[��{A����e�W^��o��;������
�7
C�;,��2-�$�?i���lS?�2�F\d�
-9:?�.�|?��S��CK��w�c��w6����q��s��6�aA�h������67�j���T����L�����������0��C��������������-�g�z�sB~�����%��&C�q��L'���rI#�������=/�������ahq��FG�2�*6�?�n�*X�A�\�>f�O�
�,e�#��	��x��_x&U4C�a�iWj2�$��C�z�8�����B�~����7i&������)O��-F������%��xQ����X�BK!	-��d�&+�KhQ[A���F���1^O�S���[����e�=��t3z5��?&�}�vr�i9�����������k�E�a�&��@�v&$�abMp�������R�JO��nE������j��k�Q��	��H�5��f5���L�+2[=�����%����d������-�5C�{~-�1���\��B�AW��b��M��"a��%;f���L���;aIN�92,��p�9C�'�b��w	���Q�������B�������c�xr�U��J���rI��T=�.r������y��	���0��&������S���k���UW�,r�Z]l������u��S'���r����5C�q�Y�����P}�����bhq����?�PZ�E5�K�o���w���w��������'�_7��Rx�������m��`�1;
�aR�~�)+��\
�Z����8~��5���+����
*����&��p����s���K�%�����<���=-*�H-n������H2�����w�0���l|=Z�7��Z��fhq����?�PZ�EehqW0�����E��"�cs��\	/T�7c�I;�]Q�^�0SA����R��6ec�zHY�
:qj�c��������"�-�[
-��{Z��'��T���3f`���j�a8���~��[W�!��m			�����D�����S'T��;�m���?~<"""��I�j�
aj�c�n-��<C�{~-�1�����.*C���Y���S���tT����T�B�1v-���[�>-�B���1n�8���&L�{������tF�}�>����o�!..����'w�u&N�����E?����O
4����0�������C�~-�2���������C�|�����k1�;*�����R��{m�}��&�;{Z��ch1���J�Yh<x0��9	%W�^E������o�a���F���h��)F���5kb���������SO��#�.^�XM�r����C�����W/3�Z}Z��fhq����?�PZ�EehqW0��b�Z<�����Pt����n���^�zx���t�}�Q�m�6]����l�2���[�n��a��>K�.����u�l����Z�.�
rM�68����Fdhq����?�PZ�EehqW������i���������K��"C�d���'Z�l��<UJ���n�+V�@LLLjh�}�,Y������z>&o<���q��i�>���~�zs�c�n����&a��������p��^O����q|���2�yG.����?W���
?���`��$S/3r+�V�iQ9	K��;�-�Fmfy�y�.����_a�i��
�
s�������-L������<�_�-���-cA��\�8�$c�zGU����C����{i�5`.�������'
=W�\E��DcC��/�{���wZ�+h��qc���:
Z�������������9_;L�5���y&T�]
����C/��i��U������g�e����e�V:2�����/��-Z�^��+W��Y3�;�+W��5k��7�������s�N�����[{���4��O>y����]�v��3P��(@
P�6(W��+�7W����q�F=���_~��}���'�`��!(T����R�pa=�^�����o���2,�9��y��:��&������;���opY
P��(@
P�}�����&A$G�x�����0�u��!�e��L���g���2DLzRdR�l�-�����{��2�L�O�F
P��(@
�>>
-�#���(@
P�0J���(Y�K
P��(@
xD���#�,����{�nDDD�����[W�Fo
��U�����I��2����_�Ad��|��y�z<W
�=�a�����������;P�V-\�|Y���O^�I�T��?�{3o\rRR��g,���R&q�<cy���x��q��5�5���F��
0�dW��{E@�x�	�������G�����57
xC@^p;o�<��3�V����M�\����uP���l��&M��B�
���������bA�J��B6��O�;���s��a��Iz~������M�s�=�{3��o]���*��N�:z����0�k�N�(_���o����}�un��CKv���W����`���8��^EN�����#���J�$" =(�.]������W_��V(�����G��l����'���77
) �b�����w�^���O?E�\���������.�1������/�{��8{�,�w��W�{N>���?%����%X�bI]�veh1�!n�rZn����x�BY[���6m�z�)��"7
x[@�4���i��R�^)Y��@�8y�$�W����{�Cq��8Az>�I>|8��_�W�l��������36l�~�C=�J�lo	,_����:j������)����D�s�>}��Z�	��^����������r{��ms52�u���z�Cdd��.&����n�f��Z�S�^xA�-��#�`w��<�@�\+�� (�����)�2s����2�Q�b�+��������|�#�YzZd~���S�pZ�/g���{[�j�{���o�C�-����J2��Us�2N���G����/@v#c`���}����xx�x] ch��72��J�*��R��"��%J���F��v{��92�����z��Ln�����_�l�2����sg�-[V�����a�" =-�������P���w�Io�]|���u�����t����*]�4�(�-��lqqgo	|�����_~�E���/�pd�
_ ��V�y�C�<8~��z��<0J�y����re�67
% N�<�k��=��+����'��|��r�J<���zh��'��b�k���r�[@���95j*W����{Q>�9r��V+N�:���z���[�n\A,�o�����%6d����u��k�ne�x*]�U�VM7n��z�|
�'��|�
,X�W��OK�,	�G�GPB���#�O����{�H�*L���|R-�E>���G��s��K���a��r��
�PFn0J@z�f������)SF�#���7o�'��-C�d%��+�{d�n��CKv�����/@Y�X�m��� �i'�z�2<QP�C���+��{�O�e�Y�G�_ ��l�	�������N�IY�I�>�^?y(�{O�Q���}�����\�����A- ���u�;-���])����=�q���=����vq��Z\b�A�(@
P����Z�%��P��(@
P�.	0�����(@
P��(@o	0�xK���(@
P��\`hq��Q��(@
P��`h��4�C
P��(@
�$����(@
P���%���-i���(@
P�pI���%6D
P��(@
xK���[�<(@
P��(��C�Kl<��(@
P���C���y
P��(@
P�%���x(@
P��(�-�oI�<�(@
P���K-.�� 
P��(@
P�[-���y(@
P��(@�Z\b�A�(@
P����Z�%��P��(@
P�.	0�����(@
P��(@o	0�xK���(@
P��\`hq��Q��(@
P��`h��4�C
P��(@
�$����(@
P���%���-i���(@
P�pI���%6D
P��(@
xK���[�<(@
P��(��C�Kl<��(@
P���C���y
P��(@
P�%���x(@
P��(�-�oI�<�(@
P���K-.�� 
P��(@
P�[-���y(@
P��(@�Z\b�A�(@
P����Z�%��P��(@
P�.	0�����(@
P��(@o	0�xK���(@
P��\`hq��Q��(@
P��`h��4�C
P��(@
�$����(@
P���%���-i���(@
P�pI���%6D
P��(@
xK���[�<(@
P��(���_�����;w6�
���Cxx�K��(@
P��(@��O���%>>k���?�����hX,=zu��A�-�#G�L&|��w��w/��z�K�����9����X�v-BCCQ�X1���������#a�Za���z��W�pa4o�\�+��F
P��(@
���OC��?��C����!!!��c&N��
�U�V�k�s��I�;g��E��
��]�L{d�9�#F��:t��5j�l6k��/b��Y8t�z����y����~��;�N�B���Q�dI�h���(@
P��OC����uP������MzS���DFF��@�<y��%�L�2W�\���H�(Z�h�&���?� "�E����������D�^�th�Mzg�����l�R�%n�(@
P����4��7N��t��
E�I
-2,""B����s�����g��a6l@�J���K�t���o��9sp��w��f'�����:��m�V�F
P��(@
���OC����1s�L=��v��z��c���F��q����K��E����l�2������C�B��dRR��e��c�/_��3n)�H�����@$s]~�a����G���(@
P���o��I`��iV�X�W
��� Z�n��U���E�vr��&M����3�:u*��/�'���y�f�#sUd���B��/�&aI��90
�t�?�
P��(@
P�7>�i� "+����R�J����n����H�K�r���i��R�`A�5
����O?�{F~��w�q�:�dZ8�:_zW���-��={p����z��.��yV
P��(@
\'���"a�������M����2�K���S���_i{Z�/�{hd���5k���k�.=�^��dZ2N�w�����.]������P��(@
P�O|Zd���>��+W�#�<���"�K����u���;C��i��	-��������z1��U��.���"��=���C���uo7
P��(@
P��>-������Z�j:�8��"�e��%X�`�MC��I��L�/S���t�?~-�Jh���R������[���(@
P��(�{��������y,��wO�����2L��������~S�l����d���7�e��M������"�iY�~���/�����n�7k@
P��(@���Yhr�)���_��a�����v�Z��!�e��	&�����^^B)_��"C��nb�]+�l�L����+V��KZ�x�^�L����3y�����D|��/����K'�����S��(@
P�|Z��C^.�{R��E�
�Sb2�����;u�pn\d��g��u���j��t��r�B�
����'�g���"K9�������O��&�(@
P���o��B
P��(@
P�Y	���%�����(@
P��ZxP��(@
P�~-�������Q��(@
P�-�(@
P��(@�`h���a�(@
P��(@���(@
P���_0��u��r�(@
P��C�
P��(@
P��Z��yX9
P��(@
P����(@
P��(��-~�<�(@
P��(���{���@B�I���d"B�Y�&6��+��&d����W�EV8l�n��Y�5"����W`w8o���a��Y��#2$�_n��M��%�Z��K�:f�o��8q�q��3�F	Z:)� IDAT3��EX�?�%�\�+�����n*�D��a���~�q;|�0��[�2e��r��>n���^��������u4���d��*�����f���8t�������s{��>��[�x[�
�f	E��
��T�[���c��q�F(P�j���8�����#))I��3g�[.�;R���C���y
P@,����Y	8s�� ���1�C8
�6�w����h��5�-[��� ����_��u#���^O����2lg����	�C��/��eY����x
�U���7�BH���s����j���c����Ox��guP9q�f���z���j���*���?_??n���D_]���[bP�go�4r�O�8G�������qr���������c��T�R�\>w�(�-�oI�<��X����f'�l��j1Z�G����y��_���2���:xX�V$&&�~=,,L=!!v{��������p��I���<�������[m��_����r��{"�w�LC�������}���+��f�A�-�q�[�O�.��P���u�'����<��z|�E��������O�x1���H8�BK= �p�%F���*����~rR7��y����DFF���{r�I]��W�^���/�_�O���+���u��������G6��s?q�?��7l��o�����*
*�z�8
����|9�/u��I��q�r���~��G�Mr_��x��s��'r�R��\��u����'���>�v�OZ_��-[�`��)���;J�(����p5�gy!���@6Z�	��)@�<Z���=�%::����=����<�m��]����:���9R�+_����<��~�
���^{�5�q�h��M��<Z���9s��O�w����z�)L�4	k���K/��?-<x0^�u.\��������/_�����%KP�fM����={6���{]���&V�:u���z*�\Qs�>��S�;wN�]�X�bE}]2�Iz9���p���m�U�T��o������y��>i�3g����K�.�� e?���z������^�z�rN�:���;c��m�A^�&��Z������f���B�
�IBL��=��eK�=qs�Sb'�R����k����kW}W6O����/��/�D�\�t��=z`�����E����������������w��k�@s��A�X�N�lyW���P��J��%+!~�����B��5k�������+yH�����A4m�TO��!M*�~�i��?m�4<����X~����({*��?^���?�=(��������5N�:=��%}�������6y�^�t)��#��i�|�.�f��C�>��3��[;v������������l�
-���-��������~�������]�v�c��1�}%�I��x�
���~�Z�J����K����m&���.�)�]�t�AV���^|$����)�O�3p�@m"��������+��������_���'�x,�C�F����_�qvHS��Dh9p�����/Z�����|gO����$��^�Z�#b ������n�5j�{N���O>�������Z|�<?�L��E>��W>=�O���L�ex�<����|.�����N��by���d�4�I�&z�����S��~���[�����{�����
-�P�����	\�������1h� ��/����<�Kh�^��
��y��P'���W��������_��	
��Cv�n�t�E��}u��<8K����>�s�h�"��$C���\�m���:����?�{��%��=!�R���+	y2g#G������m���F�E����d����%�,_�\��I�P%=2�\�<Z�g@��G2LB�������gD~��C�-aF�
�]��DB��LI�T��eu`�,AF�7
P��`h�u��2O��\,�2�C>
�Gy(��B�� ��������+5j�2eHP����#�W>���'�-2��9W$�s=<��"��py��`!=E���������B%��c��i�aN��/�E&ZKh�m���?������kj%C���_~�%u������
-��/�D��-���3���X2\KB�]w��C�8H��P#AT���/��?���%��� Hzkd�����1	>ZdX���C��Q�t��{��=XrqHZ$�I��=��
,��}�9���Ko���I}d����Cu���eh��i_�	!�s!Rw���~�mm �.C�$��r���c���?W�*U�=K�g�E�������Z|�<?�L��Et��Q&M��'$��eh�y���/2t�E���\V��aT�@+�"�+	Z�AN�$d�SeO�	a�0<w�\=<G�����|r/�����+=�C�I��!^�����0��|�.Cy$�H/��%�MPex�|Mz��!]��"�C�<Zd��w�0 ����X���K�+���<TK��1	i2�OzT�}%��\&����@&�D�.���y��6�_�� �CB�<�Kh��%4��<q�^y��^ ����j����K��>��7n�8%��R�>�+V��AL�'���\�'zZd��
	p�%���$=T�6m�=q2�N�*=*2lR�3���/�2��y���AB�\kv����x����Z���P���-R�L���Vy��P"��'��i�<�I8�O��aSJe(����v�O&�&�I���5�3����e��=z��[�p!����2$�����O��]�pA��Ez���u��!�r�'��Io�$C�d��B%��i|v7O�9�\���)RD/ AC�@i\\�n��%K�k���/��$����\��VF'��0"�O�W)��`�K6Yx�X�b�7M����y�G����u�.W�.�����|(�#se��Az�db�X��s2�J��Q��.1��I��y"���#�*�K�Y�J���&�$C�$�����I�#q���d_�Y��?��"����������Q-F��\
P S��;����D����%��'Y�����|�����Mty�h\��CE��������=6�\��O�#�2d(�� I/���^�������t�5-���o~xX,����9O��'�I$H�������\�=f���0Q-yls�nXE������B�Z���e�^�`h1��S��	��`��}6\����
-���P�����o����j��k�.!�fXJ�����X�B �Ez��GC>A��M2����a���T�"��7/"4Ly��^�������zr��Qz;�S���'r�:�����{���X�����l��-������~��^2�D
P [-�����(@
P����Z�-��Q��(@
P��`h�w�(@
P���-���mq���(@
P���CK���3(@
P��(�m�o��|�(@
P��@�Z����)@
P��(@o0�x[���(@
P���%���-.�L
P��(@
x[�����<(@
P��(�-���}��a�����fC���Q�hQ�L�t`�Z1o�<��v�o��UQ�@������v�Z����T�R(_�<,K��3(@
P��(��>-|�bccQ�J�\�W�\��/��\�r�����1g�t��������g��		��O?�������Y�}��E����[���(@
P���%���2�|�y�����������������F���S/@z`���>���+h����o���x��gp��	���k�����/�����nt��)[���(@
P��o����,��f�����{���K���c���EL��[�l�g�}�^�za���X�n&N���4h"##1p�@�g�(@
P��(@�l	�4�8�;w#G�D�Z���M�tsR�����m���_9s�D\\��G}�-���������p3��"���I��|�(@
P��(���L�����>
-g����3��&�$�D�3g��y��z�%J`��
>|8�q=�Ezg�M�	?o���Z��K/]�
�N�:�u�p
P��(@
P���G����F�g�%>>^�����:`����[����s�N=��`������?\dH��Y��/����%���.��e��x�����eK��eE(@
P��(@�|Z�
����
�*U�=,�w�����������N�:���������_����D��3��������zE1)CV��7��*,��(@
P�����%�%�M���{Z$���0	"�����,�g�M��������a�[s��=�Lzh����{���H�"�(@
P���K>-.��Q��(@
P�A'��tM��(@
P��@`	0�V{���(@
P�:���kr^0(@
P��K��%�����(@
P��@�	0�]���)@
P��(X-��^�-(@
P���N��%���L
P��(@��`h	��bm)@
P��(t-A���`
P��(@
�CK`�kK
P��(@��`h	�&�S��(@
P �Z��X[
P��(@
�CK�59/��(@
P��%��X���R��(@
P �Z���y��(@
P�,���j/���(@
P�A'��tM��(@
P��@`	0�V{���(@
P�:���kr^0(@
P��K��%�����(@
P��@�	0�]���)@
P��(X-��^�-(@
P���N��%���L
P��(@��`h	��bm)@
P��(t-A���`
P��(@
�CK`�kK
P��(@��`h	�&�S��(@
P �Z��X[
P��(@
�CK�59/��(@
P��%��X���R��(@
P �Z���y��(@
P�,���j/���(@
P�A'��tM��(@
P��@`	0�V{���(@
P�:���kr^0(@
P��K��%�����(@
P��@�	0�]���)@
P��(X-��^�-(@
P���N��%���L
P��(@��`h	��bm
8#7���"��������SU���J�{�lH
P��nW����ey].	l8��F�-���o����90���[���P��(@
(��H#��������&��b�9x
P��(p�0��.-����@f��G��M0�?_���+i�v}u��0�x��X(@
P�A"��$
���5���%9��,�R$�d7�������rk���(@
P���~�r��r�u������C��M����fZ�!C���/�H���/���_f��g����c�f���[�n���(���3�8I�9���F�"wb��X|duj��OLhN�U��/TvuO�I8�K>F�9�W���*�5'�������U}��W[��Q��(�>-III8p �^��R�J�4�H����H�>��G���1w�\�L&4�>�,j�����k��@���pJFA��HZ�����B9
�}&���C���oE�D���]���~�=�������{vLgh�P;�
P���K�g��n����1bBCCoZ6l�C�S�|y���b��	��e��9�^���\�2���\-����@��b��Lq�D��s����i)S��;���'^���{u���*�,Pg��8�����e���e�Z<�Z,��(@�`�Yh`	-���ih����`�X����k��}{������};~���V2�Lz\�}����������Z� gH����Q��xC$9l�	���9c���h[���7�������E�����7���(@��>�\�tI���x�b�7�?�<���Ol��Y�����;� ,,o���u(g�����'��b����K{���o�]��������b���`�U�J�E�u������{/��\B���pK(�G���F��k�������B
P��V�@���7�����C��
P�F

�b�
�=}����#G0t�P,Y�D���d���>z�+�_�(�����[�o�7-�-O�����@d^,8�	�D��aL�;�]�S����m5Q���*�,���D�e]~��*�>(@
P����t�/�|Z<�Q�F!$$���GTT�V+�L���e��~������
��9s�����+�
0�s�F�V��01�3f�=�E��p������E��t/�����#�����<��V�B��\�}G'��?���zT
��e�� �D
kQ�>�Z�/�o���%�]mG
P��@P�4��$|��"��c��h���~��%��W��{��G��}��J�����Z�j��6n��I��$�
���e�7
�*�Yh���(�&�_P��^9	���pm���6D�G�A�|�{[V����EX�Q]}M��3;S��r���\����q�(@
��OCK0����[ ���<��~��J�kq%��Z��>`�(@
P��/��j�����#���b�*��(@
P�v`h�][����C�Kl<��(@
*��b(/4��@k1���(@�``h	�V�5��C�-SqG
P��(�5��Q�D� ����:R��(l-������
0���(@
P��'���m��P�����<5(@
P���Co
�`h��@
P���O������5��C��yj
P��(����Z��%k#�A
P���-��o��|~-�������Q��(�-A�����ZxgP��(@�`h��6a�|(���C|���(@
�@�����0��v�(@
P��'���m��P�����<5(@
P�`O�
d-������(@
P��`O���y>�`h���a�(@
P�R�� mx^v�-�3(@
P����	0��_��F>`h�!>OM
P��n ���[�iZx;P��(@�`h��6a�|(���C|���(@
�����`h���{P��(@o�����<�_0��u��r�(@
�CK�6</;s���(@
P��Z��MX#
0�����(@
P�7`h��A�4-�(@
P����	0��_��F>`h�!>OM
P�����{�Y0�dm�=(@
P�������mq���Z��yX9
P���T��%H����C�
P��(�-��&��Z|��SS��(@�0����@���(@
P��Z��MX#
0�����(@
P��i�=@��Z�6��(@
P���i��8���-~�<�(@
P�A*���
���\���w(@
P��?��k���->���)@
P���
ZxkP �Co
P��(�-��&��Z|��SS��(@����@�-Yq
P��(�m��x[���k��nV��(@� `h	���eg.���;��(@
��C���	k�C�����(@
P�-�5(�F����(@
P��?���������d2�H�"7�9s�����
�h���X,z_����;w���,Y9s��?a�(�Z��XY
P���D�g���p`��)�={67n��{,S�={���/�D�6m�o�>�/_�[���l���~���DDEE����sg���I��2�`h1B�eR��(@�|Z$lL�<6l@ll,^}��L���^@�
��O���t�R<����^�:`��9����z}��A�
���A-�������(@
P�O|Z����>|8BCC3
-/^D��
1}���z: IDATt�*U
�����#����c�������1c�M��k��x�������
0��*��(@
P���q~Z8����Y�F�W������G}�/����1v�X������n���f���� ��Q +���������j7�����8��`�(@
P�(�T�_���uh9v�Z�h�J����e�|��g���V�X���O?��-
���H8�:���������b�z����������pr�O?O@
P��\��'�_����"���DV��������>@�F��|�	&�o��8r��*��-����0	8��ww�mx��a�	(@
P����	�4��j`�G��]O���
����j`�ZX���q��wc���z���!C����W��_�~Z�g��z~K�\�������R�\9�Sf�F��%`���(@
P �|Zd������^�p��ys���_��%J�f��HHH�C�6m��'���`y����=zT�&��u�]z9d�;\��
y�`h� &��(@
P��ih��5�����3?�w�awx�%:R��vo8U�|�~R"C��4��N���#����f��|a	G�9����(@
P��&���o-�F}~^���-R+��\�k����(7j���2��w�k�6���O�}���w�����-gh�P�':�m���Y(@
P�#-a��B�.N����/���@�w[|��S�-������~��M?��0��B9�cz��A��K�(@�@`h	�����-�7&{Z�7d	��y�l������(�-�;��A�"�p
P�pY���e:�;0chQ�G_����zK�=�����,�y,{Z�o{���oL%�
-7fB��+���X��`��x��O��%����5NZ$�D����,iF�h3V������p�6�T+nF��fl>dG�{j��j>n�r=?f���!)-��YZ<g%e-Q!��[
�"rc���8r����CK�UP>wI�?�
q��^��6��q��H�[���5�?�-�p�)@
�CK@7_��;C������BP&��}'���C~��������M��!��B��=v4,o��MV,���p^m����X���u�Z<}�0�xZ��.�Z�%����:�HTA�t�
V��e��L����`����@e�<�	�U��{�a���(��y��3�vf�[�U'63��������m#��r�4%�6�H(��{8F�K�������v�������^��:����I��zYj��6UC���D�i�3���z^��O;�W$S�J���M����a0��6�H/�{���������f�>:��;�L�+��.��%�w���{���:���j�j��������.��?�}�����;�C�8�%�n*^+(@�`h	��Yu3��������{Wz5	��x������^�2�C8�m�b��rN���Z��o$`�}�v��KW����-��b����c2D��������}�`*���0�T���U���3�M���*w���+�i�:���V���,~��7V���6%�nlU�
d����Mj��K�W�?2�>�kNnah	����J
P @Z��2�vjhI�����xsR<�_m�����U�S���c�*X��U/J��29���&�������8�@��!jN�	�$���ch��
����`)%mO���m4o.��/���]q���wLO�hV�.��l�+IW���I/�<y�L�R�q������Ts�F��w����-�i	����I
P 0Z��2�u��2�G8�3#QM�w����8x���T:I��ToKHB�+R��XT8I������	8�F&��U/�LP��"�8���~h�~R��x��,9a.�&����K{T��yH mh��C�z	�Z=
'���s�U!��������a(�#�,�������O�5C��������.�i�:�{����["C�����P���1-��������^l��y�P�I�|V"N�s���h/�(�I)���B�0L]e���6�W�����~]�����-��M�������a���a��`�h�kX!�49��b�0KvC mh��_/�|L������f|��e�g�78�V+���N�YU�+S�V{�O�j5��:}q��q���O=�����2i�_z����@<��`h1��{'���q��&�h��Q&����?�[QT�{k��{�s"���W��@Nf�OR�%y�J����M�P8�,�lS�%	T���B��i�o�j8dH�$���yW2.y\1wi���1a��h����s�e[c���q��w�=�pK~�=W��Q�6oxn��� ������q*���C����+��c(@
P��-��6�\�B������,����&�[_���~]h��bR���/J}?�kw^����o��������;���?��
l*${�&���9#Lj��"���V�@��V^.y����>CK��	�'(@��`h���>mh����-�gz}X������^�.����7�CC����?-M����p9��*��z6C�:�m����nh������~����r�����������(p;0����r
C�Q����r��z�;��VTkgF�\���Y5�3X����K
u{��	a!&|��O&���H�R����c��	�=-F�/~��5����5�d���#�*����
-FU���(Y�K
P��`h�����q���������������N���]f��k�9et�O�X������`�;~_��7,�=5@R��i78�|��?oA�����[���W��0cC�,��AW�w����=K
P�F0�-����%��x��I�YP�80j�WT���a2:H��o�t���my��	������c�D�<����s�K�1�iuI:�i�sZ��Y��%u�b���zx�}��O�9u%e�|�����}�Z��qmq�
��Y��u�����0f�$���0�CyY8(@
�)���&�?~���d�Z�l�E��h?����	^�nFx������`��
��i��[���=jT���"����;&��b���-��iC�H�V�����3&Kk���L��3B�c��f�6�zg������=��P�����a��������{z�
s�Oa.������,��N��%������CK��a��lX��E��q*�>��+��C�|?y�k�;��N�u`�~��'L�QK?��e�����;�����
C��?iCKL$pO���6�js Z����zPB��t�3U��u����l��������9J��	WKp;����*�$��������?L�]���EUCH��-F��\
P����i��+,��a?�|�����/v='�O3B���V��8�;�Q��im�%�p��#;�=kF�b&�Y��5f�;����k��rxX���+{��up��&�W�e�r+����?4��LD�QJR:+>S_��"	k����A�
/v��!���wW��E��:a��jE2�����q�+WHn����i�������'(@
��-�Q�EhQ�_�f�L�o^������f9�=��76���oK�hS��{YN��E�_��F&<��
���������������j1e_%��O�K ��)���X��fJhi[��&C���
��T{n�b�!;��W/=]���B�M������m�9-��dY�(�i�O�����?��C�������jXP5�fY�H#��c����'���s`�z	x�|�ML��%������M����e��i1��M�zX����e��D��I��t�p,�e��-���
�W���w�b�a��1���I�z���zG��Y	j����1EB����9-�t=-������7mNN���B�@��cnJ�J
P�'��pMv�
Gh��,&=��"7z�f��,NQ�c~��i��zZZW
��qI�G�%37Z�dG���`��s����U����aA�&=���Z����L���d�_W[�Z
!���'sZRC�C];p������-T/`�2)�?�����?W8��P��	-k1jQ�c�����������U��E%��(��a���,��L��%��f�
��bP�1��6���g���Cu��L��1�LN��$,ji��j��p��2F
�U��V
Q�'8�]�y)�V��Ba3��6�������/�	|Z�����x;r�e����r`�f��al�ao��h��}tllVs���R��V�3���l�_���<&,��z����G�1�sS�T
P�8���k�W�����dhq��FG�
-�����%�������2m�2l,\
�[�������QB�4!y,���V�;�H�}TY�,&�g��#�b|?<�����KOG���S��
����Vf��H�\�����p;��[�z�B�)���j�����9Z�[��^�2F�w(D
]|�~��.��a���,��N��%������&ch1F6��%C�C��2�������"KK��!c���6!B�'���
&2��rI��$s�.��,2,���E��9���6|��y��~oGU����Z�N������	�P���V�S��RW���������E_�w�q�"���pC�1�$K�(@��`h	�6�a����^c2���w���N��}7�gt����RG����R�Q_���0����W*��.����T��\��>�%�V�:p�]�a�6SC���p8�������*�|�J-�S���ghQErN�1w%K�(@�@`h	��I}Z�kL���n%�s������>fW/=����tS���F&F�'����
���KQ�����R������A��1�3�jx�h�#x��SZ��b�-�r)@
P �Z��nV]�����=�`-��D���(/5U/�,��?5�}���� P��)yhX,����V����gU8y�CmkB�"�������V�R��Z��)Y*(@
�CK�5��+���^c2�����E��QC������,Rsp�\�vuMh}�	�9���z������X5��9��
/���P"������3��M��bZd���j�D|cnJ�J
P�'��pM��bT�1�#�vN�1g���0uUY�;�y�'����k��N�{�$C�Q���(@��`h	�6�a����^c2����=-��n*�'���������R��/�_W�aO�17%K�����1z�d��z�#�e,�h�X<Q���,dH�,�,���j��������dhq�/�C�1v-��X����K�:����&h���B�@�Vx��S��")@�@`h	��I}Z�kL���Z�cO��,�SO�{kOn�Tq����h]|��uC���)@��`h	�v��Z2���
wbhq����?�PY����G���k��R��t�k���s9���S��r(�-����W���^c2�����b�C��,�S���<E�9Q+}�i�Kf�E��nZ<�B,���C�m��-�5&C�{~-�1���"=%p-�8b��F�JhX�,�����Sq���D���D�b����,<�
5�UD�"�S�n	��������������b9|���o��+`hq�1Z��ch1����T�)����tt1t*�kNlA�5�k��S��|�%��"��HT,^��$~�=���������D��]��aQ�T�5����u����Z<�Z,��/���m����6dh�d�b��=-��q�c�\U����0��x\���I,��w�m��1����6�4(X�
V������I��u�A�q�,;�.u���w�����qA}��C��~�v�X�UqglL�=���1����
/��
0��*�������Fahq��=-������"�~~g�V<�B�~m�p_�P�N�I��e��>5�$���Zr�%`�D��D|��Z*c��I��%`p������$���zL��s�r�N�@�<�1u���P�@�%��������s�"f��bL�T
�CK��\&�fhq�1Z��ch1������"[}��
��m�.��Z��F���-�v����,s����15�H���rg��2���z���������c��?����{U@i���(���$�Q:Wqt)�V��O�tT��bL�T
�CK��C��[������@s����
���6��u�2,~e2�?>�>��e�`V��|Z�������a*�$,���-�b��A����W�O��Q-�^��'6���o�1��������K�D����BJ����J5G�����l[��b��b`S�h
�CK�5���������=�����+C�x�Z)�#4P��W�r��$[��B��%gP6��8�~9_�;JM?
5����=M��IhYQ���07mR�,��MZ��,�G*��V;�
!���~�G��� +��JbJ�@��u0��=�e��U��j��F������
���9C�^ZmJN��`h	���yeZ�kL���Z�ch15�H�iq���Lx��P:�@ln.^u`��$H���E������MC��}�Zo���v�X���k��Z�p�
SV&!>�T��������I_��!i�3�JJ�Kr���Z��ttQ5���Z����G��a^�P1wiZdK�9ub���`��[M�� #�_��*�*y�a������}oC�a?,�'������������������H�'���������Z�HHH��


��2p-�F�Z��S�2�����b�C��B��c�:!:�L[c�Y-��F�p����Cg��M�?=�����f�&+��&����Q��e��\&DG����$�Ss���
-�'H�;Q����"�j�����i�����fA��05�^]�\�'�$G��"������E��F/����:]K-����(l>-D����?v���WG���u I�
2���S��$/��#�����������}�v}L�����G���;��0�zZ�kz���Z�ch1�����Bq���6�tyV-U<{���01Z��/{E��Y�~���N^��s)���Z����ZN���a���*��KiR��:��5&_��w9p��vT,���t�70���v�p T��GZ�qw}$���[zZk$U0{Z��e�,���9s�`���0a�=���z�F�r��	Jh����N�"�������A�$�������`BAr�����.�"*�"f�L�I� H��a3�;�:�38��0s�:��~�cd��{�W����S���a��&3[�l�}��������G���z����/�Fi�6��k�(-�QZ@�!-Y_��j����p�(�K-a���_s79�a�_�����x��t�T���	W����?��/�*-��x0k��:��jU�y����Q�*o����n�qo��~1(��e=7�����c�N`�,7�~��Je)-�&$;&�����e��(R��Y"��kW���m������/_^K�����7����+c��)������?����G�J���_�<�R=��bmd)-��QZ�����wi�����U��z��,�S��|���-��E}��{��*
GU4BEb$23�7��J������|6����<���h����]x���emx��;����o��4�iC�r6t����<�t�3F;p�����|O����FZ�MKvL$p~��:�i��2d���n���(��K�&��b��iiY�`8����c�����c�N��7n�^f&_���bm�)-��QZ�����CZ�zo��wUQU���4��mZJ^�!]G]S�+��h��v��b�K��oT����^�	Li��qc�!��gWy)6�|�������FeaY�����E���E%4@S�����n<����j��/z0c�u*QZ�MHvL$�'�I�����k��#G��7)�*e�%^9�DW��YV�\���'�O�>:�e������O���G�Fdd$�z���=���Gq���<a���Y��,a�1���O���y�����4��57��[S���}��_k����F�\��Wb�	����V��i<���!��Z���d�����_�m�fb��E���"�����c|u��f�!������������a7��I�Se`�Y&�`�����|m IDAT����[�L�8���c_��DrV�����}�x4��K|��&<�H�6uE%�<���CU�%Jx�%�����*x#�.R�Y�?���6�y�!v��ec����cq�Fy�y'���@�X��w���1�t���@�+�3z
vN$�;�r���t����o���[o����y-IIIx���u�DD&55�R)l�������u��e������}����ZP��dy�D_Z�l�N�:\_�#-��3�b�����>-�2�b��O��\�L��U�����"W���g
K~�\2{���8w��W{��]v������!v�+i�]=K�������P�����'��6�^�{u�T�f�����l(��>3���d��@@Jm�Y9��*Dh��(Z	O6��:%j��]�E�o�r��1<=z��DB6o���"**
|�j���f��i������
&�r��Ii����K���"8.�O<��e����(-����b���?J���.��E~>k��\���WKV��3GhA9WvrT��k�c%-�	���G�.NV;��������j�Ec����lX��h������mmhX�����o��R<���`�T���4������"�����o�X������D��������;>?�I������!U�d�����{Y&��"
4�����k�.�������P���������/����Q�j��{�\���bm�)-��QZ�����wi1u�J����_�i`�Z"&�U��M�G�?^�k�SI�jY�N%(GNW��?6�E p,��=G��*��Xlv��bj����!��/��-aw�tv�^�Uj��7\�E�����s�J�?�R�6����RZ�����Gi1�r�����d^-�h��HR�uo����`��|����I���`��~�luW��]��E���Rm9�%�
O��������Q�7=���o�PZ�����Li����b���?J������Gi1����v��#�h%cmrgn���)�S���]I�<Y���B��"7�^al��--^���(�z%����K��tw�l��7i)U
�c���;��LA��4*SW�����"��v�<JK����Hi�XRAp��� QZ�����Gi1��b*��*�����ge�
���m����>�~���)��]�[��pT�CZ<(�D�w��q2-�a1(QC�����D�D�g���Y�
��z����-���;Q*�8��o���g!%K\(-��2��)-y
��SZ�
��?J�~�P)-V�RZ��[Zr+��2]v��<S4��Kd��iY����jWV��V�?5^?KZn-�u�W��m�#1#	�m�,��n*�&�RwbS����\_�������c��oP9�JtT��C�����G����_`��CZv(-2��)-�c_�W��XCJi����b���TJ�U���/,-�"�JR����P|�Gm��=/[@���X4�F��IY�W��:�Q�AUAN�
i�m�[n96����s��YY*�H�,
k]�V����m��tW���?f�]�e����z����A�:]���NT�)�/v�������Z<��u��*�+����ClO�Ki13�/�WJ�E�
�)-����b���?J����P)-V	�_Z�E�t����!8�JU�D�
p*�e���
d����c��t�!T���`�2����rv4��@�J��nm&��EZ�"��[����+�5�aC�^���D�����h'U��_eCD�������@�
���*��mc�EZ���W����I�;c����+���}�1�Aw%,����~���&--I�v]OK���R���xn����}�>i13�/�WJ��P
�c|)-�����
2��
U���	EF#�k7���w��po0�i(����Qz����RZ,�������o�cc��O�!�2'��w_���������h^��w�g��*a��L�nr��`w���D�:���~������?��KEh�iQ����4�+������W�1��]��C\��$�z��u��z�QFml�n�o�F��6<z�W�Ug����jy���f"���o�/���5����\W���d$���0{(JG�����1~�G(Q�"��HK	�rECL��$a������n)-K*�����/[����O��j ��K�P�*�O�Ni	��w�[��X@J�x�S�i1C6���m�u{�X���+���7�����#Y����U�~g���H�G���y�����tg8�&���u6�oI�T+��X��rZ�����l���]vD�{��KJ��Q�J�;o���������S���z%���v�+a��	n�P�6�oC��(U"5���i��d-�����~�L��n:
���Q6���d��Z�V�������s.�8�]���Y�O�����L������+���Q������_�G����Rj�*J�q�f/@i����b��<J��ZZ�B-�Ui�e��X����q6t�1s6:��Ae'���L���@d�
	�T-c��JZn�e��������(m������i���D[�'-�D�w�s#%�����#"������P+no ����G���:��8�����d�K��?�R���C����poS�Y���S�����Cpw�*��%Bl���t,?�_�'����p��_��_�h4��z��5S����A
�a���RUK�8���f�}�z���W`Li�6>\f������X�Ji���b
�����2�M~�����n�U�-"-?�wb�Z�%�RI��	���f�ji�TJE"�/�D%���ny������������i8v��������[�������"D6����o��UCo(��V�O�����q�����%-6I���\�:����)n|���U�)wi������T��"����F��K��������Z~�_�%���/�>���f��HK�O���=J�E@:�!��(-��QZ����<���!:��UN\]��{�`��L����X_�*��$2����RB���\��a����!�<?SGV�u_��+L�	����x�e�������n;"U������~��)A��a�]�s�[kAR�;T�-��S�����0�5�&mG��Z�����i�*K�IK�
)��of�_L�����$��OZ��
�:*U��h18w�������t����e�MH�k�>zH�9����Q��J��g��z�������6)-y3:���(-��QZ��=#-��S")�"�	��
�%�~��N�s}(nvb�	�����z{-�)���aqjU���(��3��=���l��$�m�������2o�`h���^rZ&
���6�����mX��GW�����"�����v�?���U���M7������i13B�^)-&�^�oJ������"-Y"v[K��z;\��Q�,NO�I��W���W�F���H��]d,����:"�v]���8�TC��/���w����1�R����RZ, ��X�Gi1��b�lvE;y��T
��%:�TK����eU��-	)������I����L*��s�C}Q�"M�����b!-R�X��;�x�l��j���ksUd@�y�K�Z^'�����h��+���aW(9�d��f�u��Ml�q�*���������+�%0��@w�Oi�{�M���}87���~�T)�5�X<�li	G�7&�u�(2�.@��%�:Z	��>r���BH��HV���
Ki)�T��(-���b��<J��9�pg�����g�[T�����=�j9Wj�/�"���D�3%?�M�{���x�:!��3s�#�Q�\��I����c�W�������)-��������9��.����M�r�_/FU������P��+���MET6"��G�2�m��Ch����oU2��S/V�� ����UJ��1��X�Gi1��b��/����hK�;?>�����&g��99��3s2�z����X��J��_ ap/�$�":=��������oTT=u����9�?���n����e3���$Oy�JZ^��6��JK�7�Mi�0!�TJ�����X�Gi1��b���!-f�QZ�p
�^)-�6"����2�c$���yF����IMA�7����)��O��������}����-Bx��H������#��&��N���7�Bi�0�TJ�����X�Gi1��b�,���\)-gLgRZ�i���WJK��/ }�|d�^���#�6�G�����e�NL@�J��G��j!wu�k�n���AE]�����s�Dvz
E����"4��\
���ba)-�QZL����!Ki)8WJK������`��G�k��lJL26�E���11��?I/=O�*k��P��@�<,}����!�TdF�s��)�?<��1�����XCJ�x�S�(-f�RZ
���Rpv�t&�%�F+�E�)��I^,��P��y��%/�A�{J�����X����q����kW��r"���Khm9��`���)������
p�(��y�2V-UKm#v���/q�y��������q��)-f�RZ
���Rpv�t&�%�F+P�E�K;�����|5��y�g�n�����XCJ�x�)-��������=�d��k���|���FVK��%��T�����!�FMx22T��0�����F��U�\���Fm���QZ�M�SZ
���Rpv�t&�%�F+���GJ�!�>���b6�������s�Ad��`���"�r�B�����Z�l���J_4���HK��?���$���(�9��h��y��"�c���SZ
���Rpv�t&�%�F��bt�BZdb��M�=���H��v���5��#Ai�/0�%u�:jyw'���p���q{����9�TX��K���/���e�7b��+���"����(U��bl���cJK�ySZ
�.�����hQZ����^J����SZR��\�C�^IK�H�����paiY4+�"v�x\N$S���88T�������M�SZ
���Rpv�t&�%�F��bt�(-f�RZ,p��X������L����(���H$��4"�����u/(-� ��(2�U�U5���CQ��W`���0c���SZ
��Rpv�t&�%�F��bt�(-f�RZ,p��X�����q=�������T�%��<|�3/��y�j`�a+��v��0Oz:������f����fb�
���H��i����%-��H��cU�n���9�������n?���[����QQ��[��������"y��HS�Y*�I+�����D��sr��~�T�#6J�A��5�%�����esI�7���L�7��SZ,@��X���"����E���:�>�cWUI�R��Zm�[�u��]�G��rXBU�K�*{,-}��q�T�l�!5����R���0cS�x��������s�D?���qz�;��3��eS�����?
�W"��q�V��B�WD��:�~��r�r�N60'�����\.@i��F��bm0i���|gSZ,p��X���b���:���&l�I�T�����"��[Y�Io����:"4k��;-	C���ka>���j���~�-Hz�%������Z�C�V� J��9s��Li��F��bm0)-�������i*g�u�����E���!���k�x�M0r�3�RZ�����'Z����C�=CnSZ|��P/��99�]��;{N�~w�����w���Lw��o ��*�r�~>�O(AU�+>q�")I�_V�r
"��F���a��$%!s�V���v�s�,2�R���r���r	�>���`RZ��iI���H�L���$�y�������Ki1C�g���"���p�vec��d�����_��\�e�;:R��^iy�u�+�	S�jiQ��B�w>Ry,!Hz�%����z-lOj��m����C1=�"T	M��HKL� �EJK����&��b(��J�5~� -�^��Y?�y��^j�C�ISZ�R6��/> RZ��#��:��=�&--�%`Q*��q����D���+��9���^�1F����uQ�T9-������q aho�z!��RZ
w��[J�%4(-���b�_�I�J����L�������<�:��bf���WJK�����G�)x|�X����]��������Cr���=H�t*7�u���]d�nHS��^����F�W3���M���S����*v�U��H>�l�*����4�
y���%c�C/����Y���kCDi��/ �E�8n/QJ/ip>�7��~?U�G�E�e/U��h�n�$<	'����m����M��t;
�%�QZ�L?�Ji)8tJK��]�L_����E����h;w��Kh�����KE1G��z/ )����
��4�T���X�R�NIQ��U��FH������s����/�c���Kh�)-���b�_@I������7!�j
}k��I����������H���%����S�P��JU�7�RT���t��bf���W_|@��0�C�H�u�9{8���U����b����7��oJ�����X�h�"e8#�tC��ep�A�����*�~����oeS?I���GX�V�������P���8��M%�"���f��6d����G����Sg����.t�/�$�������co�g�#��o��|#�(-����b�_�I�DJ"�{����T"bU)�t�Y_����[UK��*���WI�Z���=la�pn��7NKT�r���E=�8��6�h�4����'���i�>���Xg��J�u���������<J��A��X�p�R��T2i�^��3�idn��t������vv��W�~�	[d�����#�7i*���q���V�_t�>���<�u����g����.t�/�$����QZ�3,h������(-���b�_�IKe%-]������QbF�E���H���Y�jW%<#�q��C����]�=� qh]�3�{/����p.#-f��_z��DFZ�-#-�2�R�)-��3?�QZ�C+����X J�5~�&-��K"-�_~���(�*O��7������#����Tb�
���G�<%3���Ti������O������������#������:zD�!OzZ�?�T�+sB�5�=6�����*��`c��\_�IFZ����:���@i)(�<��bmP(-�������~��)w��f�-��o���u���T�/]�6P��F7"C%�{�.U�2V.Ed�u�O8���f��������bf���W_|@���$�i�,Q���o6��u�U�UD�������}1')-����b�aA{���\�Gi�6(�k�JZ�fd�"E�P��HB���!U%�l����+��r��42���$�z���~��/y.������X���PZ��������r�S�@����Dvz1����b��~}1')-���b�aA{���\�Gi�6(�k�OZ����9wz���9�q��3qsI3������ii�\��yOW�<1��b���}1')-���b�aA{���\�Gi�6(�k�NZ�<�����n}���b}T��o�a�(-�yRZ�3,h������(-���b�������e;���&o�����2&4
�����5�sJK�G���gw�3}1')-����b�aA{���\�Gi�6(�k�(-�QZ@�v�����X>FZ�3��C����G��yf�C�2�
���F�cb�\XR5I,�9�7[��p�x�2�v���(�v�Wi�?>V�\���p4o�u����n?��3220q�D�\.D�j@�Z�B����1�����[}~�F�p�M7!44�4�|eJ����X�Gi1���b*��*TFZ���|_�t��$>3���
���TE��W�����RZ
�e�����������7��y��������Q�P�D��c��)��q#�9s� >>}���r��];<��sZtf����j����`RZ�
=��?J�~�P)-V�RZ�������p���gS��h%-Q�PZ�L���W�I��/��ee��	HIIA��������q��g���h��)���#)+V���i���W/���/��"�/_���x��O}�<pY�<4����SZ�����Gi1��b*��*������y��F���{���'}�\�o���K4l�#F���|�A�q�x�����?x� Z�h��K��x����i�x�
}����
0}�t}��1cr�_[
�IDAT�vax��g.��;�I)-����b���?J����P)-V	RZ
� sZ
�h`��7i���#�4i��C�j2��uC�f���G�3�����sXV�Z���8l�����z�!,Y���'>��[�F�49���{���Zx.��t�K��f�.��#��o�#�>�����{������`�������{}_���|�<����
��e���(T�I��&��t�����xN�6y) �$��_�������F������4z�@����M�N�6�&�*;pz�+���}���7 �y�w������{����k�N:h�Z5�UF��m�^#P:���l[?u�;���s�[���K���j)Z��������lWL��)�3z�kK�D�*-�^#P:���kP�J�@��MZ�
��R^z�%H��D^d�W�����IJJ�"��'��F���������j���(�/��������1�'sn��e�>��H�H�H�H�H o�]wj�����>:�o��z�j�=_|���Oyy��P�\9��R�LT�VM/�����3gb����q��:
#	��dY���\��u�>^�H�H�H�H�H@���H��DM��[���ai����������q��7�<�#G���'�D�"E�p8��C]���*SH��������h���������l$@$@$@$@$@��I����OB$@$@$@$@$`���$]�M$@$@$@$@$`���2Bv`���5kt�Sxx�.y]�~}���|I@
�d�EY���I�={���r�� 7�p�+�����.C�w�������O����R�G�<�j�����hRuS�N�u�2�$~xdY�/[Tdff"99�*U��R���>;v�-���U�X�w�K;JK���%z��AP
+0@�J�)�-{�H>	�����������Ay��n��o�yu2'�0���[��RR���L����u������K)Fs��I]US�CEf����;��-[�����a�����~��`�z��c��c,�/""B���S������[T�i���H �(-�F�|A@$E*�I����<���z������o��^C���'�`�L�8s��=C��7��Bd�[����Y�2���F&	?~\GO���]�0p�@��rtt4�wu�������_������B6l$`���Ow��3f�@��E�����p�9)���jB6��%bMi15�v���K{|�������n��	��0����C������x��K@��z��W�7��RSS�EY�#�&���������GXXX�>(�<h$&&B�y��
���/�7o~���k�l�,{,���l$`����+1j�(=�DX$�"+%7n�/��#��g��z�������br0.��)-��������I�&a������E�R��^�
i	��
�{?WZd	��|�-b-���Q�;wF�v���)y��D@��"�?��3�,Y���\u�U�$��_�W_}��,��	�$0{�l<����F�����>�������rB��=��������i�Ro�#ri�Mi�4�5��j��)�
�����I�c���o��{��7�A?����J�H��A�P�n]�-�D^ds\�j�o60E@��d��2D���r0�`x������",���?��?$�7����M��"(����m�t������G�����Y�-_�L�6
M�4A���H6	�����\,)�S��A�=����!��������������/&����������Y!�=M�6����H�y=����uU��������:�'����O��,��In�Tl���kejD���HJ������b$����n���G�]IdP���]�&���j�"<�JK�p�`_��#��u�*U��6��,����Z��H�WV�^�?��f��O<�N�:�ow�����_��|��o�eyD��e}uk��eH@��'O������a��y��'�d����r���"���[ 	�l$`����I5E��i9r����k�����k�,��c$7u�������C���Z<�g�P���
Z�5�������F�$ ����"o��m�T�7a����P��4��$�s�/G�����;��#�����(=�d)�|�-����uS".�����+�~by-�j�2/�uP�Q��HtZ~'sT~/�Y�l��@~PZ�C���	�	�	�	�	������yA     �������%    �9J�����$@$@$@$@$@�!@i�-K$@$@$@$@$�s��#�I�H�H�H�H��C���Z<�H�H�H�H�H��(->G��	�	�	�	�	���%?�x,	�	�	�	�	���	PZ|��$    �JK~h�X     ����9/H$@$@$@$@$�������$@$@$@$@$@>'@i�9r^�H�H�H�H�H ?(-���cI�H�H�H�H�|N���s�� 	�	�	�	�	�@~PZ�C���	�	�	�	�	������yA     �������%    �9J�����$@$@$@$@$@�!@i�-K$@$@$@$@$�s��#�I�H�H�H�H��C���Z<�H�H�H�H�H��(->G��	�	�	�	�	���%?�x,	�	�	�	�	���	�?��z�DgIEND�B`�
#23David Rowley
dgrowleyml@gmail.com
In reply to: Tomas Vondra (#22)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 12 Mar 2024 at 12:25, Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

(b) slab is considerably slower

It would be interesting to modify SlabReset() to, instead of free()ing
the blocks, push the first SLAB_MAXIMUM_EMPTY_BLOCKS of them onto the
emptyblocks list.

That might give us an idea of how much overhead comes from malloc/free.

Having something like this as an option when creating a context might
be a good idea. generation.c now keeps 1 "freeblock" which currently
does not persist during context resets. Some memory context usages
might suit having an option like this. Maybe something like the
executor's per-tuple context, which perhaps (c|sh)ould be a generation
context... However, saying that, I see you measure it to be slightly
slower than aset.

David

#24David Rowley
dgrowleyml@gmail.com
In reply to: John Naylor (#21)
Re: Add bump memory context type and use it for tuplesorts

On Mon, 11 Mar 2024 at 22:09, John Naylor <johncnaylorls@gmail.com> wrote:

I ran the test function, but using 256kB and 3MB for the reset
frequency, and with 8,16,24,32 byte sizes (patched against a commit
after the recent hot/cold path separation). Images attached. I also
get a decent speedup with the bump context, but not quite as dramatic
as on your machine. It's worth noting that slab is the slowest for me.
This is an Intel i7-10750H.

Thanks for trying this out. I didn't check if the performance was
susceptible to the memory size before the reset. It certainly would
be once the allocation crosses some critical threshold of CPU cache
size, but probably it will also be to some extent regarding the number
of actual mallocs that are required underneath.

I see there's some discussion of bump in [1]/messages/by-id/CANWCAZbxxhysYtrPYZ-wZbDtvRPWoeTe7RQM1g_+4CB8Z6KYSQ@mail.gmail.com. Do you still have a
valid use case for bump for performance/memory usage reasons?

The reason I ask is due to what Tom mentioned in [2]/messages/by-id/3537323.1708125284@sss.pgh.pa.us ("It's not
apparent to me that the "bump context" idea is valuable enough to
foreclose ever adding more context types"). So, I'm just probing to
find other possible use cases that reinforce the usefulness of bump.
It would be interesting to try it in a few places to see what
performance gains could be had. I've not done much scouting around
the codebase for other uses other than non-bounded tuplesorts.

David

[1]: /messages/by-id/CANWCAZbxxhysYtrPYZ-wZbDtvRPWoeTe7RQM1g_+4CB8Z6KYSQ@mail.gmail.com
[2]: /messages/by-id/3537323.1708125284@sss.pgh.pa.us

#25John Naylor
johncnaylorls@gmail.com
In reply to: David Rowley (#24)
Re: Add bump memory context type and use it for tuplesorts

On Tue, Mar 12, 2024 at 6:41 AM David Rowley <dgrowleyml@gmail.com> wrote:

Thanks for trying this out. I didn't check if the performance was
susceptible to the memory size before the reset. It certainly would
be once the allocation crosses some critical threshold of CPU cache
size, but probably it will also be to some extent regarding the number
of actual mallocs that are required underneath.

I neglected to mention it, but the numbers I chose did have the L2/L3
cache in mind, but the reset frequency didn't seem to make much
difference.

I see there's some discussion of bump in [1]. Do you still have a
valid use case for bump for performance/memory usage reasons?

Yeah, that was part of my motivation for helping test, although my
interest is in saving memory in cases of lots of small allocations. It
might help if I make this a little more concrete, so I wrote a
quick-and-dirty function to measure the bytes used by the proposed TID
store and the vacuum's current array.

Using bitmaps really shines with a high number of offsets per block,
e.g. with about a million sequential blocks, and 49 offsets per block
(last parameter is a bound):

select * from tidstore_memory(0,1*1001*1000, 1,50);
array_mem | ts_mem
-----------+----------
294294000 | 42008576

The break-even point with this scenario is around 7 offsets per block:

select * from tidstore_memory(0,1*1001*1000, 1,8);
array_mem | ts_mem
-----------+----------
42042000 | 42008576

Below that, the array gets smaller, but the bitmap just has more empty
space. Here, 8 million bytes are used by the chunk header in bitmap
allocations, so the bump context would help there (I haven't actually
tried). Of course, the best allocation is no allocation at all, and I
have a draft patch to store up to 3 offsets in the last-level node's
pointer array, so for 2 or 3 offsets per block we're smaller than the
array again:

select * from tidstore_memory(0,1*1001*1000, 1,4);
array_mem | ts_mem
-----------+---------
18018000 | 8462336

Sequential blocks are not the worst case scenario for memory use, but
this gives an idea of what's involved. So, with aset, on average I
still expect to use quite a bit less memory, with some corner cases
that use more. The bump context would be some extra insurance to
reduce those corner cases, where there are a large number of blocks in
play.

#26Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: David Rowley (#23)
2 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On 3/12/24 00:40, David Rowley wrote:

On Tue, 12 Mar 2024 at 12:25, Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

(b) slab is considerably slower

It would be interesting to modify SlabReset() to, instead of free()ing
the blocks, push the first SLAB_MAXIMUM_EMPTY_BLOCKS of them onto the
emptyblocks list.

That might give us an idea of how much overhead comes from malloc/free.

Having something like this as an option when creating a context might
be a good idea. generation.c now keeps 1 "freeblock" which currently
does not persist during context resets. Some memory context usages
might suit having an option like this. Maybe something like the
executor's per-tuple context, which perhaps (c|sh)ould be a generation
context... However, saying that, I see you measure it to be slightly
slower than aset.

IIUC you're suggesting maybe it's a problem we free the blocks during
context reset, only to allocate them again shortly after, paying the
malloc overhead. This reminded the mempool idea I recently shared in the
nearby "scalability bottlenecks" thread [1]/messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com. So I decided to give this a
try and see how it affects this benchmark.

Attached is an updated version of the mempool patch, modifying all the
memory contexts (not just AllocSet), including the bump context. And
then also PDF with results from the two machines, comparing results
without and with the mempool. There's very little impact on small reset
values (128kB, 1MB), but pretty massive improvements on the 8MB test
(where it's a 2x improvement).

Nevertheless, it does not affect the relative performance very much. The
bump context is still the fastest, but the gap is much smaller.

Considering the mempool serves as a cache in between memory contexts and
glibc, eliminating most of the malloc/free calls, and essentially
keeping the blocks allocated, I doubt slab is slow because of malloc
overhead - at least in the "small" tests (but I haven't looked closer).

regards

[1]: /messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com
/messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

mempool.pdfapplication/pdf; name=mempool.pdfDownload
memory-pool-full-v2.txttext/plain; charset=UTF-8; name=memory-pool-full-v2.txtDownload
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index d2dcf526d62..9768d2176da 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -441,7 +441,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
 	 * Allocate the initial block.  Unlike other aset.c blocks, it starts with
 	 * the context header and its block header follows that.
 	 */
-	set = (AllocSet) malloc(firstBlockSize);
+	set = (AllocSet) MemoryPoolAlloc(firstBlockSize);
 	if (set == NULL)
 	{
 		if (TopMemoryContext)
@@ -579,13 +579,15 @@ AllocSetReset(MemoryContext context)
 		}
 		else
 		{
+			Size size = block->endptr - ((char *) block);
+
 			/* Normal case, release the block */
 			context->mem_allocated -= block->endptr - ((char *) block);
 
 #ifdef CLOBBER_FREED_MEMORY
 			wipe_mem(block, block->freeptr - ((char *) block));
 #endif
-			free(block);
+			MemoryPoolFree(block, size);
 		}
 		block = next;
 	}
@@ -649,7 +651,7 @@ AllocSetDelete(MemoryContext context)
 				freelist->num_free--;
 
 				/* All that remains is to free the header/initial block */
-				free(oldset);
+				MemoryPoolFree(oldset, keepersize);
 			}
 			Assert(freelist->num_free == 0);
 		}
@@ -666,6 +668,7 @@ AllocSetDelete(MemoryContext context)
 	while (block != NULL)
 	{
 		AllocBlock	next = block->next;
+		Size size = block->endptr - ((char *) block);
 
 		if (!IsKeeperBlock(set, block))
 			context->mem_allocated -= block->endptr - ((char *) block);
@@ -675,7 +678,7 @@ AllocSetDelete(MemoryContext context)
 #endif
 
 		if (!IsKeeperBlock(set, block))
-			free(block);
+			MemoryPoolFree(block, size);
 
 		block = next;
 	}
@@ -683,7 +686,7 @@ AllocSetDelete(MemoryContext context)
 	Assert(context->mem_allocated == keepersize);
 
 	/* Finally, free the context header, including the keeper block */
-	free(set);
+	MemoryPoolFree(set, keepersize);
 }
 
 /*
@@ -712,7 +715,7 @@ AllocSetAllocLarge(MemoryContext context, Size size, int flags)
 #endif
 
 	blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
-	block = (AllocBlock) malloc(blksize);
+	block = (AllocBlock) MemoryPoolAlloc(blksize);
 	if (block == NULL)
 		return MemoryContextAllocationFailure(context, size, flags);
 
@@ -905,7 +908,7 @@ AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
 		blksize <<= 1;
 
 	/* Try to allocate it */
-	block = (AllocBlock) malloc(blksize);
+	block = (AllocBlock) MemoryPoolAlloc(blksize);
 
 	/*
 	 * We could be asking for pretty big blocks here, so cope if malloc fails.
@@ -916,7 +919,7 @@ AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
 		blksize >>= 1;
 		if (blksize < required_size)
 			break;
-		block = (AllocBlock) malloc(blksize);
+		block = (AllocBlock) MemoryPoolAlloc(blksize);
 	}
 
 	if (block == NULL)
@@ -1071,6 +1074,7 @@ AllocSetFree(void *pointer)
 	{
 		/* Release single-chunk block. */
 		AllocBlock	block = ExternalChunkGetBlock(chunk);
+		Size size = block->endptr - ((char *) block);
 
 		/*
 		 * Try to verify that we have a sane block pointer: the block header
@@ -1104,7 +1108,7 @@ AllocSetFree(void *pointer)
 #ifdef CLOBBER_FREED_MEMORY
 		wipe_mem(block, block->freeptr - ((char *) block));
 #endif
-		free(block);
+		MemoryPoolFree(block, size);
 	}
 	else
 	{
@@ -1223,7 +1227,7 @@ AllocSetRealloc(void *pointer, Size size, int flags)
 		blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
 		oldblksize = block->endptr - ((char *) block);
 
-		block = (AllocBlock) realloc(block, blksize);
+		block = (AllocBlock) MemoryPoolRealloc(block, oldblksize, blksize);
 		if (block == NULL)
 		{
 			/* Disallow access to the chunk header. */
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
index f98a203a0ce..0ee64fc13ef 100644
--- a/src/backend/utils/mmgr/bump.c
+++ b/src/backend/utils/mmgr/bump.c
@@ -71,6 +71,7 @@ typedef struct BumpContext
 	uint32		maxBlockSize;	/* maximum block size */
 	uint32		nextBlockSize;	/* next block size to allocate */
 	uint32		allocChunkLimit;	/* effective chunk size limit */
+	uint32		allocSize;		/* effective chunk size limit */
 
 	dlist_head	blocks;			/* list of blocks with the block currently
 								 * being filled at the head */
@@ -183,7 +184,7 @@ BumpContextCreate(MemoryContext parent,
 	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
 	 * the context header and its block header follows that.
 	 */
-	set = (BumpContext *) malloc(allocSize);
+	set = (BumpContext *) MemoryPoolAlloc(allocSize);
 	if (set == NULL)
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -216,6 +217,7 @@ BumpContextCreate(MemoryContext parent,
 	set->initBlockSize = (uint32) initBlockSize;
 	set->maxBlockSize = (uint32) maxBlockSize;
 	set->nextBlockSize = (uint32) initBlockSize;
+	set->allocSize = (uint32) allocSize;
 
 	/*
 	 * Compute the allocation chunk size limit for this context.
@@ -289,10 +291,12 @@ BumpReset(MemoryContext context)
 void
 BumpDelete(MemoryContext context)
 {
+	BumpContext *set = (BumpContext *) context;
+
 	/* Reset to release all releasable BumpBlocks */
 	BumpReset(context);
 	/* And free the context header and keeper block */
-	free(context);
+	MemoryPoolFree(context, set->allocSize);
 }
 
 /*
@@ -326,7 +330,7 @@ BumpAllocLarge(MemoryContext context, Size size, int flags)
 	required_size = chunk_size + Bump_CHUNKHDRSZ;
 	blksize = required_size + Bump_BLOCKHDRSZ;
 
-	block = (BumpBlock *) malloc(blksize);
+	block = (BumpBlock *) MemoryPoolAlloc(blksize);
 	if (block == NULL)
 		return NULL;
 
@@ -458,7 +462,7 @@ BumpAllocFromNewBlock(MemoryContext context, Size size, int flags,
 	if (blksize < required_size)
 		blksize = pg_nextpower2_size_t(required_size);
 
-	block = (BumpBlock *) malloc(blksize);
+	block = (BumpBlock *) MemoryPoolAlloc(blksize);
 
 	if (block == NULL)
 		return MemoryContextAllocationFailure(context, size, flags);
@@ -603,6 +607,8 @@ BumpBlockFreeBytes(BumpBlock *block)
 static inline void
 BumpBlockFree(BumpContext *set, BumpBlock *block)
 {
+	Size	blksize = ((char *) block->endptr - (char *) block);
+
 	/* Make sure nobody tries to free the keeper block */
 	Assert(!IsKeeperBlock(set, block));
 
@@ -615,7 +621,7 @@ BumpBlockFree(BumpContext *set, BumpBlock *block)
 	wipe_mem(block, ((char *) block->endptr - (char *) block));
 #endif
 
-	free(block);
+	MemoryPoolFree(block, blksize);
 }
 
 /*
diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c
index 9124d9b9522..d2dfd1e8039 100644
--- a/src/backend/utils/mmgr/generation.c
+++ b/src/backend/utils/mmgr/generation.c
@@ -65,6 +65,7 @@ typedef struct GenerationContext
 	uint32		maxBlockSize;	/* maximum block size */
 	uint32		nextBlockSize;	/* next block size to allocate */
 	uint32		allocChunkLimit;	/* effective chunk size limit */
+	uint32		allocSize;		/* first block size */
 
 	GenerationBlock *block;		/* current (most recently allocated) block */
 	GenerationBlock *freeblock; /* pointer to an empty block that's being
@@ -206,7 +207,7 @@ GenerationContextCreate(MemoryContext parent,
 	 * Allocate the initial block.  Unlike other generation.c blocks, it
 	 * starts with the context header and its block header follows that.
 	 */
-	set = (GenerationContext *) malloc(allocSize);
+	set = (GenerationContext *) MemoryPoolAlloc(allocSize);
 	if (set == NULL)
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -242,6 +243,7 @@ GenerationContextCreate(MemoryContext parent,
 	set->initBlockSize = (uint32) initBlockSize;
 	set->maxBlockSize = (uint32) maxBlockSize;
 	set->nextBlockSize = (uint32) initBlockSize;
+	set->allocSize = allocSize;
 
 	/*
 	 * Compute the allocation chunk size limit for this context.
@@ -327,10 +329,16 @@ GenerationReset(MemoryContext context)
 void
 GenerationDelete(MemoryContext context)
 {
+	Size	allocSize;
+	GenerationContext *set = (GenerationContext *) context;
+
+	allocSize = set->allocSize;
+
 	/* Reset to release all releasable GenerationBlocks */
 	GenerationReset(context);
+
 	/* And free the context header and keeper block */
-	free(context);
+	MemoryPoolFree(context, allocSize);
 }
 
 /*
@@ -361,7 +369,7 @@ GenerationAllocLarge(MemoryContext context, Size size, int flags)
 	required_size = chunk_size + Generation_CHUNKHDRSZ;
 	blksize = required_size + Generation_BLOCKHDRSZ;
 
-	block = (GenerationBlock *) malloc(blksize);
+	block = (GenerationBlock *) MemoryPoolAlloc(blksize);
 	if (block == NULL)
 		return MemoryContextAllocationFailure(context, size, flags);
 
@@ -482,7 +490,7 @@ GenerationAllocFromNewBlock(MemoryContext context, Size size, int flags,
 	if (blksize < required_size)
 		blksize = pg_nextpower2_size_t(required_size);
 
-	block = (GenerationBlock *) malloc(blksize);
+	block = (GenerationBlock *) MemoryPoolAlloc(blksize);
 
 	if (block == NULL)
 		return MemoryContextAllocationFailure(context, size, flags);
@@ -663,6 +671,8 @@ GenerationBlockFreeBytes(GenerationBlock *block)
 static inline void
 GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
 {
+	Size	blksize = block->blksize;
+
 	/* Make sure nobody tries to free the keeper block */
 	Assert(!IsKeeperBlock(set, block));
 	/* We shouldn't be freeing the freeblock either */
@@ -677,7 +687,7 @@ GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
 	wipe_mem(block, block->blksize);
 #endif
 
-	free(block);
+	MemoryPoolFree(block, blksize);
 }
 
 /*
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index d6bf204ce27..1f1731cb6da 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -1726,3 +1726,1074 @@ pchomp(const char *in)
 		n--;
 	return pnstrdup(in, n);
 }
+
+/*
+ * Memory Pools
+ *
+ * Contexts may get memory either directly from the OS (libc) through malloc
+ * calls, but that has non-trivial overhead, depending on the allocation size
+ * and so on. And we tend to allocate fairly large amounts of memory, because
+ * contexts allocate blocks (starting with 1kB, quickly growing by doubling).
+ * A lot of hot paths also allocate pieces of memory exceeding the size limit
+ * and being allocated as a separate block.
+ *
+ * The contexts may cache the memory by keeping chunks, but it's limited to a
+ * single memory context (as AllocSet freelist), and only for the lifetime of
+ * a particular context instance. When the memory is reset/deleted, all the
+ * blocks are freed and retuned to the OS (libc).
+ *
+ * There's a rudimentary cache of memory contexts blocks, but this only keeps
+ * the keeper blocks, not any other blocks that may be needed.
+ *
+ * Memory pools are attempt to improve this by establishing a cache of blocks
+ * shared by all the memory contexts. A memory pool allocates blocks larger
+ * than 1kB, with doubling (1kB, 2kB, 4kB, ...). All the allocations come
+ * from memory contexts, and are either regular blocks (also starting at 1kB)
+ * or oversized chunks (a couple kB or larger). This means the lower limit
+ * is reasonable - there should be no smaller allocations.
+ *
+ * There's no explicit upper size limit - whatever could be used by palloc()
+ * can be requested from the pool. However, only blocks up to 8MB may be
+ * cached by the pool - larger allocations are not kept after pfree().
+ *
+ * To make the reuse possible, the blocks are grouped into size clasess the
+ * same way AllocSet uses for chunks. There are 14 size classes, starting
+ * at 1kB and ending at 8MB.
+ *
+ * This "rouding" applies even to oversized chunks. So e.g. allocating 27kB
+ * will allocate a 32kB block. This wastes memory, but it means the block
+ * may be reused by "regular" allocations. The amount of wasted memory could
+ * be reduced by using size classes with smaller steps, but that reduces the
+ * likelihood of reusing the block.
+ */
+
+
+#define MEMPOOL_MIN_BLOCK	1024L				/* smallest cached block */
+#define MEMPOOL_MAX_BLOCK	(8*1024L*1024L)		/* largest cached block */
+#define MEMPOOL_SIZES		14					/* 1kB -> 8MB */
+
+/*
+ * Maximum amount of memory to keep in cache for all size buckets. Sets a
+ * safety limit limit set on the blocks kept in the *cached* part of the
+ * pool. Each bucket starts with the same amount of memory (1/14 of this)
+ * and then we adapt the cache depending on cache hits/misses.
+ */
+#define MEMPOOL_SIZE_MAX	(128*1024L*1024L)
+
+/*
+ * Maximum number of blocks kept for the whole memory pool. This is used
+ * only to allocate the entries, so we assume all are in the smallest size
+ * bucket.
+ */
+#define MEMPOOL_MAX_BLOCKS	(MEMPOOL_SIZE_MAX / MEMPOOL_MIN_BLOCK)
+
+/*
+ * How often to rebalance the memory pool buckets (number of allocations).
+ * This is a tradeoff between the pool being adaptive and more overhead.
+ */
+#define	MEMPOOL_REBALANCE_DISTANCE		25000
+
+/*
+ * To enable debug logging for the memory pool code, build with -DMEMPOOL_DEBUG.
+ */
+#ifdef MEMPOOL_DEBUG
+
+#undef MEMPOOL_DEBUG
+#define	MEMPOOL_RANDOMIZE(ptr, size)	memset((ptr), 0x7f, (size))
+#define MEMPOOL_DEBUG(...)	fprintf (stderr, __VA_ARGS__)
+
+#else
+
+#define MEMPOOL_DEBUG(...)
+#define MEMPOOL_RANDOMIZE(ptr, size)
+
+#endif	/* MEMPOOL_DEBUG */
+
+
+/*
+ * Entries for a simple linked list of blocks to reuse.
+ */
+typedef struct MemPoolEntry
+{
+	void   *ptr;	/* allocated block (NULL in empty entries) */
+	struct	MemPoolEntry *next;
+} MemPoolEntry;
+
+/*
+ * Information about allocations of blocks of a certain size. We track the number
+ * of currently cached blocks, and also the number of allocated blocks (still
+ * used by the memory context).
+ *
+ * maxcached is the maximum number of free blocks to keep in the cache
+ *
+ * maxallocated is the maximum number of concurrently allocated blocks (from the
+ * point of the memory context)
+ */
+typedef struct MemPoolBucket
+{
+	int				nhits;			/* allocation cache hits */
+	int				nmisses;		/* allocation cache misses */
+	int				nallocated;		/* number of currently allocated blocks */
+	int				maxallocated;	/* max number of allocated blocks */
+	int				ncached;		/* number of free blocks (entry list) */
+	int				maxcached;		/* max number of free blocks to cache */
+	MemPoolEntry   *entry;
+} MemPoolBucket;
+
+/*
+ * MemPool - memory pool, caching allocations between memory contexts
+ *
+ * cache - stores free-d blocks that may be reused for future allocations,
+ * each slot is a list of MemPoolEntry elements using the "entries"
+ *
+ * entries - pre-allocated entries for the freelists, used by cache lists
+ *
+ * freelist - list of free cache entries (not used by the cache lists)
+ *
+ * The meaning of the freelist is somewhat inverse - when a block is freed
+ * by the memory context above, we need to add it to the cache. To do that
+ * we get an entry from the freelist, and add it to the cache. So free-ing
+ * a block removes an entry from the mempool freelist.
+ */
+typedef struct MemPool
+{
+	/* LIFO cache of free-d blocks of eligible sizes (1kB - 1MB, doubled) */
+	MemPoolBucket	cache[MEMPOOL_SIZES];
+
+	/* pre-allocated entries for cache of free-d blocks */
+	MemPoolEntry	entries[MEMPOOL_SIZES * MEMPOOL_MAX_BLOCKS];
+
+	/* head of freelist (entries from the array) */
+	MemPoolEntry   *freelist;
+
+	/* memory limit / accounting */
+	int64 mem_allowed;
+	int64 mem_allocated;
+	int64 mem_cached;
+	int64 num_requests;
+} MemPool;
+
+static MemPool *pool = NULL;
+
+static void
+AssertCheckMemPool(MemPool *p)
+{
+#ifdef ASSERT_CHECKING
+	int	nused = 0;
+	int	nfree = 0;
+	int64	mem_cached = 0;
+	Size	block_size = MEMPOOL_MIN_BLOCK;
+
+	Assert(p->mem_allocated >= 0);
+	Assert(p->mem_cached >= 0);
+
+	/* count the elements in the various cache buckets */
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		int	count = 0;
+
+		Assert(p->cache[i].ncached >= 0);
+		Assert(p->cache[i].ncached <= p->cache[i].maxcached);
+
+		entry = p->cache[i].entry;
+
+		while (entry)
+		{
+			Assert(entry->ptr);
+
+			entry = entry->next;
+			count++;
+		}
+
+		Assert(count == p->cache[i].ncached);
+
+		nused += count;
+		mem_cached += (count * block_size);
+
+		block_size *= 2;
+	}
+
+	/* now count the elements in the freelist */
+	entry = p->freelist;
+	while (entry)
+	{
+		nfree++;
+		entry = entry->next;
+	}
+
+	Assert(nfree + nused == MEMPOOL_SIZES * MEMPOOL_MAX_BLOCKS);
+	Assert(mem_cached == p->mem_cached);
+#endif
+}
+
+static void MemoryPoolRebalanceBuckets(void);
+static void MemoryPoolEnforceSizeLimit(Size request_size, int index);
+
+/*
+ * MemoryPoolInit
+ *		initialize the global memory pool
+ *
+ * Initialize the overall memory pool structure, and also link all entries
+ * into a freelist.
+ */
+static void
+MemoryPoolInit(void)
+{
+	Size	size = MEMPOOL_MIN_BLOCK;
+
+	/* bail out if already initialized */
+	if (pool)
+		return;
+
+	/* allocate the basic structure */
+	pool = malloc(sizeof(MemPool));
+	memset(pool, 0, sizeof(MemPool));
+
+	/* initialize the frelist - put all entries to the list */
+	pool->freelist = &pool->entries[0];
+
+	for (int i = 0; i < (MEMPOOL_SIZES * MEMPOOL_MAX_BLOCKS - 1); i++)
+	{
+		if (i < (MEMPOOL_SIZES * MEMPOOL_MAX_BLOCKS - 1))
+			pool->entries[i].next = &pool->entries[i+1];
+		else
+			pool->entries[i].next = NULL;
+	}
+
+	/* set default maximum counts of entries for each size class */
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		pool->cache[i].maxcached = (MEMPOOL_SIZE_MAX / MEMPOOL_SIZES / size);
+		size *= 2;
+	}
+
+	AssertCheckMemPool(pool);
+}
+
+/*
+ * MemoryPoolEntrySize
+ *		calculate the size of the block to allocate for a given request size
+ *
+ * The request sizes are grouped into pow(2,n) classes, starting at 1kB and
+ * ending at 8MB. Which means there are 14 size classes.
+ */
+static Size
+MemoryPoolEntrySize(Size size)
+{
+	Size	result;
+
+	/*
+	 * We shouldn't really get many malloc() for such small elements through
+	 * memory contexts, so just use the smallest block.
+	 */
+	if (size < MEMPOOL_MIN_BLOCK)
+		return MEMPOOL_MIN_BLOCK;
+
+	/*
+	 * We can get various large allocations - we don't want to cache those,
+	 * not waste space on doubling them, so just allocate them directly.
+	 * Maybe the limit should be separate/lower, like 1MB.
+	 */
+	if (size > MEMPOOL_MAX_BLOCK)
+		return size;
+
+	/*
+	 * Otherwise just calculate the first block larger than the request.
+	 *
+	 * XXX Maybe there's a better way to calculate this? The number of loops
+	 * should be very low, though (less than MEMPOOL_SIZES, i.e. 14).
+	 */
+	result = MEMPOOL_MIN_BLOCK;
+	while (size > result)
+		result *= 2;
+
+	MEMPOOL_DEBUG("%d MempoolEntrySize %lu => %lu\n", getpid(), size, result);
+
+	/* the block size has to be sufficient for the requested size */
+	Assert(size <= result);
+
+	return result;
+}
+
+/*
+ * MemoryPoolEntryIndex
+ *		Calculate the cache index for a given entry size.
+ *
+ * XXX Always called right after MemoryPoolEntrySize, so maybe it should be
+ * merged into a single function, so that the loop happens only once.
+ */
+static int
+MemoryPoolEntryIndex(Size size)
+{
+	int		blockIndex = 0;
+	Size	blockSize = MEMPOOL_MIN_BLOCK;
+
+	/* is size possibly in cache? */
+	if (size < MEMPOOL_MIN_BLOCK || size > MEMPOOL_MAX_BLOCK)
+		return -1;
+
+	/* calculate where to maybe cache the entry */
+	while (blockSize <= MEMPOOL_MAX_BLOCK)
+	{
+		Assert(size >= blockSize);
+
+		if (size == blockSize)
+		{
+			Assert(blockIndex < MEMPOOL_SIZES);
+			return blockIndex;
+		}
+
+		blockIndex++;
+		blockSize *= 2;
+	}
+
+	/* not eligible for caching after all */
+	return -1;
+}
+
+/*
+ * Check that the entry size is valid and matches the class index - if smaller
+ * than 8MB, it needs to be in one of the valid classes.
+ */
+static void
+AssertCheckEntrySize(Size size, int cacheIndex)
+{
+#ifdef USE_ASSERT_CHECKING
+	int	blockSize = MEMPOOL_MIN_BLOCK;
+	int	blockIndex = 0;
+
+	Assert(cacheIndex >= -1 && cacheIndex < MEMPOOL_SIZES);
+
+	/* all sizes in the valid range should be in one of the slots */
+	if (cacheIndex == -1)
+		Assert(size < MEMPOOL_MIN_BLOCK || size > MEMPOOL_MAX_BLOCK);
+	else
+	{
+		/* calculate the block size / index for the given size */
+		while (size > blockSize)
+		{
+			blockSize *= 2;
+			blockIndex++;
+		}
+
+		Assert(size == blockSize);
+		Assert(cacheIndex == blockIndex);
+	}
+#endif
+}
+
+/*
+ * MemoryPoolAlloc
+ *		Allocate a block from the memory pool.
+ *
+ * The block may come either from cache - if available - or from malloc().
+ */
+void *
+MemoryPoolAlloc(Size size)
+{
+	int	index;
+	void *ptr;
+
+	MemoryPoolInit();
+
+	pool->num_requests++;
+
+	MemoryPoolRebalanceBuckets();
+
+	/* maybe override the requested size */
+	size = MemoryPoolEntrySize(size);
+	index = MemoryPoolEntryIndex(size);
+
+	/* cross-check the size and index */
+	AssertCheckEntrySize(size, index);
+
+	/* try to enforce the memory limit */
+	MemoryPoolEnforceSizeLimit(size, index);
+
+	/* Is the block eligible to be in the cache? Or is it too large/small? */
+	if (index >= 0)
+	{
+		MemPoolEntry *entry = pool->cache[index].entry;
+
+		/*
+		 * update the number of allocated chunks, and the high watermark
+		 *
+		 * We do this even if there's no entry in the cache.
+		 */
+		pool->cache[index].nallocated++;
+		pool->cache[index].maxallocated = Max(pool->cache[index].nallocated,
+											  pool->cache[index].maxallocated);
+
+		/*
+		 * If we have a cached block for this size, we're done. Remove it
+		 * from the cache and return the entry to the freelist.
+		 */
+		if (entry != NULL)
+		{
+			/* remember the pointer (we'll reset the entry) */
+			ptr = entry->ptr;
+			entry->ptr = NULL;
+
+			/* remove the entry from the cache */
+			pool->cache[index].entry = entry->next;
+			pool->cache[index].ncached--;
+
+			/* return the entry to the freelist */
+			entry->next = pool->freelist;
+			pool->freelist = entry;
+
+			MEMPOOL_RANDOMIZE(ptr, size);
+			MEMPOOL_DEBUG("%d MemoryPoolAlloc %lu => %d %p HIT\n", getpid(), size, index, ptr);
+
+			/* update memory accounting */
+			Assert(pool->mem_cached >= size);
+
+			pool->mem_cached -= size;
+			pool->mem_allocated += size;
+
+			pool->cache[index].nhits++;
+
+			AssertCheckMemPool(pool);
+
+			return ptr;
+		}
+
+		pool->cache[index].nmisses++;
+	}
+
+	/*
+	 * Either too small/large for the cache, or there's no available block of
+	 * the right size.
+	 */
+	ptr = malloc(size);
+
+	MEMPOOL_RANDOMIZE(ptr, size);
+	MEMPOOL_DEBUG("%d MemoryPoolAlloc %lu => %d %p MISS\n", getpid(), size, index, ptr);
+
+	/* update memory accounting */
+	pool->mem_allocated += size;
+
+	/* maybe we should track the number of over-sized allocations too? */
+	// pool->cache_misses++;
+
+	AssertCheckMemPool(pool);
+
+	return ptr;
+}
+
+/*
+ * MemoryPoolShouldCache
+ *		Should we put the entry into cache at the given index?
+ */
+static bool
+MemoryPoolShouldCache(Size size, int index)
+{
+	MemPoolBucket  *entry = &pool->cache[index];
+
+	/* not in any pool bucket */
+	if (index == -1)
+		return false;
+
+	/*
+	 * Bail out if no freelist entries.
+	 *
+	 * XXX This shouldn't be possible, as we size the freeslist as if all classes
+	 * could have the maximum number of entries (but the actual number grops to
+	 * 1/2 with each size class).
+	 */
+	if (!pool->freelist)
+		return false;
+
+	/* Memory limit is set, and we'd exceed it? Don't cache. */
+	if ((pool->mem_allowed > 0) &&
+		(pool->mem_allocated + pool->mem_cached + size > pool->mem_allowed))
+		return false;
+
+	/* Did we already reach the maximum size of the size class? */
+	return (entry->ncached < entry->maxcached);
+}
+
+/*
+ * MemoryPoolFree
+ *		Free a block, maybe add it to the memory pool cache.
+ */
+void
+MemoryPoolFree(void *pointer, Size size)
+{
+	int	index = 0;
+
+	MemoryPoolInit();
+
+	/*
+	 * Override the requested size (provided by the memory context), calculate
+	 * the appropriate size class index.
+	 */
+	size = MemoryPoolEntrySize(size);
+	index = MemoryPoolEntryIndex(size);
+
+	AssertCheckEntrySize(size, index);
+
+	/* check that we've correctly accounted for this block during allocation */
+	Assert(pool->mem_allocated >= size);
+
+	/*
+	 * update the number of allocated blocks (if eligible for cache)
+	 *
+	 * XXX Needs to happen even if we don't add the block to the cache.
+	 */
+	if (index != -1)
+		pool->cache[index].nallocated--;
+
+	/*
+	 * Should we cache this entry? Do we have entries for the freelist, and
+	 * do we have free space in the size class / memory pool as a whole?
+	 */
+	if (MemoryPoolShouldCache(size, index))
+	{
+		MemPoolEntry *entry;
+
+		entry = pool->freelist;
+		pool->freelist = entry->next;
+
+		/* add the entry to the cache, update number of entries in this bucket */
+		entry->next = pool->cache[index].entry;
+		pool->cache[index].entry = entry;
+		pool->cache[index].ncached++;
+
+		entry->ptr = pointer;
+
+		MEMPOOL_RANDOMIZE(pointer, size);
+		MEMPOOL_DEBUG("%d MemoryPoolFree %lu => %d %p ADD\n", getpid(), size, index, pointer);
+
+		/* update accounting */
+		pool->mem_cached += size;
+		pool->mem_allocated -= size;
+
+		AssertCheckMemPool(pool);
+
+		return;
+	}
+
+	MEMPOOL_RANDOMIZE(pointer, size);
+	MEMPOOL_DEBUG("%d MemoryPoolFree %lu => %d FULL\n", getpid(), size, index);
+
+	/* update accounting */
+	pool->mem_allocated -= size;
+
+	AssertCheckMemPool(pool);
+
+	free(pointer);
+}
+
+/*
+ * MemoryPoolRealloc
+ *		reallocate a previously allocated block
+ *
+ * XXX Maybe this should use the cache too. Right now we just call realloc()
+ * after updating the cache counters. And maybe it should enforce the memory
+ * limit, just like we do in MemoryPoolAlloc().
+ */
+void *
+MemoryPoolRealloc(void *pointer, Size oldsize, Size newsize)
+{
+	void *ptr;
+
+	int		oldindex,
+			newindex;
+
+	MemoryPoolInit();
+
+	oldsize = MemoryPoolEntrySize(oldsize);
+	newsize = MemoryPoolEntrySize(newsize);
+
+	/* XXX Maybe if (oldsize >= newsize) we don't need to do anything? */
+
+	oldindex = MemoryPoolEntryIndex(oldsize);
+	newindex = MemoryPoolEntryIndex(newsize);
+
+	if (oldindex != -1)
+		pool->cache[oldindex].nallocated--;
+
+	if (newindex != -1)
+	{
+		pool->cache[newindex].nallocated++;
+		pool->cache[newindex].maxallocated = Max(pool->cache[newindex].nallocated,
+												 pool->cache[newindex].maxallocated);
+	}
+
+	MEMPOOL_DEBUG("%d MemoryPoolRealloc old %lu => %p\n", getpid(), oldsize, pointer);
+
+	ptr = realloc(pointer, newsize);
+
+	MEMPOOL_DEBUG("%d MemoryPoolRealloc new %lu => %p\n", getpid(), newsize, ptr);
+
+	/* update accounting */
+	Assert(pool->mem_allocated >= oldsize);
+
+	pool->mem_allocated -= oldsize;
+	pool->mem_allocated += newsize;
+
+	AssertCheckMemPool(pool);
+
+	return ptr;
+}
+
+/*
+ * MemoryPoolRebalanceBuckets
+ *		Rebalance the cache capacity for difference size classes.
+ *
+ * The goal of the rebalance is to adapt the cache capacity to changes in the
+ * workload - release blocks of sizes that are no longer needed, allow caching
+ * for new block sizes etc.
+ *
+ * The rebalance happens every MEMPOOL_REBALANCE_DISTANCE allocations - it needs
+ * to happen often enough to adapt to the workload changes, but not too often
+ * to cause significant overhead. The distance also needs to be sufficient to
+ * have a reasonable representation of the allocations.
+ *
+ * The rebalance happens in three phases:
+ *
+ * 1) shrink oversized buckets (maxallocated < maxcached)
+ *
+ * 2) enlarge undersized buckets (maxcached < maxallocated)
+ *
+ * 3) distribute remaining capacity (if any) uniformly
+ *
+ * The reduction in (1) is gradual, i.e. instead of setting maxcached to the
+ * maxallocated value (which may be seen as the minimum capacity needed), we
+ * only go halfway there. The intent is to dampen the transition in case the
+ * current counter is not entirely representative.
+ *
+ * The bucket enlarging in step (2) is proportional to the number of misses
+ * for each bucket (with respect to the total number of misses in the buckets
+ * that are too small). We however don't oversize the bucket - we assign at
+ * most (maxallocated - maxcached) entries, not more in this step.
+ *
+ * Finally, we simply take the remaining unallocated/unassigned memory (up to
+ * MEMPOOL_SIZE_MAX), and distribute it to all the buckets uniformly. That is,
+ * each bucket gets the same amount (rounded to entries of appropriate size).
+ *
+ * XXX Maybe we should have a parameter for the dampening factor in (1), and
+ * not just use 0.5. For example, maybe 0.75 would be better?
+ *
+ * XXX This assumes misses for different buckets are equally expensive, but
+ * that may not be the case. It's likely a miss is proportional to the size
+ * of the block, so maybe we should consider that and use the size as weight
+ * for the cache miss.
+ */
+static void
+MemoryPoolRebalanceBuckets(void)
+{
+	Size	block_size;
+	int64	redistribute_bytes;
+	int64	assigned_bytes = 0;
+	int64	num_total_misses = 0;
+
+	/* only do this once every MEMPOOL_REBALANCE_DISTANCE allocations */
+	if (pool->num_requests < MEMPOOL_REBALANCE_DISTANCE)
+		return;
+
+#ifdef MEMPOOL_DEBUG
+	/* print info about the cache and individual size buckets before the rebalance */
+	MEMPOOL_DEBUG("%d mempool rebalance requests %ld allowed %ld allocated %ld cached %ld\n",
+				  getpid(), pool->num_requests,
+				  pool->mem_allowed, pool->mem_allocated, pool->mem_cached);
+
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		MEMPOOL_DEBUG("%d mempool rebalance bucket %d hit %d miss %d (%.1f%%) maxcached %d cached %d maxallocated %d allocated %d\n",
+					  getpid(), i, pool->cache[i].nhits, pool->cache[i].nmisses,
+					  pool->cache[i].nhits * 100.0 / Max(1, pool->cache[i].nhits + pool->cache[i].nmisses),
+					  pool->cache[i].maxcached, pool->cache[i].ncached,
+					  pool->cache[i].maxallocated, pool->cache[i].nallocated);
+	}
+#endif
+
+	/*
+	 * Are there buckets with cache that is unnecessarily large? That is, with
+	 * (ncached + nallocated > maxallocated). If yes, we release half of that
+	 * and put that into a budget that we can redistribute.
+	 *
+	 * XXX We release half to somewhat dampen the changes over time.
+	 */
+	block_size = MEMPOOL_MIN_BLOCK;
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		/*
+		 * If the cache is large enough to serve all allocations, try making it
+		 * a bit smaller and cut half the extra space (and maybe also free the
+		 * unnecessary blocks).
+		 */
+		if (pool->cache[i].maxcached > pool->cache[i].maxallocated)
+		{
+			int	nentries;
+
+			pool->cache[i].maxcached
+				= (pool->cache[i].maxcached + pool->cache[i].maxallocated) / 2;
+
+			nentries = (pool->cache[i].ncached + pool->cache[i].nallocated);
+			nentries -= pool->cache[i].maxcached;
+
+			/* release enough entries from the cache */
+			while (nentries > 0)
+			{
+				MemPoolEntry *entry = pool->cache[i].entry;
+
+				pool->cache[i].entry = entry->next;
+				pool->cache[i].ncached--;
+
+				free(entry->ptr);
+				entry->ptr = NULL;
+
+				/* add the entry to the freelist */
+				entry->next = pool->freelist;
+				pool->freelist = entry;
+
+				Assert(pool->mem_cached >= block_size);
+
+				/* update accounting */
+				pool->mem_cached -= block_size;
+
+				nentries--;
+			}
+		}
+
+		/* remember how many misses we saw in the undersized buckets */
+		num_total_misses += pool->cache[i].nmisses;
+
+		/* remember how much space we already allocated to this bucket */
+		assigned_bytes += (pool->cache[i].maxcached * block_size);
+
+		/* double the block size */
+		block_size = (block_size << 1);
+	}
+
+	/*
+	 * How much memory we can redistribute? Start with the memory limit,
+	 * and subtract the space currently allocated and assigned to cache.
+	 */
+	redistribute_bytes = Max(pool->mem_allowed, MEMPOOL_SIZE_MAX);
+	redistribute_bytes -= (pool->mem_allocated);
+	redistribute_bytes -= assigned_bytes;
+
+	/*
+	 * Make sure it's not negative (might happen if there's a lot of
+	 * allocated memory).
+	 */
+	redistribute_bytes = Max(0, redistribute_bytes);
+
+	MEMPOOL_DEBUG("%d mempool rebalance can redistribute %ld bytes, allocated %ld bytes, assigned %ld bytes, total misses %ld\n",
+				  getpid(), redistribute_bytes, pool->mem_allocated, assigned_bytes, num_total_misses);
+
+	/*
+	 * Redistribute the memory based on the number of misses, and reset the
+	 * various counters, so that the next round begins afresh.
+	 */
+	if (redistribute_bytes > 0)
+	{
+		block_size = MEMPOOL_MIN_BLOCK;
+		for (int i = 0; i < MEMPOOL_SIZES; i++)
+		{
+			int64	nbytes;
+			int		nentries;
+
+			/* Are we missing entries in cache for this slot? */
+			if (pool->cache[i].maxcached < pool->cache[i].maxallocated)
+			{
+				int nmissing = (pool->cache[i].maxallocated - pool->cache[i].maxcached);
+
+				/*
+				 * How many entries we can add to this size bucket, based on the number
+				 * of cache misses?
+				 */
+				nbytes = redistribute_bytes * pool->cache[i].nmisses / Max(1, num_total_misses);
+				nentries = (nbytes / block_size);
+
+				/* But don't add more than we need. */
+				nentries = Min(nentries, nmissing);
+
+				pool->cache[i].maxcached += nentries;
+				assigned_bytes += nentries * block_size;
+			}
+
+			/* double the block size */
+			block_size = (block_size << 1);
+		}
+	}
+
+	MEMPOOL_DEBUG("%d mempool rebalance done allocated %ld bytes, assigned %ld bytes\n",
+				  getpid(), pool->mem_allocated, assigned_bytes);
+
+	/*
+	 * If we still have some memory, redistribute it uniformly.
+	 */
+	redistribute_bytes = Max(pool->mem_allowed, MEMPOOL_SIZE_MAX);
+	redistribute_bytes -= (pool->mem_allocated);
+	redistribute_bytes -= assigned_bytes;
+
+	/*
+	 * Make sure it's not negative (might happen if there's a lot of
+	 * allocated memory).
+	 */
+	redistribute_bytes = Max(0, redistribute_bytes);
+
+	MEMPOOL_DEBUG("%d mempool rebalance remaining bytes %ld, allocated %ld bytes, assigned %ld bytes\n",
+				  getpid(), redistribute_bytes, pool->mem_allocated, assigned_bytes);
+
+	block_size = MEMPOOL_MIN_BLOCK;
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		int	nentries = (redistribute_bytes / MEMPOOL_SIZES / block_size);
+
+		pool->cache[i].maxcached += nentries;
+
+		/* also reset the various counters */
+		pool->cache[i].maxallocated = pool->cache[i].nallocated;
+		pool->cache[i].nhits = 0;
+		pool->cache[i].nmisses = 0;
+
+		/* double the block size */
+		block_size = (block_size << 1);
+	}
+
+	MEMPOOL_DEBUG("%d mempool rebalance done\n", getpid());
+
+#ifdef MEMPOOL_DEBUG
+	/* print some info about cache hit ratio, but only once in a while */
+	block_size = MEMPOOL_MIN_BLOCK;
+	assigned_bytes = 0;
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		MEMPOOL_DEBUG("%d mempool rebalance bucket %d maxcached %d cached %d maxallocated %d allocated %d\n",
+					  getpid(), i,
+					  pool->cache[i].maxcached, pool->cache[i].ncached,
+					  pool->cache[i].maxallocated, pool->cache[i].nallocated);
+
+		assigned_bytes += (pool->cache[i].maxcached * block_size);
+
+		/* double the block size */
+		block_size = (block_size << 1);
+	}
+	MEMPOOL_DEBUG("%d mempool rebalance allocated %ld assigned %ld (total %ld kB)\n",
+				  getpid(), pool->mem_allocated, assigned_bytes,
+				  (pool->mem_allocated + assigned_bytes) / 1024L);
+#endif
+
+	/* start new rebalance period */
+	pool->num_requests = 0;
+}
+
+/*
+ * MemoryPoolEnforceMaxCounts
+ *		release cached blocks exceeding the maxcached for a given bucket
+ *
+ * XXX This gets called only from MemoryPoolSetSizeLimit, which updates the
+ * maxcount based on the memory limit. Maybe it should be integrated into
+ * that directly?
+ *
+ * XXX Or maybe we should simply do the rebalancing for the new limit?
+ */
+static void
+MemoryPoolEnforceMaxCounts(void)
+{
+	Size	block_size = MEMPOOL_MAX_BLOCK;
+
+	/* nothing cached, so can't release anything */
+	if (pool->mem_cached == 0)
+		return;
+
+	/*
+	 * Walk through the buckets, make sure that no bucket has too many cached
+	 * entries.
+	 */
+	for (int i = MEMPOOL_SIZES - 1; i >= 0; i--)
+	{
+		while (pool->cache[i].entry)
+		{
+			MemPoolEntry *entry = pool->cache[i].entry;
+
+			/* we're within the limit, bail out */
+			if (pool->cache[i].ncached <= pool->cache[i].maxcached)
+				break;
+
+			pool->cache[i].entry = entry->next;
+			pool->cache[i].ncached--;
+
+			free(entry->ptr);
+			entry->ptr = NULL;
+
+			/* add the entry to the freelist */
+			entry->next = pool->freelist;
+			pool->freelist = entry;
+
+			Assert(pool->mem_cached >= block_size);
+
+			/* update accounting */
+			pool->mem_cached -= block_size;
+		}
+
+		/* double the block size */
+		block_size = (block_size << 1);
+	}
+
+	MEMPOOL_DEBUG("%d MemoryPoolEnforceMaxCounts allocated %ld cached %ld\n",
+				  getpid(), pool->mem_allocated, pool->mem_cached);
+
+	AssertCheckMemPool(pool);
+}
+
+/*
+ * MemoryPoolEnforceSizeLimit
+ *		Release cached blocks to allow allocating a block of a given size.
+ *
+ * If actually freeing blocks is needed, we free more of them, so that we don't
+ * need to do that too often. We free at least 2x the amount of space we need,
+ * or 25% of the limit, whichever is larger.
+ *
+ * We free memory from the largest blocks, because that's likely to free memory
+ * the fastest. And we don't alocate those very often.
+ *
+ * XXX Maybe we should free memory in the smaller classes too, so that we don't
+ * end up keeping many unnecessary old blocks, while trashing the large class.
+ */
+static void
+MemoryPoolEnforceSizeLimit(Size request_size, int index)
+{
+	int64	threshold,
+			needtofree;
+
+	Size	block_size = MEMPOOL_MAX_BLOCK;
+
+	/* no memory limit set */
+	if (pool->mem_allowed == 0)
+		return;
+
+	/* nothing cached, so can't release anything */
+	if (pool->mem_cached == 0)
+		return;
+
+	/*
+	 * With the new request, would we exceed the memory limit? we need
+	 * to count both the allocated and cached memory.
+	 *
+	 * XXX In principle the block may be already available in cache, in which
+	 * case we don't need to add it to the allocated + cached figure.
+	 */
+	if (pool->mem_allocated + pool->mem_cached + request_size <= pool->mem_allowed)
+		return;
+
+	/*
+	 * How much we need to release? we don't want to allocate just enough
+	 * for the one request, but a bit more, to prevent trashing.
+	 */
+	threshold = Min(Max(0, pool->mem_allowed - 2 * request_size),
+					pool->mem_allowed * 0.75);
+
+	Assert((threshold >= 0) && (threshold < pool->mem_allowed));
+
+	/*
+	 * How much we need to free, to get under the theshold? Can't free more
+	 * than we have in the cache, though.
+	 *
+	 * XXX One we free at least this amount of memory, we're done.
+	 */
+	needtofree = (pool->mem_allocated + pool->mem_cached + request_size) - threshold;
+	needtofree = Min(needtofree, pool->mem_cached);
+
+	MEMPOOL_DEBUG("%d MemoryPoolMaybeShrink total %ld cached %ld threshold %ld needtofree %ld\n",
+				  getpid(), pool->mem_allocated + pool->mem_cached, pool->mem_cached, threshold, needtofree);
+
+	/* Is it even eligible to be in the cache? */
+	for (int i = MEMPOOL_SIZES - 1; i >= 0; i--)
+	{
+		/* did we free enough memory? */
+		if (needtofree <= 0)
+			break;
+
+		while (pool->cache[i].entry)
+		{
+			MemPoolEntry *entry = pool->cache[i].entry;
+
+			pool->cache[i].entry = entry->next;
+			pool->cache[i].ncached--;
+
+			free(entry->ptr);
+			entry->ptr = NULL;
+
+			/* add the entry to the freelist */
+			entry->next = pool->freelist;
+			pool->freelist = entry;
+
+			needtofree -= block_size;
+
+			/* did we free enough memory? */
+			if (needtofree <= 0)
+				break;
+		}
+
+		block_size = (block_size >> 1);
+	}
+
+	MEMPOOL_DEBUG("%d MemoryPoolEnforceMemoryLimit allocated %ld cached %ld needtofree %ld\n",
+				  getpid(), pool->mem_allocated, pool->mem_cached, needtofree);
+
+	AssertCheckMemPool(pool);
+}
+
+/*
+ * MemoryPoolSetSizeLimit
+ *		Set size limit for the memory pool.
+ */
+void
+MemoryPoolSetSizeLimit(int64 size)
+{
+	Size	blksize = MEMPOOL_MIN_BLOCK;
+	Size	maxsize;
+
+	Assert(pool);
+	Assert(size >= 0);
+
+	pool->mem_allowed = size;
+
+	/* also update the max number of entries for each class size */
+
+	if (size > 0)
+		maxsize = size / MEMPOOL_SIZES;
+	else
+		maxsize = MEMPOOL_SIZE_MAX;
+
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		pool->cache[i].maxcached = (maxsize / blksize);
+		blksize *= 2;
+	}
+
+	/* enforce the updated maxcached limit */
+	MemoryPoolEnforceMaxCounts();
+
+	/* also enforce the general memory limit  */
+	MemoryPoolEnforceSizeLimit(0, -1);
+}
+
+/*
+ * MemoryPoolGetSizeAndCounts
+ */
+void
+MemoryPoolGetSizeAndCounts(int64 *mem_allowed, int64 *mem_allocated, int64 *mem_cached,
+						   int64 *cache_hits, int64 *cache_misses)
+{
+	Assert(pool);
+
+	*mem_allowed = pool->mem_allowed;
+	*mem_allocated = pool->mem_allocated;
+	*mem_cached = pool->mem_cached;
+
+	*cache_hits = 0;
+	*cache_misses = 0;
+
+	for (int i = 0; i < MEMPOOL_SIZES; i++)
+	{
+		*cache_hits += pool->cache[i].nhits;
+		*cache_misses += pool->cache[i].nmisses;
+	}
+}
diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c
index 516e1c95aaf..53f1271c288 100644
--- a/src/backend/utils/mmgr/slab.c
+++ b/src/backend/utils/mmgr/slab.c
@@ -359,9 +359,7 @@ SlabContextCreate(MemoryContext parent,
 		elog(ERROR, "block size %zu for slab is too small for %zu-byte chunks",
 			 blockSize, chunkSize);
 
-
-
-	slab = (SlabContext *) malloc(Slab_CONTEXT_HDRSZ(chunksPerBlock));
+	slab = (SlabContext *) MemoryPoolAlloc(Slab_CONTEXT_HDRSZ(chunksPerBlock));
 	if (slab == NULL)
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -451,7 +449,7 @@ SlabReset(MemoryContext context)
 #ifdef CLOBBER_FREED_MEMORY
 		wipe_mem(block, slab->blockSize);
 #endif
-		free(block);
+		MemoryPoolFree(block, slab->blockSize);
 		context->mem_allocated -= slab->blockSize;
 	}
 
@@ -467,7 +465,7 @@ SlabReset(MemoryContext context)
 #ifdef CLOBBER_FREED_MEMORY
 			wipe_mem(block, slab->blockSize);
 #endif
-			free(block);
+			MemoryPoolFree(block, slab->blockSize);
 			context->mem_allocated -= slab->blockSize;
 		}
 	}
@@ -484,10 +482,13 @@ SlabReset(MemoryContext context)
 void
 SlabDelete(MemoryContext context)
 {
+	SlabContext *set PG_USED_FOR_ASSERTS_ONLY = (SlabContext *) context;
+
 	/* Reset to release all the SlabBlocks */
 	SlabReset(context);
+
 	/* And free the context header */
-	free(context);
+	MemoryPoolFree(context, Slab_CONTEXT_HDRSZ(set->chunksPerBlock));
 }
 
 /*
@@ -562,7 +563,7 @@ SlabAllocFromNewBlock(MemoryContext context, Size size, int flags)
 	}
 	else
 	{
-		block = (SlabBlock *) malloc(slab->blockSize);
+		block = (SlabBlock *) MemoryPoolAlloc(slab->blockSize);
 
 		if (unlikely(block == NULL))
 			return MemoryContextAllocationFailure(context, size, flags);
@@ -795,7 +796,7 @@ SlabFree(void *pointer)
 #ifdef CLOBBER_FREED_MEMORY
 			wipe_mem(block, slab->blockSize);
 #endif
-			free(block);
+			MemoryPoolFree(block, slab->blockSize);
 			slab->header.mem_allocated -= slab->blockSize;
 		}
 
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 4446e14223d..6571dc9ca1f 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -189,4 +189,13 @@ extern MemoryContext GenerationContextCreate(MemoryContext parent,
 #define SLAB_DEFAULT_BLOCK_SIZE		(8 * 1024)
 #define SLAB_LARGE_BLOCK_SIZE		(8 * 1024 * 1024)
 
+extern void *MemoryPoolAlloc(Size size);
+extern void *MemoryPoolRealloc(void *pointer, Size oldsize, Size size);
+extern void MemoryPoolFree(void *pointer, Size size);
+
+extern void MemoryPoolSetSizeLimit(int64 size);
+extern void MemoryPoolGetSizeAndCounts(int64 *mem_limit,
+									   int64 *mem_allocated, int64 *mem_cached,
+									   int64 *cache_hits, int64 *cache_misses);
+
 #endif							/* MEMUTILS_H */
#27David Rowley
dgrowleyml@gmail.com
In reply to: Tomas Vondra (#26)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 12 Mar 2024 at 23:57, Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

Attached is an updated version of the mempool patch, modifying all the
memory contexts (not just AllocSet), including the bump context. And
then also PDF with results from the two machines, comparing results
without and with the mempool. There's very little impact on small reset
values (128kB, 1MB), but pretty massive improvements on the 8MB test
(where it's a 2x improvement).

I think it would be good to have something like this. I've done some
experiments before with something like this [1]https://github.com/david-rowley/postgres/tree/malloccache. However, mine was
much more trivial.

One thing my version did was get rid of the *context* freelist stuff
in aset. I wondered if we'd need that anymore as, if I understand
correctly, it's just there to stop malloc/free thrashing, which is
what the patch aims to do anyway. Aside from that, it's now a little
weird that aset.c has that but generation.c and slab.c do not.

One thing I found was that in btbeginscan(), we have "so =
(BTScanOpaque) palloc(sizeof(BTScanOpaqueData));", which on this
machine is 27344 bytes and results in a call to AllocSetAllocLarge()
and therefore a malloc(). Ideally, there'd be no malloc() calls in a
standard pgbench run, at least once the rel and cat caches have been
warmed up.

I think there are a few things in your patch that could be improved,
here's a quick review.

1. MemoryPoolEntryIndex() could follow the lead of
AllocSetFreeIndex(), which is quite well-tuned and has no looping. I
think you can get rid of MemoryPoolEntrySize() and just have
MemoryPoolEntryIndex() round up to the next power of 2.

2. The following could use "result = Min(MEMPOOL_MIN_BLOCK,
pg_nextpower2_size_t(size));"

+ * should be very low, though (less than MEMPOOL_SIZES, i.e. 14).
+ */
+ result = MEMPOOL_MIN_BLOCK;
+ while (size > result)
+ result *= 2;

3. "MemoryPoolFree": I wonder if this is a good name for such a
function. Really you want to return it to the pool. "Free" sounds
like you're going to free() it. I went for "Fetch" and "Release"
which I thought was less confusing.

4. MemoryPoolRealloc(), could this just do nothing if the old and new
indexes are the same?

5. It might be good to put a likely() around this:

+ /* only do this once every MEMPOOL_REBALANCE_DISTANCE allocations */
+ if (pool->num_requests < MEMPOOL_REBALANCE_DISTANCE)
+ return;

Otherwise, if that function is inlined then you'll bloat the functions
that inline it for not much good reason. Another approach would be to
have a static inline function which checks and calls a noinline
function that does the work so that the rebalance stuff is never
inlined.

Overall, I wonder if the rebalance stuff might make performance
testing quite tricky. I see:

+/*
+ * How often to rebalance the memory pool buckets (number of allocations).
+ * This is a tradeoff between the pool being adaptive and more overhead.
+ */
+#define MEMPOOL_REBALANCE_DISTANCE 25000

Will TPS take a sudden jump after 25k transactions doing the same
thing? I'm not saying this shouldn't happen, but... benchmarking is
pretty hard already. I wonder if there's something more fine-grained
that can be done which makes the pool adapt faster but not all at
once. (I've not studied your algorithm for the rebalance.)

David

[1]: https://github.com/david-rowley/postgres/tree/malloccache

#28Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: David Rowley (#27)
Re: Add bump memory context type and use it for tuplesorts

On 3/15/24 03:21, David Rowley wrote:

On Tue, 12 Mar 2024 at 23:57, Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

Attached is an updated version of the mempool patch, modifying all the
memory contexts (not just AllocSet), including the bump context. And
then also PDF with results from the two machines, comparing results
without and with the mempool. There's very little impact on small reset
values (128kB, 1MB), but pretty massive improvements on the 8MB test
(where it's a 2x improvement).

I think it would be good to have something like this. I've done some
experiments before with something like this [1]. However, mine was
much more trivial.

Interesting. My thing is a bit more complex because it was not meant to
be a cache initially, but more a way to limit the memory allocated by a
backend (discussed in [1]/messages/by-id/bd57d9a4c219cc1392665fd5fba61dde8027b3da.camel@crunchydata.com), or perhaps even a smaller part of a plan.

I only added the caching after I ran into some bottlenecks [2]/messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com, and
malloc turned out to be a scalability issue.

One thing my version did was get rid of the *context* freelist stuff
in aset. I wondered if we'd need that anymore as, if I understand
correctly, it's just there to stop malloc/free thrashing, which is
what the patch aims to do anyway. Aside from that, it's now a little
weird that aset.c has that but generation.c and slab.c do not.

True. I think the "memory pool" shared by all memory contexts would be a
more principled way to do this - not only it works for all memory
context types, but it's also part of the "regular" cache eviction like
everything else (which the context freelist is not).

One thing I found was that in btbeginscan(), we have "so =
(BTScanOpaque) palloc(sizeof(BTScanOpaqueData));", which on this
machine is 27344 bytes and results in a call to AllocSetAllocLarge()
and therefore a malloc(). Ideally, there'd be no malloc() calls in a
standard pgbench run, at least once the rel and cat caches have been
warmed up.

Right. That's exactly what I found in [2]/messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com, where it's a massive problem
with many partitions and many concurrent connections.

I think there are a few things in your patch that could be improved,
here's a quick review.

1. MemoryPoolEntryIndex() could follow the lead of
AllocSetFreeIndex(), which is quite well-tuned and has no looping. I
think you can get rid of MemoryPoolEntrySize() and just have
MemoryPoolEntryIndex() round up to the next power of 2.

2. The following could use "result = Min(MEMPOOL_MIN_BLOCK,
pg_nextpower2_size_t(size));"

+ * should be very low, though (less than MEMPOOL_SIZES, i.e. 14).
+ */
+ result = MEMPOOL_MIN_BLOCK;
+ while (size > result)
+ result *= 2;

3. "MemoryPoolFree": I wonder if this is a good name for such a
function. Really you want to return it to the pool. "Free" sounds
like you're going to free() it. I went for "Fetch" and "Release"
which I thought was less confusing.

4. MemoryPoolRealloc(), could this just do nothing if the old and new
indexes are the same?

5. It might be good to put a likely() around this:

+ /* only do this once every MEMPOOL_REBALANCE_DISTANCE allocations */
+ if (pool->num_requests < MEMPOOL_REBALANCE_DISTANCE)
+ return;

Otherwise, if that function is inlined then you'll bloat the functions
that inline it for not much good reason. Another approach would be to
have a static inline function which checks and calls a noinline
function that does the work so that the rebalance stuff is never
inlined.

Yes, I agree with all of that. I was a bit lazy when doing the PoC, so I
ignored these things.

Overall, I wonder if the rebalance stuff might make performance
testing quite tricky. I see:

+/*
+ * How often to rebalance the memory pool buckets (number of allocations).
+ * This is a tradeoff between the pool being adaptive and more overhead.
+ */
+#define MEMPOOL_REBALANCE_DISTANCE 25000

Will TPS take a sudden jump after 25k transactions doing the same
thing? I'm not saying this shouldn't happen, but... benchmarking is
pretty hard already. I wonder if there's something more fine-grained
that can be done which makes the pool adapt faster but not all at
once. (I've not studied your algorithm for the rebalance.)

I don't think so, or at least I haven't observed anything like that. My
intent was to make the rebalancing fairly frequent but incremental, with
each increment doing only a tiny amount of work.

It does not do any more malloc/free calls than without the cache - it
may just delay them a bit, and the assumption is the rest of the
rebalancing (walking the size slots and adjusting counters based on
activity since the last run) is super cheap.

So it shouldn't be the case that the rebalancing is so expensive to
cause a measurable drop in throughput, or something like that. I can
imagine spreading it even more (doing it in smaller steps), but on the
other hand the interval must not be too short - we need to do enough
allocations to provide good "heuristics" how to adjust the cache.

BTW it's not directly tied to transactions - it's triggered by block
allocations, and each transaction likely needs many of those.

regards

[1]: /messages/by-id/bd57d9a4c219cc1392665fd5fba61dde8027b3da.camel@crunchydata.com
/messages/by-id/bd57d9a4c219cc1392665fd5fba61dde8027b3da.camel@crunchydata.com

[2]: /messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com
/messages/by-id/510b887e-c0ce-4a0c-a17a-2c6abb8d9a5c@enterprisedb.com

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#29David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#20)
3 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 5 Mar 2024 at 15:42, David Rowley <dgrowleyml@gmail.com> wrote:

The query I ran was:

select chksz,mtype,pg_allocate_memory_test_reset(chksz,
1024*1024,1024*1024*1024, mtype) from (values(8),(16),(32),(64))
sizes(chksz),(values('aset'),('generation'),('slab'),('bump'))
cxt(mtype) order by mtype,chksz;

Andres and I were discussing this patch offlist in the context of
"should we have bump". Andres wonders if it would be better to have a
function such as palloc_nofree() (we didn't actually discuss the
name), which for aset, would forego rounding up to the next power of 2
and not bother checking the freelist and only have a chunk header for
MEMORY_CONTEXT_CHECKING builds. For non-MEMORY_CONTEXT_CHECKING
builds, the chunk header could be set to some other context type such
as one of the unused ones or perhaps a dedicated new one that does
something along the lines of BogusFree() which raises an ERROR if
anything tries to pfree or repalloc it.

An advantage of having this instead of bump would be that it could be
used for things such as the parser, where we make a possibly large
series of small allocations and never free them again.

Andres ask me to run some benchmarks to mock up AllocSetAlloc() to
have it not check the freelist to see how the performance of it
compares to BumpAlloc(). I did this in the attached 2 patches. The
0001 patch just #ifdefs that part of AllocSetAlloc out, however
properly implementing this is more complex as aset.c currently stores
the freelist index in the MemoryChunk rather than the chunk_size. I
did this because it saved having to call AllocSetFreeIndex() in
AllocSetFree() which made a meaningful performance improvement in
pfree(). The 0002 patch effectively reverses that change out so that
the chunk_size is stored. Again, these patches are only intended to
demonstrate the performance and check how it compares to bump.

I'm yet uncertain why, but I find that the first time I run the query
quoted above, the aset results are quite a bit slower than on
subsequent runs. Other context types don't seem to suffer from this.
The previous results I sent in [1]/messages/by-id/CAApHDvr_hGT=kaP0YXbKSNZtbRX+6hUkieCWEn2BULwW1uTr_Q@mail.gmail.com were of the initial run after
starting up the database.

The attached graph shows the number of seconds it takes to allocate a
total of 1GBs of memory in various chunk sizes, resetting the context
after 1MBs has been allocated, so as to keep the test sized so it fits
in CPU caches.

I'm not drawing any particular conclusion from the results aside from
it's not quite as fast as bump. I also have some reservations about
how easy it would be to actually use something like palloc_nofree().
For example heap_form_minimal_tuple() does palloc0(). What if I
wanted to call ExecCopySlotMinimalTuple() and use palloc0_nofree().
Would we need new versions of various functions to give us control
over this?

David

[1]: /messages/by-id/CAApHDvr_hGT=kaP0YXbKSNZtbRX+6hUkieCWEn2BULwW1uTr_Q@mail.gmail.com

Attachments:

0002-Modify-aset.c-to-store-the-chunk-size-in-the-hdr.patch.txttext/plain; charset=US-ASCII; name=0002-Modify-aset.c-to-store-the-chunk-size-in-the-hdr.patch.txtDownload
From 3980d35a11d7f27d18c57586a7f7539d86dafae3 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 26 Mar 2024 00:06:55 +1300
Subject: [PATCH 2/2] Modify aset.c to store the chunk size in the hdr

---
 src/backend/utils/mmgr/aset.c | 51 +++++++++++++----------------------
 1 file changed, 19 insertions(+), 32 deletions(-)

diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index c683e2a928..27006662c4 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -772,7 +772,7 @@ AllocSetAllocLarge(MemoryContext context, Size size, int flags)
  */
 static inline void *
 AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block,
-							Size size, Size chunk_size, int fidx)
+							Size size, Size chunk_size)
 {
 	MemoryChunk *chunk;
 
@@ -785,7 +785,7 @@ AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block,
 	Assert(block->freeptr <= block->endptr);
 
 	/* store the free list index in the value field */
-	MemoryChunkSetHdrMask(chunk, block, fidx, MCTX_ASET_ID);
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_ASET_ID);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 	chunk->requested_size = size;
@@ -817,14 +817,13 @@ AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block,
 pg_noinline
 static void *
 AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
-						  int fidx)
+						  Size chunk_size)
 {
 	AllocSet	set = (AllocSet) context;
 	AllocBlock	block;
 	Size		availspace;
 	Size		blksize;
 	Size		required_size;
-	Size		chunk_size;
 
 	/* due to the keeper block set->blocks should always be valid */
 	Assert(set->blocks != NULL);
@@ -869,7 +868,7 @@ AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
 		availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
 
 		/* store the freelist index in the value field */
-		MemoryChunkSetHdrMask(chunk, block, a_fidx, MCTX_ASET_ID);
+		MemoryChunkSetHdrMask(chunk, block, availchunk, MCTX_ASET_ID);
 #ifdef MEMORY_CONTEXT_CHECKING
 		chunk->requested_size = InvalidAllocSize;	/* mark it free */
 #endif
@@ -892,8 +891,6 @@ AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
 	if (set->nextBlockSize > set->maxBlockSize)
 		set->nextBlockSize = set->maxBlockSize;
 
-	/* Choose the actual chunk size to allocate */
-	chunk_size = GetChunkSizeFromFreeListIdx(fidx);
 	Assert(chunk_size >= size);
 
 	/*
@@ -938,7 +935,7 @@ AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
 		block->next->prev = block;
 	set->blocks = block;
 
-	return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
+	return AllocSetAllocChunkFromBlock(context, block, size, chunk_size);
 }
 
 /*
@@ -969,7 +966,9 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 	AllocSet	set = (AllocSet) context;
 	AllocBlock	block;
 	MemoryChunk *chunk;
+#ifdef ALLOCSET_USE_FREELIST
 	int			fidx;
+#endif
 	Size		chunk_size;
 	Size		availspace;
 
@@ -985,6 +984,7 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 	if (size > set->allocChunkLimit)
 		return AllocSetAllocLarge(context, size, flags);
 
+#ifdef ALLOCSET_USE_FREELIST
 	/*
 	 * Request is small enough to be treated as a chunk.  Look in the
 	 * corresponding free list to see if there is a free chunk we could reuse.
@@ -998,7 +998,6 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 	 * doubling the memory requirements for such allocations.
 	 */
 	fidx = AllocSetFreeIndex(size);
-#ifdef ALLOCSET_USE_FREELIST
 	chunk = set->freelist[fidx];
 	if (chunk != NULL)
 	{
@@ -1007,7 +1006,7 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 		/* Allow access to the chunk header. */
 		VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
 
-		Assert(fidx == MemoryChunkGetValue(chunk));
+		Assert(fidx == AllocSetFreeIndex(MemoryChunkGetValue(chunk)));
 
 		/* pop this chunk off the freelist */
 		VALGRIND_MAKE_MEM_DEFINED(link, sizeof(AllocFreeListLink));
@@ -1039,7 +1038,7 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 	/*
 	 * Choose the actual chunk size to allocate.
 	 */
-	chunk_size = GetChunkSizeFromFreeListIdx(fidx);
+	chunk_size = GetChunkSizeFromFreeListIdx(AllocSetFreeIndex(size));
 	Assert(chunk_size >= size);
 
 	block = set->blocks;
@@ -1050,10 +1049,10 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 	 * chunk into that block.  Else must start a new one.
 	 */
 	if (unlikely(availspace < (chunk_size + ALLOC_CHUNKHDRSZ)))
-		return AllocSetAllocFromNewBlock(context, size, flags, fidx);
+		return AllocSetAllocFromNewBlock(context, size, flags, chunk_size);
 
 	/* There's enough space on the current block, so allocate from that */
-	return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
+	return AllocSetAllocChunkFromBlock(context, block, size, chunk_size);
 }
 
 /*
@@ -1113,6 +1112,7 @@ AllocSetFree(void *pointer)
 		AllocBlock	block = MemoryChunkGetBlock(chunk);
 		int			fidx;
 		AllocFreeListLink *link;
+		Size		chunk_size;
 
 		/*
 		 * In this path, for speed reasons we just Assert that the referenced
@@ -1123,13 +1123,14 @@ AllocSetFree(void *pointer)
 		Assert(AllocBlockIsValid(block));
 		set = block->aset;
 
-		fidx = MemoryChunkGetValue(chunk);
+		chunk_size = MemoryChunkGetValue(chunk);
+		fidx = AllocSetFreeIndex(chunk_size);
 		Assert(FreeListIdxIsValid(fidx));
 		link = GetFreeListLink(chunk);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 		/* Test for someone scribbling on unused space in chunk */
-		if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx))
+		if (chunk->requested_size < chunk_size)
 			if (!sentinel_ok(pointer, chunk->requested_size))
 				elog(WARNING, "detected write past chunk end in %s %p",
 					 set->header.name, chunk);
@@ -1174,7 +1175,6 @@ AllocSetRealloc(void *pointer, Size size, int flags)
 	AllocSet	set;
 	MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
 	Size		oldchksize;
-	int			fidx;
 
 	/* Allow access to the chunk header. */
 	VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
@@ -1313,9 +1313,7 @@ AllocSetRealloc(void *pointer, Size size, int flags)
 	Assert(AllocBlockIsValid(block));
 	set = block->aset;
 
-	fidx = MemoryChunkGetValue(chunk);
-	Assert(FreeListIdxIsValid(fidx));
-	oldchksize = GetChunkSizeFromFreeListIdx(fidx);
+	oldchksize = MemoryChunkGetValue(chunk);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 	/* Test for someone scribbling on unused space in chunk */
@@ -1464,7 +1462,6 @@ Size
 AllocSetGetChunkSpace(void *pointer)
 {
 	MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
-	int			fidx;
 
 	/* Allow access to the chunk header. */
 	VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
@@ -1481,13 +1478,10 @@ AllocSetGetChunkSpace(void *pointer)
 		return block->endptr - (char *) chunk;
 	}
 
-	fidx = MemoryChunkGetValue(chunk);
-	Assert(FreeListIdxIsValid(fidx));
-
 	/* Disallow access to the chunk header. */
 	VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
 
-	return GetChunkSizeFromFreeListIdx(fidx) + ALLOC_CHUNKHDRSZ;
+	return MemoryChunkGetValue(chunk) + ALLOC_CHUNKHDRSZ;
 }
 
 /*
@@ -1554,7 +1548,6 @@ AllocSetStats(MemoryContext context,
 
 			/* Allow access to the chunk header. */
 			VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
-			Assert(MemoryChunkGetValue(chunk) == fidx);
 			VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
 
 			freechunks++;
@@ -1665,13 +1658,7 @@ AllocSetCheck(MemoryContext context)
 			}
 			else
 			{
-				int			fidx = MemoryChunkGetValue(chunk);
-
-				if (!FreeListIdxIsValid(fidx))
-					elog(WARNING, "problem in alloc set %s: bad chunk size for chunk %p in block %p",
-						 name, chunk, block);
-
-				chsize = GetChunkSizeFromFreeListIdx(fidx); /* aligned chunk size */
+				chsize = MemoryChunkGetValue(chunk); /* aligned chunk size */
 
 				/*
 				 * Check the stored block offset correctly references this
-- 
2.40.1.windows.1

alloc_performance_aset_without_checking_freelist.pngimage/png; name=alloc_performance_aset_without_checking_freelist.pngDownload
�PNG


IHDR�`��gsRGB���gAMA���a	pHYs�����e��IDATx^��|u���g���E��
��x���@�b���)��������)*�;���EEO,*������BB�?�_�&��f��f����zvg�������g���W0�TN�?Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���"���@�����v��w�/��I�m���N8��5k�=��#nX��-��3��n����h�^{�5{��W��:����kk���{����_�<`�V�r�O:�$�j���c���|�^��F�i�}�Y����JKKm���������sm��VXX�j��U+��6�|s�a�l�������s	{�1�9sf��f���m��Fm���w��VPP��=���L�b��������VVVf�H���ic�m��u�Q����b�h|�,Yb����9s���e��0����4h��/�R]����+o���~��n[XK�.�����.\��xK��\���<��5�[o��{��@3�`�+��b�|�I0�������_|�E��~�~�������{���e�
�E���{��7���n�o����M}�Df��m�?��]}����o����A�������=�~����qW�������An�i�������NM�M�7o�=���U'��x�j{��~������;���S�V������_� ����������Uu��7������g���y_~���5����{��\�H�@3����������	��d�R���)e����s���N������z{���k}����'�x������t�r�t+(_�io��:`�AN<����|���6b�{��Gk�(#����{�������`([Sl/��OS`������+WC�S�����u<u}WO}Ve����gJ�h������I������$@]�^Y!x�Mnn�������j]��O;F]�#-Z����N;����O���4��\*;D���OJ$����	�w(�th���w������:=Z�u���9h��1�6B���
�X��������\V�D�^����%������������W��:��c����
0����}���_�~��{wW:DA�.Epj�[l�E���L���=n���p�nk��?��~OAt x:H����u��%�8i_?f���v������Gi{���m������,��(�}��=R�6������>��=V_����y���d�K6��~�J�M�<9xf��cG<x�r�!n_������Sh���6��w-**�i���L�P�U����K��T�$�I�xm/|��L�f�
6p�;]5>q���>�����>7Pdt�Ah���������i�i�� v����T�J��������.��64
�*��o�
���2Y�1�4=�T/���.��C�������+��?�����G����w�
hu	���c�������-��2����)~�g�rv�e����2��qc���A��A��,n�S����@���6|J1�oC'��_�>^�������i�*~"u-_��3>����6���(~�)��W\��~�`�J�,Z�(x��n�����Ms�9��O?��^���4��Q�m����R_Y,C�qpO��_}�U�,utPs�AY�=�!����
I��i�Mu�Ke���
�}��.�K�����_	
X����{������U���
�@�d���%��I��3����Z�BY��k�/=�N��NT�D����e�
��l���b{��w����
��!��7�e�������}{�d�M�g�%EjS�]Afx�w����I}m�a����/� �����>h�<�LUmB�����rA����w����F�n��V�:���>|��:����*�>H
��@f���M�C�IJz���d����r��kCe���2�?����4?�r}.��N���3�c��y��I����6�<��{�����2�D5UO8��X;��������n� ����^�z�Kj�/Y��IuPuFV;�F:�����{owy��m�`QMX]r�_��
���nk;�������4����l��y���t���O�yvY��K�>���=����������o����k���!�<T���s��{� �aV���u�o�����
��C�i��)����[��U+7o5�j��["���7:���[o����h>h���/��.�<��T,3�O]"�q�
b�2�|�g�5/���\�
��n4���C���^{m�q���K4����k{���:�'�t�*���my����Emtc�_m�E�k����i���e���q��tCCM24��fh\���f�]��U������:��R��D�SwI�������Lh�)�}VA�t�����o���v���������D�������A����c������G���t����%�^��~���v�z�j��T��#��I�u�&�����?�/'Nt�X-k-��r�X�i�'P)?����Z��Ov]��������������M��/	�#5���������[:�����]gS�o��?��z���\��=���^�r�'�x�j�rAGqD���G����5�D�4��kyh���^�|���z�j3�J�z;���>��7�o�O���m������j�`H2�����7�g��5��V������'� �:�����������;�WZ����<���v3�����M���xh���K�@�Q��=���n�Of�I��.�������I~�������<Zx>&�h�o�j3�4�i��*L������i=�~�g����~������,�^t\��������#M����H����$�>����>�l0$�t/3��w�~f����j^h[���V:���v�}��U�i}N��}�p'����T��tk�N<�D7<�n�[�u:��7ep��_����h|5�@}�^�;��:	���v���kz2w\�X;>�� �����:�t����*P���
7��
���?��OW?�d�v��/v���� B��v(�������{�=�������/_�v���]�W��6&���R����Q'D�������Y���R�B�����Y�4�t�� �:���a����Vcu��9rd���+Q�[����o�N�\���O�c
S�_Z��:B���y������?�$�c��A����r�w+��Nn�NV"�Yf���������h�D/3�jY����3�E�I%����������WuF��C��5\��N"i:�I�5����z��C�� B�mK@Q������^�L5L_�]�]QU�N��oO����}����S��7<�:��vF��0}�Ni���3���E�Ny}���_�s��Qn�5
�]O��u&]m*<��E���h��M�u2w���T �o�����X]f�tSE���2�v&�v �������y�����k��R6�S�~���5��N�$z��;�v�������j�vZ���N�G�?�'O���2��>Cm$�v]��P�!���m��[�W�B�� ���>�_������[�Cm;�^k
��z��?��j�����M����.]��iT�I�lt�]�8��]�~Q�f��An��V���~^��>�_�'��C�o��m:��
�{���]���>������+�����7o���o<Z_�w�x&��o'���O�1@�2Ku_N����
��~��������62<����u��':4z�h�O����tMW�>B�t���c1mc�}���o_~�i^���k"��+
dj�t�*z�����1�u�'��'��/����~G��X�}����I��2��6�>�����W��m��=}���=��V���X�C����6�bNm�&�^f�mXx^+����3�*�����?j�u�_�'U}W�1�kzE�S�\����]�U�1�����N��WX�9�n�[�u:��7��I���{��������&H����{�e�D���t����6�������b����;�h�=e�=���	���8�������64M����'�Lj|=�*��l��`H��������h�l�Dcph^������������s���/�6Q?/��~}���������'{7o�����r�7U�1���:����h����:M7�O�VPg9����h]T��i]�|��Lf�k��m�SO=s<�6���O�����`������+��Mfyj����SI�c�9��������D��dh[��Oa��`���������I���n:h��Z���Mk�����	��{�~H���l
x�]����Q{R�5�x�{����N����0��
���P����&�W�q�	�TL����XA)�^�~���m���������|=��M����v�I��C�L_����nk�>-#��5e����#F�H�����u�����{]W;If�+�o:�� ^��������h~i�j]���0��:q��D���1�[�����{������
m74=�._���M*�Y,Z&J���h�����k,����/Q[P?Ht%Rm���q�LtRB��/J��Qi��%:����v6���_U���Q�����w����9��]����j�0n�8wV�w����%k:�M�
���w(�O���w4:�SIg��.:`R6sMgfk��pM�<����x�TgLgvuy�v���*���HeK�;��.�
/e%D_��u���k>��|e���q��f��k\��j--O��}�������x���o7��y�]b�6����V�	/3e��v�i	3f�Q�e��~�D�V����E�����L�
�5�4�j�����A�i�j]S���r���z�u-��+�uH�-,��PS���+��uQ������~k�V�yx~&Z����]7�/������@;�>(���Eo34nZ�j{�T����i�>e���j�4�b���M�RGWt�;��Mi��h��v@���@��TM4u��S�
e�����'��.]m(�-]�e�Kh{�+����o������6�.Y��^��i�8�r�]w��mK�&�~(���Z�j�:��c�H�vJ�St��6(�Km+z�z?�?�����m����[�E������_����g������o	o�=�������-[--Omg�%H�/8���~3���Ye�k��>]�]�v^����;>�����m>kO������V�Z|�W)	mG�^�.���~6��h���6#��^��qm���mc�2
���/���SO=U�O��k;�y�����=D�/%(4�j[j;��ktSe�C��MY�*���5���h[���G�D���m/�@�����6��o�I��U�V��7�G����^V�_���w��c)��"-mGU�S��j�^�QP>��K��!�>���e�M�T���Cq���Oju\�+�R���EO�?�}_�N����/�<�r�r�r�����xm/����j����4��������@mQ���6�����^���e������v|�3��:���k�G�4Ig��A�vr���{O9����B].���~Ct�W�3���*�C��:v
�i����7M�:�
(h�34��m�V~�;���Xg�5��^S�_�NH���������_�!u
t��i^��l*�<�]����s�M�[��QG�������:c�q�A�:D�=u�}AA�7������0iz�)�A���h��l4����g�k��X�m9�d��:L:��M�.;������r�����Mk���k���PgF����<t���g:��8��nZ�j�z]z�D���C}�(����myZ�:8���y����@��5����i]V��r����H��u8L�OB�-���vFA�������jKZ_��-7u05���A����?��n�����W�1����o�4}�F�
t'�������[��qW`@WW�����]��<������t�M��������j�����5Z��Z���6��>��*������t�CW<(C�o�4�u��6�
�Z?$�vW��^������8O���Gm���|����LmS����XZ�4��>����V�v�!�C�f�z��n��+Z���>[�@���=U�Z�����������Re���P-o-��t�[��lQ	M���/7}���\�Y�?��~�����d�Dm@�9�r'����h~j>h��q��F�����tlG�fW[�oi_������K4N~� ~���U����l��qm���m��'��4$s�����Z/|V���N�(`�V���6�����$��dhz���~O�=���V��E������i�u]�
wx[�q��C�3e�k&Z���m�$�^4~:����.�(S�����Cx�Q�K�-7mo4Z/��0����cz������/����W��W@5VM���]	�3�5������r�i�p�W���}U-�s��O�e�y��ez�Eo�D'r��{D�Im#�;�P����e]�D��:���?9���p?�����~��O��Nhj�G����������m����?�-��g�\E�%�-R��%h���SGT;�h:@�=L;L�P��������g����w���)�w��9���7��:@���:H���C��@R�aO�������LN��U��,�puf�g:��L� �~CAU��A����5��qQGA�V�)���Y�u�]w
�U~��5u8������������~-[O(> PZ����]�F�q�;:8���g�����Z�~����=�X�I�G�]��S�v�Z����E�3:T�I�c��^>:��@��+�I�eg%�l�=Z�������:��������7M���?]j����1�ngZ���M������B:������x�+Pt��Am/��	���4]
p�5�~h�����M�0mw��i*[2��D%��/���Pb�$����'���e�@k<Z^
{�>�r=W�G��D���>�o�����_�4�R@S�[����^C��LJU_N�4>��\p6��-�TR9|�o7��WD�3��������\�u,� k���~'�����O8���U>0�e������������<M[�����+��W�(�ot���`m�u��!i���h[�q�7��u[I��>i�!�s���j^�u#i������E���E�N��Q�MU_2Y���}
�*���i�(��QgLg����!^�Y�9�4J;�D�/:�����������:xSGE��������<�A�L5�AD�r>u4t������}�������Z'��Z>>#1;j:@��p'�*�M����W|*S&���N���>���QI8XZ��:3��E�%:�����u��3������)����r�	Z�U�\�����k�������
��)��E�l@�,2����)�l��u���Rt���M�.��wp����]mR�@���+�i]�9t����tih:(���;�te|�^}�s�h(�F���mg�'���&�x�iL�!�U�� m��|��6#��5��E�������h�}��*30:h�)P��hZ\J%m��'�!��J���?����]v���ost���5d;��T������CC��^'R!|���I�k
�i{������4h|��o�~dM�b����C�I���2����A���h��l}�J�r�I'O�m����
	])���7�}J���e��:����N�\��/��=k�^f��oS�*�M����_����M���RI����^����n4[��t�P�p�K��l����y8��d�O�i����t+������^z�;(�%�������$+��L������@��%��4��v����I���Q�� #��>k�����3�:D��j���vbu�G����?�������k���RM���nu����?��U�u�_���1�`Z<
�_x��.���M~�+�����.���<�$�������E�L���"|���'�Z�M��;��G��x�v������V)�vR���W��k�hH
�j�J$h�8'��E�'�D\C��j:W�"�����`RX8KXr��y^6�h��Of��J����	H�c�kK�Fx;K:��Z>���Q�,��O�B���j�v�I���i=	���I�l�0���3]E}�d����o�����ub������Ie�Z��/��.�V�W|�P|��Xt��+���S'�T��_��n��'J�J���������	���������P�L��DIk�B���U:Qky).��B�^�B�e�����?��SiVm[�W4i[�8@�$���F���X2Ra�}4�;��:V�_�?�l"|`�P��R�G�8�������@&C��~��A����6bM��O�_��c�?-O�M�)��x��3�g:�O6���x��_�"]�a:����:0�MO��<]��E��Np��+�����
d����&����De�&4��)���0���:yk]�����K\���uK������;�
�������:��O������_|q�����O?�t���t�
I����j;�L�*����3��Q��VC�[���
bh]��M�Y_�:L�2�u�L*$3����*.���Q�
���[���/w'�TF$Q�
k�v�IZ����i���%�	�����|�;�m����!��T-����>�[Z�b���?������'Q�F����8+������'�#�5��GZ�U#;r�i[�������b�xT��o��)A�&�=]�����v�qG���?��%���_Z�O=�T���@]�F��N��R*�'�<f��i:����=���v�m��UW]�2Q����;�k��l�O��D�:X��s����0oSA�p�����3kR���e�e����	���~�u���a�\6�.Y��y*�c"��Q�3��8Q8���y�^4��L������Z�
h���7����:���~�[gt�_]2�b��E��u�qJU�J�X�S�Q20�������R��U�j@w�g,�d@M��&����!i���>������[Tu�4���	��:q�����qJ�vT��7�U���>e�+���k��+�����W�5d;�vZ/���:Y�me*���}}u�u]O����M[���_7��gQ����5�v<M���T?\������m/tlw�5��$��I�o�j
����}���'|U�h���ij��e���W���	Om1|?�.tY*���t��M#G�t�A��=z��C�J����Mt��j��3�:��;�7d����mc��`};��L����(+����n���&�`��'D#�# �i�J
L�1��ui��:q��;�e���������2�c��6t�l�l�����������=MCc9H�H^S��b��)������/�`n���2��-��X���*����Ns�e�i�k��Z�JNH�%���?��r�W"Hm��h7���T����������Q��e7����a������y��'��w�u'����b@8�e�T�T�D������l}�����_�!m�D�n���8��@7�!����Or��u��%?
E�: �Y�x���,��x�.e:����:��i��^;;e$K���n�5���S�Es���6Z��#_x��=�kFS6�~C7�9��#]}�T��Sx=���qVV��TR����_�;����N������2"�E|���������Zo��S�^���F�*��t��BkC���������l�x�Gs�~��T�[�����Om>\�D�j�*3��F'���SM��U?L�����F����MYs��(�M;N��^m����D}h����A:��tP�)V�M��/�K�K����J��p==��T���PA�J�$3^_AWF�D��	�+��Oo�����D�K�eKTFG�0���������i �
4�����(��������Z�G�@){H�'���?�x��pA��Hj���V}V��o���^M%L��m�d����Y����vRU�]�w��	_&������)���.p�7�x�;����i��j�
U��>�j�L�=��p��T�Oem�L�*h� �:�g�u����
7��J�}����G�t8<���1�o��j�23���lVwcQ������~4uMy?�X�-����p��N��y�>�o�Z�ksc�Tj�����t�A�F�J������AG������*���)0�B�M�2U�Dty��j��1�����0|���R=��-R��^N�����N+	%��z������(�>����^z������~���\fMM��%�����v���	Z���H�@�A��Q�3��)�7�x�* �q�o�������ST�O��&
L�3���V8��@���}&�T��� "\P��L]��H�p��.�N�`:�Mj��5G��p^�Ye����2�6�`���i/[9��7rS��6fzo�\O����0�Q��T�=�|���������\�>���7�|��2u-�N8�.��B��l����������h��� z��������=��.�MW *��K����5��Pc�����V�T�Fe��������,�:���vT}4��N,�`JX������?�<-�p�/�~��d��N}�m��j���6m�K:P��j3������T���3�u��td�*#U}9��q����^t�peS��4$���.�2k
h��.{[�����w�n
���"��X�K��W��hy�o"�	�w7�$	��n�)��<�Hci��NP�v�j���k����\���u�Z���S��U���l����s.:�x���3�!�$uL����)a������{���^����q����}3�XT��!:�:�����|��GI�U_��)�����wl	��4����}F�e�G T�VY5�'��@7��M��$Z�<}w�4@S��:����:��P'_�+��O�,��k+]�GS���C�e���$���{t0�?����my����}i;�<_}����nM4�Ufwj^y���� ��D��^%����d��5Q �J���l�������>�~Gc_��K�^f:�I�����(9���4_�Vy��O���1c�4����������
�d��_|<jX
����p��������A(i&�����p����a!�
4 e�)X�iG=y���o��vU��;���A:�pV�v�����(S @��)���|�T	�X<e�;A
�$sI������sYN�_���_*������l��'����|P�p�r�^�z%��c	�u���@��U�Z���`t8��%sS.e6�F^�:b���(k0<��L�R�:>o��j���={���|V��5��~�G��MMS8�1a������e����X]"����TS���A�\{M�J�h�S��P�++�!�W�Z��V�Ft�RW�\?����j,��x�D�)���:��m��f:c-]�QM������p�:Z�}4���i���)�m�N�$���L��.�D��+���;5fa��z�������������K�?����1���9A����������u��XW�))'\�D5����X�����z�e�mR2}t������!�YS�u��V���{%�����M����m�O����Pj�@7���������p?���8e�)��������=|�i*���j|���2N�a�G���u� ,����vt����U^
�K���Wg��>�	��Rmqe���q|���\`F������2�x�t��'|BE��X��S�O��S�C*/cS�k]���k�����c���#�<R-(�t�����@N��O<��;��G���{�*�@mMeX�/����j_Z/��j�Zg�������C�~��O>Ixi����j�u��h=����N6)sP��h;�K����@��S����q�A��{�]����]w���%�)x1j�(��Aa��������e���Wm�s�h���~�1�[�M���G�1��
��)���j
_����2Q�G��y��]8��!���H���d������Oh]��I��+M���:��J�
'pW�n�rjW�w�����w��=����'#Q{�I��Z^�������z��x�|��W�b�W�j���%~^�O����:�����&j�QX�eP�m��G	J��C��z�������2k����L�4����:��)��6�i|3��@�@�h`o�q���:�����������������>ZuI�>��p
������9u�H���:<:���z�$�����%�z�:��4W6Ot�C������W�R���x%L���:�������N�:���q��l����/W��� W���If���������pe�(X���_>�_�AYW: ��G�A���sc�pv������w�]�w��u�DmFA�X��:����������#�����j�*s�����v�uU�0Z��W�R�3z�2�Zo�U
j�{��W����p�~_�Vo��8*���[nq���-�|L�M�=���3BDY����4������BY�����qUgz��w�5N����_��gv���}���n������������V�u�e\j�DoD�Mvk�����65����w�a7�tS�uG�J�����5]�GS���C�a��H�Jm�tr^t�%���)���*��rB�F�����p_M�M�t�>��m���F��JW;�<g�j��L>mGD���?�K�l����ml��=�]�}���J�(������R�G� �C?�RE�U�'|�
��Z>~^iy�
h��o�M���}]�'����9�rH�~���[o�����hz�>���o�vBYeH��!�7��}@X�EA���$-'�Z�D���M��V�+�y}g����EmD���f�
��:���e�\h��2��H�)������6��\����?����{]�����v�$�L��4D*U���@�hg�����y��.&
~*�M�}V;=�5e��.���Mt ��T�<O:��j��E�NY����.O����i'�]�V�UP�=���e�;=u��:�������>��QU�������Y|uB��RD�����������&u�:��j��9���6�)������O/��-�-�D���Hv�y
������d�T�A�����6��S��d���������%�^&���^t�)HH���iO�lUu��~���.��[���~2�~(��/M�7j��4�5.>,Y�SO=5�W�	�P��*���������:��M���(��E���G*��������d�e���O�H��n��������Nr��i��h����vY��~��
������u��K��K�vT��|����E�t��c���HG;�Tp;���)��R-3Y%>��E���n����,�t�����If{������y�K�%Z�j�.mk������:*��V�e]Ov��l{�j�O�tsW�C�>NW�*P���Ni��������|?M�J�n��j�OKF�o'C�K���������,�L/���R����l�A�MP�F8!A'Q�8��j��a��y����m��� ��ux�<�H�!��ND�Sgi���O����O����I���:��VM�	R�Nw��w�i���uu>�]��S��(u�p�i���W8k(M�>��o�����"�e���^��p6|"z�������������:��\"�����:\��z�ARm�kC�E7\$�^��-�g� �h]��������=��C�<�^��?�����V�{t��\����.���h�mS

�w�y�DV2mD��{��u��q}���G��gmh��>�|��6t�P�qO���:�M�>B��~4eMy?$����DA�p@E��nl��u"���~S/��H����zEA��X���w�u��$)��s��)�5�8��1����*@���5�+-C��c���R������G�$]A��F��Rc^�c�m{Q?M����i�i�t�$z��N�� ���D'�5\��R�s�|��0j�OK�~����v�+���6�i>��c�mct�&�8��2�������vc�w���|:��An�B����@������i���f�=:C�fI���,GmP�m�({BgJ���/1���D�9��L��u��E�`��9��>��P�u�����_�q��S�q��(�DY��,�KE�S�@�u�u_Y>����+�@.�,�����t��?��lT��p��6�C����J6������>��o���-M�:���~9�����buy�:���^�%��-�-(P�#W�.3OmH�UuA�������c?�9-;���2S}��5�������g%��������Q������m�h|u���zY����L�^����t@���q�w�MR�Z��4=~W[�t�n��'��:���o�c?u��������?-?�����[���]�W�+�R�6�����_��MS�6A�%uX��k�N���.����D��dh�T
A'lty��M���/G�4kY���2��?�}F�!�2
���������)�����Z�R)�Gm�������m� �/�D��y���Pm�A��OmK52�[��z�R�e����/�� �S S�
�K� ,�Q-�7�o�<���2��j�����5���n���Pf������.���6�i>��7U���!D�����!����������j^i��j�����?��h}o�kZ�jC�Q�?�7�����J�������5�����'��H���i|�M������������g�y��1�<�~Y�%�����%�x�4��M�W�~Z2���������o�$�\��Q�^��W�W��������6�8S��S�Je��.�:����>���m��O~��fJ��.��������J�PR����e
��Kd=�(U) �\�%���@CRPO%FT"KTBFW��t5��	����@��LTTY�@:���Q�n@���&S�A���R��O��� �
 ��Fe>��z�*=��N������~�n��r5�
>U�BT�$|oP?�d5��M�FQ�Pu��un�tP�do�;f���U5pcQ��1c�Te~+��@7�C�nYC5��y�w�=��J��i����2��:�,wS2 �t�������F�
z�o���n��$�y��4�2e�;�����	��y 5t��7������O>���b�`�^*����OW��� �.������ u(] k�$����)�{�����W�`�~�n������;CS�=���l����H12�d����^~�eW:BY�l��+��G����@CR���>�����7o��1�?�n���u���v�qG������ �t��KY�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t�Z��B���6kA�}0�,x��Dlp��������V���f�������k_���=x��|�L+����YjDr�Y����}A���v�}��W������������nt��7�������g��[�<���������R�e��V������Z���,��f�3 ��_�������V�C?��~b�Hl�'��]3
���	���=%xP{�o������R��OKl����34����� ���l�H�l�=���+�e���D[Dr��|�(��t��X�2xP{�MBY]�T\\l/���s�1���Z�^���3���'ZII��/�����g;��l��%���+++���z�^}��`R"����3���sO{����!���x��'�v���v������U��w�L����~��������5�>���`H������\��~�a;��#]��y���/����=�����mc�{�=�>�?����6������k��E������iJ����o*�o�'�q�����M��L�d������:�,X`>�`���4U�h@
]y����?��f����9��������v���z�{Oc��K/���k��/��1������k���?O��n�
���=�]�{��v��W7��x�������'+*JO�"������/��.���l�u�]m������;�0{���~s2O��\�?�x��:�{���O��LRp[�9a��G����������{�wEE}��7�M�'�|�y#�
@z��l���6`��!��C�)�L�����?.{�1iL&���Wt�x��7���i�*Pm��#�pm��E�K�O�������RB��R���K'�N>�d{��w�����_|�m�6�l3�������?>Qi��I.[��}c���������+���p����N�>}z���Sp���x�j����#F�h�'y��G��n��'O��2�
��{����={��g���� %�����n��w���_m���N�Jl��^AHe�6��IO�R�S����K[DR���o����m��vv�9�X��m��H$b�n��+��7���o�������]V��{C�q���%''���{o;������~r��.#���)�[�r�k������#�
@QPGu]�l��}0t���_��u�f?��C�km��O<at��%���
�4{���.�W�J��]�x�����f�r�T�;�<����wG�a����?=�0�M���������t���;
���~������,���n�����nw�DY���zj�jlS�Nu������;C�P���>��/��


���QPR�ST&@mA�����%�<��C���5�uH����[nq�z����(C3L��]w����^���s���-�W:��7E��l��F��_?���K0t�6m���U�C�B�g�-[f;��������|���c-��d���E]��3������_-�K.�$�vW�L���?�i�>��m���.��?=���U7������w��}V������v5��F�D@��(����M�C���>���l�v^�m���Wu_�-k-����_�u�cm�4����������.��G}�z����{v���j�?�n�~;�:���8��nH�V�\ H��~��a��U��u�����)����v���@����Q�\�3V�\Qp���.{R����Q\�
Vm��.�#�_�U�Az��8\u���vA
�k��y]b}����QG��D
n�;���ovZ��E��])�����>+6e0j+�q�m�	����������E)U�B�[��_b����.��ua���nX4
��
�����.��?����k7>x*
����2�����5���<+��k������3
�h}R&�w�}�5�j���	mwZ�h����k�k���Zk-w_��/�h�6�����r��;�z�����'�|������iP[T��u�M�o�aQ[�����T�6V%U������<�~}���
&*0.j����������W�F��������m�6R'at�`wrR�}��nU.���O�<0Z7Z�:�}�����1G-k-s�^�n��f�l�����:��a~�������>�M�}�������m�Nj���B�n�i�6N��N"�����|�U�U[��Q���:��@cA����eK���R�*0���T�d����5XG�Q0��O?uZ�k�K�U@�]
�(���$;������@ZeX����s$+L��j���_��
�(�M����2���}����x�*��/)W���Y��w�����^��E�u��v�l{]^����O�;eT��J��]�C_��������^-(�,��M��������A���^���\�Q�FA��LZhTWWmUY�*7����~�{��+�p�F�H4
�"���������A��@���oo7�x���)����'��h5L�@�!�:u�y�+TtKWhyi��,m�m�5Q[O������EZ����j
f����~��.:�����{�+&4
j|���j����W�����6����M�I��@mwxW�S������������m����}��aeM����vZ�	��R YmP7�T[�6FY���@'����U�6����u����^S���B�������}�d�����=�#�)�$��y���U��lv�A���*D����z�=���^��%=����c��y
@cE����@~��e�:���2����`�tk��.p�>��S�N�@Z��:��Y[�-��`Y�a?���;�U`E��(P�b��2�����N�����<��]�S�Q��J�x
�}��.X���o��.=WpM�z
x({O�H�������N:�e����=e�*S��|}�������'�;�^�u��pP EA ���]�C����M����}�_��Lceg�*	���J]�� ��'-z�@�����kFqs���w��:!�R57�t������;S�N'I�A��Xm�$k��m��.�k~+��1Zt�EAB-w� �n�:!�m�N�t�����6Q�?t�P�{~���u�F�E}�N����m{5�����/[l��+w��t5���M�otuRA�I�������]R;��$�*��+�Z�%
�txz�lk�j�������UP�o�E����t�]�N�G���.j^���+����.:��i�=!h�t��8{��G\@�_��/=�2e���S����A��UDS Y�
�(�[��K�u �����������/�|��X����LI���z�z����4h|U�Bf��9RK���������^�����*���2��MF�Nzl�����5T�FAk�Q��#Q�O�C��0\T��)����@������20�����w��}p[��Z�����~���������M�L�7��LP��.���������g��Uk9��
S�N�[��f�vDP�5m�����>�����
t�D��G}�JK�w|�0L�D���������j�����O����j�:Q���g��+>4\'��r�9�uCt`�]�vUAm�;OmG�J�t��'�5^j�5���/m����ok����^���Q���\�K�J?����k{��y@:����V��a�\�����%�:p�e���.k��4KD������,zMt����c�@Z���~��|��	��}=�g�y�e���S����W@Ie�+�L���������FW��@��,T�]����Z��*U� R�}�T�!V���'�(TY_3^����~G�D��x�I��dn�b�
WK[�W��X�Pewt�%���4�U*�s�G��s�O��p�u����o����tr����s��x�U�m��
@��u����]���h
��^���i��@v�I������d�,P�U�Z���k^xa����Rv��}��R�*����ub@�*���Bm����M'`T�C�^����,�������gYk���7�m�d���j���_
�k����5�W���.����������L�~����m�~����$����L �
@Q�e)��[�E�3E|Q�]��������R���a���b�@ZAx�wS�0����`T�0��^S����k�:�
j*��r����+� c�(������A�0	U�V��pVaM�e����6���3iuRD�Q����
)�S�k���}
��i�S�R���:1�����Yg����?�k�����6P'�bQ�M'�t3P}F�-P�~m�o^�.���L�>���
�j[k=��6���-)H�-jx�U;ZtTmN�Q�z��������5`���Y]�oZ�
�jk?����vX�������UW��*`���v����O&�-�j������I��R�]M�}�j�t�An�tR�{h3�4��>������.eVpO�s��	��LY�5��Ae���ieX+��K�ce�G���r{��.����O�O�:�V�]��nv��;x�Ty?��zZ�
"�_���>��`�VSF�P�����@f8�Vm@�*=��MYUG��r*=����8��9�)Pi�|�A�����
�R�65<��$��M��S]ke��t��n�K�[�e�bQ�j�-�jF�Ie��lne'��fR	m�b�?�7n��j'�����ae����
������5M��}��^-km��o�p�
���D�����ky$K������a����i_����6H�R�6�t��]�)�/�^�������o���Pi�0{��n;�N:����:)���4V�h��e��PP&���:������Dm:pUPQe�e�a���t�/�`�k��_�~�5��������
6�f`Ht�S	�#S{�k����(��-(�Z��
�t��1ZI�������8)����d(X�����eIjK�(uC��U�
+�	�M��L��>��� ���.��r*���N4���d�<���~K�T7������|����������4�����v�n���E�-�s
����$�OmSA���j���}��\��X����Uz���`O�$�S4�e]���$���J+i���%�D�N��l���*�R[����n����>���X�� �t�@�
�t��l�2�8L�t0+�	�(�+�D�(�����w���C+)p��f�M����:��D�}�������^u ��W)����/�a����s�,��}�����2E5�u�z�����v���i'K�-V[P`DmMA���
�����*�I����B����s4�u�g��N���a���tFS4��)XC7���DmJ'6��]�}h��3�T9��c��+;T�6m.�X�Y��1�6�@bt�R�nm/��5�
�*�;��=oe��
���N�h��V����]Mo�U��s���|�	m�
��n�7m����>wuQ���?�}Nu��\�4Y
f��rt[����S�]�^��v���MTf��e�'�E��Of��G�I�#F��A���pY�����������$*����46�h 
��F�n������k�^S-L����.�V0�L����� Y���
���V�b�A����T��/�����
*\���9�}�Q{����A����Q*���{�q����) �LZ�0	��z
��&cjow�uWU�M�C�(|���H��t�����S��?�����{t�s�=U�X7�������z�>����::�����A���1��R@K'S�=��:��`�.��4�
,M�8��9��#6�)m�T� zh��m�����v��mj�
r��y*���^*����t�M%Q�Vt���6�6��,Z��S�+���G���k�,�N�i�+t�a0T���O����y�*�,�:�m���#�e���:)�uC'?}9
$���[]i��$�/a�}����'��[m���v���6�?��D���M����vN���P���}��_t2P�J�h���vN�l]�����?�2��'
5�j��+p��4&��^���d��F������~��SZZ�
�/��2K�����>�[����%��w�}����:u��.�V���.W}�X�#]�z�:�V�G���>����Ot�����@���1U�X��
��`����dZ���Y��� ���:���U=U��
��PM��N:�]N�I�/����o��^���^<K-]b�`���>1���2iU2��E-�TW]7�Kf9(r�y�����^����O���vX��i\�����Ni�uC8�;~|�;����S��j<k�P�q�M6q�B��Q�FA�k���E����*��;�W�R�������n|�B��c���������	�Y���Y�����6 *K�y�k(�m��W5�6H�+��8�m���sU-a�-o�\u2&^��W_}�N;�4�X��u �}�&��@��q
�i���uh� ��-m���~�o���3�tW�� ���*��������~;�A�]w���L����l����Y�M<�ak�����Tn�|����p��D��tc���B���I<���~�*�Z��x'�tRF����������U7u���������$��o����m�~[����~?|�g��1�m�=~;�R�O����V�<+��d�Wf���e�;w{��y�n��_��:����:V]b����s�9�.��r�U�}Nf�*���e�����*��a*���\
���UY��B�A��+�4>oe!*+L�z+����2�M���G��s����~[��pF��
��Z����W�:��N����tP���5
���O�x�����k���,�����R�W��Z�>{6��6�-*�Y��tPPF�<PT`8����|e��z��.��������>��P�v� �������
B��qU��u�uAe:�{�������4W&���^
:�KUh��8)S����R��n��(�An)��+_��
jk+���iSy��>bm���trLe������|�&�F���5&}�Wv�N��-h�s�1v��7��`��� ���j:�+�qP���3j7
�uI��6��5U&�������=��j�+����@����N)���r.����Q������R���f��5���&����6u���&]^KAueM�����j�3g�t�Dt�C'bt�9\'>�N���������
������UZ�|;S�[���pm#5>�}m���j����������}�D��SmRW�i;�~Jx��!��$dKFwc��)�l3�5�����m�1��Ie�����aS�-�M�j�+��2
J7Di�l�-�M�N *�\���.��Q����F7���]u�.e���n ST#[7f��
Q�TS}ve�+� 72E�h��VVumn�
@sG��$�d��@VP����M�t ���.�F�4�������l�#�:�*G��#5�`n���
\:�����~O<��+��|Ns�Iw����u���������n���[�5!�
h�1}7.��W^�)[���:pV]o�����������qc�r����c��Y��<�y��P�b���
�ts@��,��579]��R/��_�/[r��w���
n�����������wh�(�N�u��i���AQQ�����7�{V(�[u���
@���
h2f-(����R�C��
��� zC�M��jo���x��'�"�+/\i�/<mV\I��������{����\�2�u�@�E���S�*��/�i�?�<K�Hn;�t=��A��}�M}/��wSH�LU']j��es������_�
��F��]l����9o��a�}��g��~{��H��{oNDP�Y��%��F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5�i�p�B����O�>�Dl�-���/�������~���c�n���}��VXX�����&L�`G}�u�����oo����=���VTT���4�W���]��i�4@cA�;����;������.���������n��=�P�<y�{^WK�,�����6s��`�o����#�<b�r�=�����������I���#������V�X�{�3f�����U4��M��'�h����F�;M�]}�M7��/�lW^y���7n�J?�����_�-��R�������?�����L�2��{��m��Ow��7�|��<�@1b��=:xw%����g�v���G�q�����w����v��w�� ��D�;Mf���J�v�av���[�V�����;��S�����Q�F�[o�����2����^;���m��6�V��$O=����?��j�o�}�����nj�_�m��F�=��4N7e�+{;//�
o���]t�Ev��'�i�4@��N����{4h�������J
k�����V\\�'��o������������u���+����.�{�=���}�C���������e}+�\4.'9���\`>L����M��F�;
V�Zes��u�������������6��� ��-��LY���wAl�DYo���W~K�he]+���C�`�
�w������O>q�T����>������v���E�������O?�|	��#��
F���O��KW�#�u�Y�:w���?��s04���rW2D���
����hDo��]U��h*a">�{������>���D��+H?o�<[�re02�@w�F�
0+��m�����S��ny�I'����H$x%6�)��)����qM����n�S9��.�r�	#�-Zd'�p���~��'�W�^�+k$��0eZ��*���V�D��c�9���VI�p�c�=�n&y��W���_�.�=���m��:u��s�d���{��<����~��Q�������v�����Q����.�[��#2��@%C�=�\�2e�=��3���[���3�U���'�p7����7��SN9�������Vo;QF��q�l�}�u��k���rss�W����_�{�w�}�~������a�77�h#{��G\i�2���E������n���K0�:�U�D7v�)P���o����]0Z�����o�-���3g�����[���n���q�����
2c�7������_�r�@S�x�K�E���T��e���P��i������3g��?��_����{�v��D:t��2�c�����}�{��6p�@W�[�p����m��V6w���we�+��)`.
ro���6k�,[�`����>r������	���_�~.�<v�X[�dI0�RII�M�8�=�m��,??�=�G7���������e�n�����~���/�[���f�;����-�Q���W_����]�����I^z���d�+���w�q��i�L#��&��>��#�
$���[�b������#m��6x����r:��G���o��f�6m����*Y2l�0�q���OQ��a�f>��=���U�nM�m��f?��wM#d7�L#�����d����6o��f��jho��6v����.������I�\�{��A�f��:u
^�-��(E����w�j�T����&3f�p�5\*Y6y�d;��s\y�(������
W������Md�i�@�2�o���������kn�E]d�?��o������gC��^x��:�(����\�[eG�y����+~����Q�*wM��E�D�@cAF7 ���
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V��W@�3��2{|�j�:��V7��U��z��q}[X���o�/�-��x��4��TA^��<�%�n�z"���R&wS
r��M���!�
��R���n�%�#��n�VS���������@7TPH��a��hh
t�^)�CG���_Z9$�d�X9@���j���z[y�_����W@�2������m��/�>�Z~�Y�j��_����E�pY���.�\�`���v����y���5+,.����U}�/���T|Y��,��\d����!�����r-��{��������(�Y�����P�WV@���[��G�]����y�}0��������"�S�\[�]������MU����c������)b��*�����V��?�hy��ZPfoW<���;�����Jm��r���{���k�|V�������{�YY��G�T��U������#��Kd�^]r��F��`Q�]3��F�_���s
����m����S����c�\���W���_���9��O�/�q�T��vI����zQ���Ub����-*���W�vu�o>3u��kB��������	@�����u���������VW�)��z��z=����v��E������ ���6�����<��U|��������!��_AuOY�*}�v��m�qn0�D�@Vk�2b9����2���~6��2�q�����>�_���s\�?<LY��_�����$(���U��*��c���nY�C����qK���y6�/����Z����,�{�7������E���>�f�/�oW�����J���k(k\�M��D*6����n�����v���]����)��{��y��CF�u�J�+�M'��>)���m�Mr���
���Z��Z`�����b��+�g� ut�j��,���VI�jiO�]�n`9��"���g�������<�x��TN�rM����������o�g�o�+^���2���Uk�����@7���bU�����Z���`��>b�+^�������*3�)h���f����]�n�k�o��E�6w���_jC,�}nX���z�������ee�j}S� }t�j_/*���f];F�
#E���>b�VYU=�pJu����/�a;���������+��~�u��U�d�n����r7kM��x����������:����t�j���`^�u��c��o���s���.k���_��������6��O�?�_j��,�������6yv�a���-(�Y����Y����e�����o�\;c�|���V.S����?5eu�*��@7��w��b�<��zo�k�W�2�'^b#����T�2Y�}��oe.0~��T����������k�M�]�nN���I��sl��r��	e�?��o����:����������
� �"������~E�����+���zo�5�k��T?�M�uAF7���Pt���5�+�V�;�$�����
�n�H$��x"l9�5�����
th�R��
��#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 ��d5���F���t��n@V#�
�j�Y�@7 �E�+��Qp���Q�6~X�� ��?���SV�������it_����#�����zv&��z�h��>��U6qvI�	r��"s��i�4�;�h���]T�t/B��i��n4j*W��M��$x�.t�Qk�r%��������kGeL��nd����c�U_��+���@
�@7�A���5�"������~E��i?�M�K���u�E���������gV��l�W�6bl�-\��v����t��3����;�X^�Yaq��?���g�{z����������v��b��s�{�v�y���N�8�����c�m�G5��f��h��w���i���_j��)��_�Z���nx��8t�6��c���o��Wf��?^.�Y��}��:9���=�T���oJm���v�^zOe0|��k]`6��2���\j�.��H��z���X��rm��2�fT��~����������<������+�}��b���2��A�{m�-*�3��R�it�{nS����sl��y�����XN$�2�o|���o�+E.�
 �t �u��c�[�}�K�+"�_�5\�G�2��n}���zwu0d�����)0S�����Lo���e���29\�N�C���U��g_"@�"�
���3�]Y=��<L���?.�O��b���	�ZT��c�+u�����<Xh/��&�{���&bKV������y;t�<{mXwY{���\�8��#�
������:�l��2�F���=q^k�x���7�����Z��{cN�L���}�s��0}�M���
:����wu���n4k�y�m�+vu�?���v��k�
��2��=�m���lV����&�����v��+���W��W���'W�;����k��^�+�xH��z��,w�u�pY�M�]�n`9��"W������]�_��}�lp����'�����\Y6e�v��Bo �t ��XU�n�V��A�u�G�
%�b�{u�����VZD7�\���Z���v�w)�}������r���"W��k�������=�������{�?��z_/*���f];Fl���]\��~��-[e.K�	��m�g9����E0�2h�M�(2����7��#�N��o����5��Z��;�;w����7\;�~��e��T<=����>���>�Wj]:�����l����c~]�^W���[���?=�`~�������}�{�������X�N9���R�U����Y{�[�����Z�"����OY���Y������~���
��f����e�,�2��4��W@�2�����m��6�#@,����=��|�i�<+haV��l��%v��b�VP��-��H��{a�����v�4�N�#�z��c�9f��{��R�}L�g�>�|����2����r�{��=[����Y�V++3��c���z���_�[���Qn�@���h�H�����&�$�8��@����(]����q���5�+_!��ZM�#�@�D��L$eg��@�@��[���d�n4o�eY�@7 ��d5���F�;�.\h7�x�����"��m��v����y��w��>���{�}�^}�~'���2�0a�}����S'k�����������ZQQQ�����o@C#��F�|���v�iv�e�����m���n�m��f�z�M�<�=O��o�i�����������r�J�����O��+--�Gy�9�{���m��7�m���&M�dGy�]}���b����k��1��=������k�['�x�}���n8d��4),,��n��^~�e���+m��96n�8�9s�=�����_�-���tv���~������s����g���c�}����m��%�'*M�2�����[���m���.����x��1�F�����Ip������>r������z��W��;�t���h���/w������=�����X��1�:k�,W������?�Z�j���)��b��w��5��z�-7�&'Nt�G�!~�QGYNN�����$�2���c�~��.*K��SO����m���������m���v����Fm���k�4n�-eo�����m����.��N>�d7m�F�����.]j���]Y�v���v�m��{���O�5�u�������d�����m��A���kC+)p�����o[qq�{���(h��7���i-[��-Z5���\��{�i}��
����gO������VF�h\4Nr�A���T�{��n�4��i	t+���l���p�	.s�c����^{��!C�
�w����azM��{��zk���[�t�U�V���s�����w��h����m��6�<�����������J�|����5T;���>s����C���u��v���k(8��[7��O>��
S�n}�V[me]�vu���������q����
t�������"V)�]w��{�1WZu�_�u������K.q>�������g=�����/q���3���ll�}��O?Y�.]\��X�Yg�������������SRRb��M���>��5QI�^�z����-���DS	����W}��[��qW�^�D7��Lj�@�2/��r;���l�}��/���^|�E;���m�
6��9�^������V�����r�w6��b�`^o���m��������k�E���N;���S�����.��Z@�g��tJ~~�{\e�+�[��t�;�R�����?����N�I�D�W�����[n����w�u��[V��h+ �����������_�j�^{�R@s)�<F��~����,��O>Y�����{������e�]f�����|��v�E���*s��'��W_�2�c�~��ns�1���^���S��5�yO"���^�Hl��-�GM���*k�bc4/�o�}�(y��7f�{���e���P��-Q���u'�R#
J��������J��&���?o�|��{M� *�RZZ���K��d�J�h���D%g�!d,�[���=�\�������vA��BeE.��b���{��7���w�=xe��fEG��~���=����2e�+o�����;�u�����P���+��G}�}^7�<����������z�����T�&M��v�����?����������7�R<����i-��5�X:��L��t�=�C�����e����gO�x��9��h
$��9�z��������_�����]v���cQV����Ygk��� I'��j+�;w����/nX�N6|����}>���n������,p��}��G��[!�
 �2�n����~��g�x��`h���_?@;v���+))q7�eF��I"�W;vt�������Mu�usN��_}7L��;����-}6�W_}e��Ow7���AE��q��^z�7�K�.]j�����6M#dZ�������V���u����.��)P&�Ga�F�reCV����V���#G��#l���UA�DT����������w�i���������q�����C����>����c=��t��i��U����K6l��8�x��H�������|��4��nM�m���J�h�5��i���,ggU#V�q,�
JI=���� �����W_u���6���Q}�m������^W�$�����V-�'�|��5W��������w���'�_�;i�o,)�����\P[�J����E�1c�{�����N���'�9��������v���G����w�u�� ���
�j���ne"+��l�;)���n���Q�Q&���?�� w"�H��T����v��E�U�DA�p�[�|��!��/�QG�J�(���#�<���et�[4nG�[wM��E�D�@c���n�	|a�&�� M�[7:����������/���RW���ht�����[�n��N;��g��jC+�]\\l��z�
<�>�������@�����
������m�m�q�e��U�x�bw�E�P��o�
^�RF�
`�T�I'�d��������o��U���^�F�iW^y�M�0�F��@���U���7��M7������Y����W�k����q����9s��\�2x���-[f�g��w���v���S�N��wo[�`�+w����Q���Z$	�P;t�$�2����h��`hl?�����1��_���X��u�����o���=��#VTT�R�J��r�-�������sx-]2x�`;���l��av�!��s�=g?�������'�|b#G��<����^��A|�J��
����?�]x��6j��`�o����v�]w�f�m��~E��i?�M��5���(�w�n�?��M�0��?�|�����k���)9��Sm������������n��>��?P��et/_�����>��]\\�M�?��v��g��%K��d0�]TT�J����VZZ�M�O�>����geee�P�t����f��_~��=_�l�]w�u��K;��s,??�
���>�;���v�qG����Zk�h��T ���Akt���v�I'� w]�~������H$�)#��j����+V�O<a?����SO=e;v�����Z�h���rssm�v�]v��


���:a�&
�[�h��p�	��������U�V�+P��@���Pf�OYmS�������1R�m��=����-�g���B
���mIUg��{�����o5fi+��?��i��M,@�� ������K�L�[���&M��P?t�����&��!eM��U�V��W^i�_~�-_�<
�:er�4�w4M��P?Yqm�O?�d�|�M�4*W��M��$x��t����
7�`�H�����:6v�X�����u��U�c�����/�l�{��v������T�$�1�O��sss�����~��C����QG�J����CR�����u����j����Q�k��H�-]�����~��\����A����G[�r��o�����SO�f�m|@�T�#Q��{\�Ox�o�G��%z �2V�����=�P�c�=\�7u)W�44B�_<j��k<�����d������X��s-?��h����Jm��"[�,v�c���v����s�������=��m��e�=����!����]+�7�{��������XFwXYY���3����;��������%K�Xqq��	��������w���i���_j��)��_�Z���nx,
`�i�|�x��M�Sb�x��>������kg���3
rk��^�~O��H���.\���{��s�=�z�![�`�~������?o{����3�����%�zo�k��5��l��%�=�p�m�-rm��96��R�it��2��nSl?�Zf=;��V]s����.���z������/��nem+���������oW]u��A��`w�������K/��>�����J�:�X�Vf��Rn+�+�����k�^wB�s���ja��Z����7��lia�=$�B�i�7���<��|Oj��	�h��������~����J;v������7�8x��m��v�
7��#l�����du��6-#����+�����+�xt�j�`�����`�Y�
s�c��-YQn�+�YJm�e��C��= ����z��z=����q	� �2�.,,�i���L�3�8�Z�j�R]NN�x����~���������uh��$"5��������7��C��Wd_�XfG����8������v������{r@ze,��r�J�7o�����S�`hlm�������O?��nP	�Jg��o�l�g��)��'���>���X?��u��)sK����vz��c��=���h�)--���^�������J������������o��=��$����<��g�}�}�]=��F�{j�}Z�\��zX���_��������+_�`��`hls���I�&���s��U�VV^nk��^d��w����u
N��g������}�����&b���9�*?b�,��z��bx�Nk~/���/c���-[����k_~���z���p��������o��
�9s����������B��#�Q��P��_�}���R`:v�y��vB�|+\m��	��	N�K�����m���_+����2^���/��Kh'�t���_��w��.��2������_~���~��>�l��������6d�4hP�I�J�~_f�+�.r����]v����[��s���K��
|�vBK~bK���:9v�^����l�G%�V����e^��k����[��������&�v�a�5��u�\��=����'�} �"�5�N�e����7�h#F�p�cQ���k���;C4�_<j���$$����d���#6��|�i�<+haV��l��%v��b[Y\���wh��D"v��l��s������bQ=n�|���9v����e�\��3+.1�hA�=�f��������������x�[4
?���M�8�et��1�z��m�z�r�M6�h#�T��4/6����D��H&b��6��B�a?��(��
��?��-S����#���FS(j��6m�4����m7�t�����u������;F���E>94Yt+�=|�p�������Nv��g���^j'�p�m��v���[��wQQ����OFK�(�=t�P���{�]�v��_?�u�]-??��������7�x��W7�����-//�=��5�K�K�~j+�x�VO�de+WC�[�m;�����>�d���+
5�t	4o���h�[����>�������m����+�4js���/��&O�l�<��r�!�������U���?�i�M�J�HA�u�����F��7��dd�t���+m��)��6��
7��� �D"�����r�-��F�+��b������4)����E��iH����^�`�������N�g������y�\��2�+i���O	�_�����������DI2�~jth�j[�;c����|���@�e,���}{�m��l��q6{��`hl����i���.���>X#��zkB��L�G����}��������nH��wYYY�j%ez����v���ol'�p��4[i�8G���-[^��E���ROK�,��C���_��t�RwSJ����u���=���|�����[ni7�|�����n�����]}����G��������2k��������UV<�][~�-V����]���f{ks����c�����?����f����r�T�[{��<�p�]w}+��k[|�1nx����Z�?�=�e�+/��[�
����kS�G�Xs�*�$����-r�c��
����A�����N�:C4e�k?�+�c����O~��w��������x��^y���<m�s�ob��v��v�������-�g-������c�^~�{����Jk�� +��+z�����o��V�$3�Wo����{V����b�mm������	��G�@�p@��~��t�,�������$����ck����@�G�;>��_~���*�_�?��W��H������X�e+[z�eV�i���>�Lk}��V����@x�F�l���[$��b����Y3����T��[���������k��w[Z�������~��9I}�@7�d����� 
1VpZ%G��]�?}� 7����i���}�MUPY���������2+���f{*IR��W3�� /�
k��N��
��oRk������[�����L��G���(ui��b��_	�T�~������!�X��g�!���9;Y��E����.S<��FV^����{��;�][��)��=#]}�X�^�o+[����z=*��4���r:�m� �>Zy����7���
zGZ���.��(����ei�m�����|W%ZA��-g���x�{.C����f�z)�6��c�}���Y3m�#�����\����_�:����s�����j�]�[���������+W�bVC��@t@*�e�������k^SJ����[��GZ��l���C�P)���_��J��W���q�a{���{X�gs�x�;���K@!�
Y�|�r��r�Y�C0�R���W�Xf����y�X���m�8�J���^��j�����|�J�������\+~wR0����,T��<+[��+'��A���]���-[�2�cQ�����n�������*����������E?[��]�`�=����mc��R+���`H�w�����~��V���`(@�#�
Y�2*]��l[=c��v�����R���v�E�&��?|����w�[����_����9�Oi��V����[�s����+x������[7���j���������7X��;Y�Ws�����{$���wO�����F�^�t�M�4��z�-�����������b$�������my������Zl����[���[����m])���*�������r;o�����=��]re�_�K��V�>�S>��E����g��
��n�3��e�]_�V�<�����I���n@�D�+�3b��6|�p�h�p�
��eK�6m��u�Y6s�L���;��w�m�s�E"��h.\�"x����&xTi����G��M{���d�]�oMl���� ���?Tb?HFF3����g�w�t��7VTTd+V��{����w�yg;����{/��R{����c@Mj�N���
r�RF��F�r�I������w�ik���}��n�^{�e�>���=��{�9��_|�JKK�c@d� ��2�^�r���=�h]t�u���
W���?�����k�;wv����c������3��/_��*D�����KJ7h�2�.,,t��{��a:tp�������?v�����u�����u���l�2+))q����
D��XFK�D������>�m���z��5w�J�,Q�;'�Q�2 �25n�����V��E��a����m�����6��
��s������w�V�ZC�`��e��6`�{�����3��+���.��2��n����eee���o�_��WW�[����2Zd��A6l�0;v�]s�5.�{��!v�����W�Xa���?��W_u��~�2�V����|��=�����{��w����n@��O{��g���n����;;����������
'���\�����#�����`(kd<�
@}4X�{��E��~��?=��#�H���xdt�Z��B�8������_u��Zk-���kx�T�[�������5m��U�����;���u_�<�����P�� 
1Vpz���vz��w��)�?�Y<����F���t��n@V#�
�j�Y-���1c�����oeee�j'c��e�����_���K���P;t�O?�d=z��:C������mk�z���>��/^�v2�.((��.��es�}���^�J�(��de,��d����k������^z��������G"������-Z�(�2�.++���)S�!�^��B����5m���	UZ8p��Q���kS�G�Xs�H��Om������l��`hv��mg�;�����ly={C ���$/c��S���?�iE�5� ��/_��I��iR���W�Xa��M����v�}���������>��s+**
�4��.o��`M��H���m�����[7�i����3�����������[o����'�||h�T���+���z�
t+�=l�0��������6�l���U�l���������!C��o�
^����+�����	�

t+��R%'�t�}������/Z��}�W��^{m9r�]y��6a�5jT�
��H�f��s�{4���U���7��M7������Y����W�k����q����9s��lB7��$M��5_[��f�N�
 c��e������m�w��]�Cc��������,X����Z(����z��u�m���m��6��y�Mk�-nx<-���:�����T����#,��&�;"U�Yw���������j�����l}��;�:���`h ���h&2~3���\�D��u����,���z�t[~���z��V�w77<��s�obm�����ojE���\k%h���`m��S�{6�fm�\��S��h����f��7R�nw�����s�V�������������d,���$��VV��E�������6c��~}�Y^����{k+Y0��^;�
G?���s
���L�>�����e�n��V�y���q��������e-���
��Q����\�\�Y~�m����N�&��=����������sO��a��Nr�
)c����[�O���[��#�XQQQ�Ju*qr�-���������0������_�������{��=�p��eeV:�����~���-��LWZ��Y���m���������g�6m�����~����V�>�	-]2x�`;���l��av�!��s�=g?�������'�|b#G��<����^��A|�8�D���_�!�����|�?�d�)V8��`�Y��mi9;Y��E����V:�++z}��|:������O8����m��in��������t�P������[�����7��2�^w�um���.�=v�X;�����������n��f��z����w�}]V����|@N��-��<��VG��������J-�9��yq�uz�%k9p?+�6��0�#-[Zn�
�|U�-��&+zk��m�����|�p%&P�oF��{w{���m��	v���[����]�v�L��
�+���f��Pw������~����o�g���O��V*�1��
���&�}h;�jm��^�i��"�V2�+[z�����������j�]�[����
!��n)((�=������{��7m���6y�d{��]677�~���%V^R<��zh��������[��GZ��l�����ae��	�� ����J�Y`���h-��!xG�{�,r�N����Ue���o��d,��h�";������o���g����W�b�YY����!RI������KK��5��5�ns��V�������UV��v���,��&���&�KY�u�]g=+[���W�tY�@�e4�{���v�%����{����+]2q�D[�bE���|=�����J��n��
����w��e�\�v,�;�j��?��m�w��������a��r��:��\Ts;g�N.�^��wV���V��g������g�������Yi��~�m0H���;v�h�=�����K.�{YEG|����^{Y�.]�
*�x�	[�dI�	�*�%����3�Yn�
���/uY�m�\�n������
|w��^�p���A���X�3�d��mm��1.\7���;�esW|g��K������k��`k��[^��V��[=k��}��2�����VGo�/������J��kEo���4�Hy��qF��Y�l�����s������b6`�;��#����]�ZNN�(- �\�<��?�M�����;���u_�<���DQ�=T�D�������w��"-��h�O�lKo��l�J�������k]��e���v����\,�^y���z�������2���f��V^����y���q�+c��9�,k9����)�%V<�}W�;\
%�j�\���i4��0���?���������_~�/^l�
rY��:qc�9 �����,��O�T#�5!�
HF�K��M)���o�����3��|�M�P[�I'����9�t�{��g��~������v�&�lb'�p��9����c������+����Z+�$��Je��]7
��,c�n�d�������[�;�`^x���n���u�Qv�}��|`+W�t%L.��r����������*g2����o4������l�����z����_|������SO���G�v�m�U�V�=��*g2�L���@wAA�m�����{w������k�q���>��1�������8x7�e,���m[������/��o���^x�;���m��9���V[me������}�������2~3����`�
l�����CU�������!C�w���=�-
>	@#tG���#�<�.��2����l��6^��F�.//w��/����z����gO�b�-����+Yr�y���a�l���
>@�����6u�Tw#�]v����>���l�����sg�������?��s���w�i����������.�62�V���;�v�yg���+l���v����c�=f���M�<���nBI7 ��ft�l�����0a�n�5��?�xW�$�� ���;v�h�=�������{Z�6m�WH^������������4i�
6�
T���'N���"�>�e�t�|����\�n��f7�p�����Uz��^{�q�g���>�
t/\����~���m�w�{������z����}������@��/������s��?���z��/<\�S�7at��i�l��w�������]����4\��}�����Z�*x��W�\i����m���:w��M��}_|���X�"
@#�%���@w�6m�G�6c��������u�O����2�n���
0��}�]���l����+�i�^������>����%{���u�Q������sO���kl��q6q�D���k�^��?����T�h�{����;���N?�t�5k�]q���������s
��z��@X�oF��sg�����M7�d�z�r�=�p���-��n����-����j�F���c����\��z�Z�p��x����x$�-���.��b�7o^��������9s���=��N�:��Gm&L�������i�^�����������o�>����-�c��4
�M4�A��|���v�iv�e���j8�
����\�������d(����O�n���>/�|�|�����������.��r[�b�{�+--�Gy�9��>��m��I�&��GiW_}�o>#3f��c�=���4��M��'�h����)W�,Y��eh�����������|��YQ�����M�]w�eW^y�{��U+�A������^����������������]�y���w�QTk��M�P�)
"��HoR��Xh*�"��+� ����T��Q���+�HQT0(Jo
���t��R ���|gg�H�&&����{�ug�����L����������#[{���o�����Y�j��w��=����:��_t�F����;w�����������M{l?���)o������[���L >n�8y������3?=' ���<�C�hh�=�r�}S{����\mO��q?�o�d�����o=��|��}�d���9~�����(h��������	���ppp�	�5���,K�,1�YY�h�)[�������\�EK�4i����3e_����2
�g��){��1A�;�V��W��#GJ�*U�:�A�����F���r���8p��{��������-��^��~��=z4���zs+�I}��A���8��58�v�t�R9{������3gL��Z�jI�f�L��^��UMx}�����:t���n���4o����V�F
i����\����V�������	�S������������-�����`�9}���00��;v�0��k�6��U�XQ���'�7oN����b����#d���&�������&�i���5�.]�����D������VZ�d��mR�n]���KM[zW\q�y���?L���|-]���`���gA+g������\��)����.�����^��[���:�ZO[�l��ir����!��4q�IO{�+w��S�N�2&�Mf����5���{�y�:��|�5<����d���&��4������>S��.��4`�P�������yG�|���M��n��I``�iw����E�5�Y�����[���8v���-�.U��)���v�mfp�����R��p�
r�]w�}���+V���?njx?�����&K�^������/� _��@�W�^r�M7I�:uL���-��i��n�:���y�d��=f���^zI4h�����%={�4eI>��33�dz��s!��,X�@�x�	9p�������w�4%J�O�n���_~���I�������;MY]�����d���%���\eO�����n�=��=����y����~����Ja�@a��Q#{�s�>�c�����/�0�������J�V��l��f��-ZHDD�y���iMh��0a��f���2{�li��a����	��%Z�:�2-L�K�xJ{�O�2��G��I�&I�>}�������|bb����{[-Y��N��=9v_�����i`������3L9�)�����{w��X{~�pU�\4H&N�(?����i��^�"����~���#�C�
�������+WJ������[R�D	{I�#F��a�d��i&8�z���s����Wf��)W]u�'���-3N<����'yf�V�T��X{��-�v��#���S��|�j{
.����~ +��>��&��#�I�&������-[����s�N��������3*n�����\s�O� .V����Q�L�yeD��
6��^{Q{B������[�9s�H��m3��^�reS�{����MO��}�v��;�		�+��R6n�h���l���<k�N�
��
Lj�a�*y������u��R�J	

���-:H��ZG[{I��=�/^l��g��:����2d������w����{[����7nl^K{w��k�.Y�v�4o�\�W�n��X�����I_����S�|�rsnz��mX�#���k������
��Zip��'��z���zkr������J�N�*��v����K.��d.88X���3����ce��5fK�=��jz��qj]n7���s����G��&��[�a����>���#xAw��0%F^z�%������o�z��I��}Mj���0�M�_k���;JTT�i;z��	������sMOm]'�G��T�f��������7K��M����35�uPP���a����n��R�|yS"E�Q�U�Y�]�A�E�Ik�S�@A@���j��izD�5J��-+����}�����v�-�|V���c�y���A����	�����m�d������_|a��������1��*=v==='=7(���0�ut�#���S��|�j{
.����~ +��>�zt|A7��t|Z�
�O�:%��-�%K������V��z��o�>8p�<���r����f�i����j��<��][f��!��	H��A�����G���o����%>>^bcce����a�i���t����;d�Y�n�����A����My���zJ�~�m)U��������]�v2k�,������/�4���7O�4�kAw\\�l��Y��ooJ�T�\��k�����K����R�J��a������-[$&&����t�>}������+�t������������i
��tPP��(QB���%!!����>ejG��_�U���'�j��[�B�%K4�(P��2���!!!�7��/���2m:�����A�R�J��v��a�vW�ZU�/n�����X�b.K�,��}���a���g�5�Z�je�p��)K�.��������n��W��DDD���Ce��2b����_�~r�]w������������M��@j^
�����[�n�O?�T��['o���iW:e��
��/�����'�����upIxR{qk��N��O<!]�v���`��^��'V{:O?~\�~�i��w���}�]v��;V��)c��g�#c�)��hh��LG�7���[����\����@V
�}�=�tGEEI��=���9��QN�>]��+g��g��`����7��B�
�D��.�^�:����G�<f��!5k��>}���U��,��q�<��S&�z����'���F��:���������u�<��32x�`y����i��i�_s�5�T��I�d����7��7������2s�Li���t��M����%i9�����}����O?Itt��/�111��H�
�X�bvk�t��w��a9{���
��n�����5�>s�����'N���i����7�p�Z�*�k����H�?�8�N{IZ			2e��^�-�d����tJ�=�N�:��_?����,\�P���o���2�:�����!C�e����KS�7��J{t���R�zu�k;""B*U�$]t�y��O?�T:t� �'O�j���[����[i9�e���@�����Z�j�v}����d��2g�����L;�y=�V!!!&���{���bY�y����Lo�����k�V���)�����H_^���}�=zT�m�f/9_�R��W�^j��g�#c�)��hh�=�r�}S{����\mO��q?�o�d�������;66VF�-o���DGG�����)�O�.����[�3�
�F��S���Y!�x��A����C;,,L����I�&h/==����`��l�����@V���Z�}��iy��'e��y2e�t;{)l�;�
��~����
A7�^�2..Nv��-����f��rr�kAw@@�I�%�39���%�����K/��+L����+�K�����7���S���Y)��Kv�!q3������g��6Gh�m�\J�u���e��?���:�d�=$::Z>��c�����nr�x���/��oBne�D�s�s�s�������tJ�J��������{On��F1b�������g�X�r%�8�{�����D�M�r��J�DEEI��=e��vK�"""d���R�\9��?������������@V
�} ���~��;#Z���9��9�g�t�����i�����vK�J�*%�z����P��?#��o<����q?����p����)�����~��o�B�S���Y�>��2
���s�[�V�9��,fh�~�M�J��@�			�l�2:t����~��B���T�t@�
R��7��o������YJ�4��'�2���6n.��O�2���-.a��7�����U�z�������v�s��$���IK��?@a�����������[Z�j%�F���&?t�]�v��G��g��T���M`}n�Z�yk�����7oe������[�5��E�m(�"E�%.���R��?�qv�r�N�I���&��� 	�Z]�/���"	��&E4�����{��������#�������I�&2q�D����:���g��'�|����Zu$�����o��ze����K�������Rg�!}����I����<e��H�c�����4���{M�����3��Hp�p	�r��]�D���$g��'�o���C$�F-)R���7�^
����gB�G}T-Zd���m��H���i/o�	�J����b���6}�ym��)R���Ny_�zt��s>q:��+�������%?�^+i��������5����S'E���2mT��_�t�>}Z��Y#��5���KXX��$-m����������3����*���v�K�y��>��?-J�=�=��[^/��#���7��r�_o9��,3����F����(9��Z��zU�ny�kAw\�u���R�~}�T����1]������k�^�����9���[� *����L����v�9&
�S��@>��`��9�Y]��u���?�-*��B�T�6o-�6n�����.������������+d���r��!�5c�\���u;���y��X		�\�R"i���v�!A����m[����v�J�����E���M��'1���[�^��%�����b��Ixx��Z�JF�%��������\�k�������bcD���*m�������)Vb���'=��6���U����4�y���x�o9��s��g��$�u��
 y�tI�v��{����H��me�������������k�.���t�bo	P�%��-�S'%���f�H���+�3:����'�dI�kJ��}r&��5�./q��b�>-���Ir@~�j�]�Ly���������7��a��C�&�g��v]�������&H�����5X�b	{|��]�o`��%r��_�r
�K�����^r���-��#4T��]ejt�X�r	���Y~f�w�Wy�����U�^�Fy���QV�T����@{��1r�����[�u^�u��P���8�&H����r���FB"E�5L�_"���3�%BM)��
I�����dm�D�������vk�"W���J��x	)q�����4����}+�2&���JbO8g����E��s
������[44� �G�7���[����\����@V���d/a��Yq�N�������������[o�z��� 	����2`�i���DFF����� k���s��u��2���[C���~Z�x�	��}�����t�����,Y�Dn��F7n�$$$�K���=��3f���'J�=d���j/q
T9o�<Y�f��������?�`/���(m�-Ry�kA��&��Y�f&��^��8i?������o�i���w�n7l�;L��@!���[�����[���/�*U�[3V�jUi���l��Y����V�\�D���u���hO���@{�^�CBB��+�0�K���g�f���C�~�z��n�����b��Ixx�l������0;#G��Q�F��U�����3�����%��_?�:u�\}��r�����1c��Lks��f���o�����A��!1b�<��3r��9x2$�������c��r]��%���>e��ee����c��;w��v?t~��m�t��@z^��*T� ��z����~�|�J���p�k�V�	�-���'O����e���o�����3�z��t:%22R���z)]��4h�@�.111r��4h�<����V7�y5�NHH��'J�.]d��%fJ��i��o�>�0a�<��#r��q{	.^
�W�Z%����\{���\����s���R�2e�������{���?�\���{	.^�e���f��7��f��I@�����Q>��sR�^=Y�|���
�����S�N�o��&m���Z�j�����K�I�&���Jll��
��n�Rkt�(QB������9	��H����h��f��={�d�K����7��������;44T���/�}��yX�e/IK{}O�2E"##���n^��I�.]�e����_?y���e��r��Y���������d��!f��[n��l����nU�Z57n�T�^]^{�5���k���>��(���'�����r���R�fM{K\�t�-Z���o�n�����#F���J�6m�VRx=�V����A����+���sr��1�����[����?/�]v��&i��;��� )S��-Z�n s^��N�l��E���;ILL4m���2l�0)W���,YR���~��o�Y@j^
�ccc��^�:u���Y�L�
��y�S�[K�h��T���_���o{K\�tGFF��	����5���@���|���R�jU3������_��s��vR�Z�}��Y�����J�*2s�Ly��G$88X�m�&��/��;J�V��@��=�����*����=���[��h���eK�Q��i�,K�-[f�4h ��7�!!!�����>}��i@y-��A(L���a�N�:%����T�\Y7nl����E��
*HTT�)c�v��azt��W���v;q������r���&��@y-�

������L�n���?���r&���3������l��]�6A7 
��Z���[n�:u��C=dzk������Y3����Y~��I����e����]�w�9@y-�V5k���?�\(�5�x@>���s[K�J��S�N2i�$�>�y5�V�����_��k������n����b����q�d�����	�y=��J�%$ ��&�KV{
������[44��r9���=���G���RX����g�H��b%D��>.��8�u�����������~��2�@j��������AC���m��{����*��9'snI�rA7�@0=����9?�tn�@�#�Q�����ZhO��D�
(|�\I6��H8aO��D�
�?�����t�	+=�@��z�/�/��^{M�~�i�������������-����������H�i�d�|��2�Y�=#2e�%o~��[R��y�+�4x��������H�s;�����9��!�u�n^��������{��G��k'��q�����3}���O����	@��x�%C?p������ y�s���4��9'��\�4}4Q�h�����n-���Yf������ y�����
�L��=	����A���n�j����GKdd������>�.��������l��7+-���C��vH�2b�u^�uy�T���/��`�%c
�����������]f����U)_J��V���!?�b� <�C�D���)�P���k�GGG�����v��2s�Ly��g$<<\��m���y��l�P�D��l�cI����D�Y��]�'KUiD{}�(�
R4�nL��_r���W�M���I�����nZ����N���C��`}���t�={V>,-[��5j��dN�b��\)���~>�R!���N��(R&L�Cp���l�%�~��K������]��[nI�9$�����kAwPP����I``�8�O8OL�%�c��<��J����7&H����{�E��{�����K���!5�p����Z�]�dIi����_�^:d���2H23��{�s�f��W������dc�>����Knm�Aq�-^��w��=�R�J2j�(9r�������pH�l�I�itfa���I�k���\��0�Q������"y�+�<|K��^����e��]r�����E������n�A�����1c2|L�4Ibb��;��-���u�n��?����A��RV>� r<Z$!�nH�=��Ur��C��������k,�08Q��O0��O��k-��%A^��j�J���tk`=|�p�������S�;&?���L�8Q���c��9& 6���a%DjWu��m�$���>o�m���!�]m����%��&���Szy[I��YR�������!��J��zT����![?	���z��
@�����CCCM�����]_�6���E�D��q�W+,SR���"s�Z2;�q[K�Y���d�BK>����g��
i\�!�Lu��?-9rR��%�L�t��XQ���"�J�}h��=�K�9$4�!;���tK����m��?t}�Px��������N��=A����QH���0<���-{,�}O�R$�K���!������C�~�)wM0����������b;��7�;+�=
J��X{��-bO�i����o�#W�S.	���Sy@�t9���@P�9{
�������H����> ��[��5��M�f�{��e�u���t#�]@�R����/
~��o�t �@�q?�o��}��q�[�-������={������g�_�`���DDD���\�rv�/��������7Y�>����-��P`��#��)����@V��7�r5�X�B���x���#��������B�
���Z�F�
�S��[F��^�{��D}+VB�����J��\	�:Xa��F��>�������NC���m��{����*��9'snI���E�
��0=����9?�tn����Q�����ZhO �t(0��\IfN��-��F�
�i.������������[ -�%2{�%-�'J��	���D�z�e��{Fd�BK��"��92\��wB��g?���>����;
D��{�n8p�T�VM����G�&�>}��i���%>>�^@a�x�%C?p������ y�s���4��9'��\�4}4Q�h���2]Gw�pM��������>��7�<H��'�t[�%�~���m�V�x�
��g��������o�>����2��
��	"��������!��y�ym���Re��t��5��y(@zwL��P��$�`��g��,�hI���������������A���[��g�5��k[{vk������z�)i���Y�d�{	��*:Nd�K�^��@���k�.Of/W��{��@	o���Av��r�T+����J��:���@��Z����g��%;w����_���~�P���k$\@�4k�L�|�M)[��|��w��7��+���Q���`K?�^���p:3���3�XQ�;��U�l���	��:�����kA��S�d��
a���U���i�F�m�&���v+��(&���1�L�H�|���!�vpH��%��������=���kAwBB�DGGK�r��h�w{

�%J�m���GF��yR+��z��6Y2-���G��YA2sx��g�����wx-���ZkpGEEeY����3r��a��n��
-��2���G��uV!�'�����8�)}::$��C�����!ur�v]�����;44Tj��-,�U�V����Z��~��DFF��u;�Wp�#���`�D$(0���<� ��1K��%R���/����=A�no�Z���Nv��M�T�"�i��������.Z�����������(o���4�U(L\!rX	��U�n�%�veC}�����UR�����tH��"�l��T*�Z�����[��[W�
&G��{������`{��5��uk)U��<���r��99r�4j���@��
���ti���VX2����s�Z2;�q[K�Y���d�BK>������r%��,ye�S��$�����g:�}#��N�*=���W�n��!w�y�|�����{wS�[;vL�l�b�;u�$_}��������k4�=z�4l���UW]%�
���w�k�����s�I��M�����?����q�����%K�M7�$�f����x{���1���1���9���9����CF� ��;�J�yg�SF= ����fHt:d�K~���{s�i��'�C���;��)N��%�<�������{��2��E�-]����]�LI�����tM�����#�<"�����U�J��5e��}��H�z���w��-Z�kg����&M��C��u�]'��O7!vz����,���������K�"Ed���f^��GHH����.�c����j��K/�T�o�.{���:�;��c��K�#�hN������H����+���rIXT���oA���)��~����~��d�?g��'{���9��-�}r��=��+^��	��l���_��!���c��1!����Mu���a��<y����S������P��������\�R^|�E�S���]�VV�X!?���	�o��f�0a��1����/�,�7o6��i�&s�������������>��:P�y�Sg�N����}"������7��/��u��/��q�)
��sg3����*88Xz��mB���g��%KL�'�}��9<<���P�Br���hY��3g�^�O?�t����W7u���]G�[7=&=�>}�H�^�$((��k�������k�M����A��Z��F�R�lY����.��:����l��er��A����2e���.k�Z�t��={�LgE��h���[��3�<c�h
�3s��!���m���dIz���l������J�E�Ii}t
�S�������9��y5����/�_�~�����n���	r3{h����Um%Cg���;v����k���*V�h�tky�����-Z���R"��;����i���5�.]�����D�r�e���4<W����m�6�[��y�3r�W�g�5N�����X��H���e����G������|��y��"��~��asn�yt���*U2���=j�^X�&MLmn
�=������7q�I��#������S���n�.�������w����8���kA���� VD�����S�SZK[f����5�=����
�	�����5����@+�=��4�����m�g������E:pf��=Mo��>�Lj��e/I��:Y��!w�y��>}���J�h�&��N�'xF����>�'�\��u��)���^p�=���F�J�]������;�]{�������GJ���x����~ +��[���F��S��Z7j�-��!����n {���[i��A����c��1�&:{��r�V�_����k�%)�=��>��3��gWV=�����������#$00�^����{���f���r/�����tj���R��L�:�'���7���������ik�i����o�#W�S.	���S�-(��=�S���Y�>���� ��Z[����z�jx��WJ�R��M�6����4%tE�15��o�.'N��[���[�����y�1����#f�����tp�%dtM
������o�������[�;�t/[�L:u�$��������K�,��f���o�8�N�nA�������-[�szZ�E��:u��0:/T�\Y���+;v��0p��[�x]���\5�4��6n�h���l���<k]q���7y-�>{����5K���#�>���Z�������O?��2-[�4�������� �/6��Z���E�����%M7nl^K����k�.Y�v�4o�\�W�n��X����_}^Op���|�rsnz��m^��7�����s������J��MM)�=4��j't�iO��]�����������XWM1
�?���0a��z����r^�;��C�V�*c���5k���$��,:t��q�������[���G}$��MK����/S�L1�������X��� �P��_�y��0�A���Kr��W��7�(�����}���C�1����s�9;v�h�d�S��53u��C�0����3u����/���7a����n��R�|y<x�9F=V=f=v==='D��%
���Z�Z��������Y���5j��-[V"##M���e��9��E3�����~�����s�{���m�6Y�~�);��_��a���"==6=F=V������������r�����~�M���.���{L���6�-|�~����i?l:���=���G���\���[P�9{
����7���}����@Ny�G����K/�TF�-����\��������~hO���*�P�y-�>~�����Cn��6S�d��UfPJ�	��]������{x�t��Z�����vK�J�*%�z����P��?�+����������@V��7J��-^��
�/���`����7���}��t�-^+]@n���[������i�|TT������[��|R��[�/��"��mp�t	���[�]�\9�����C���:������[��5�;v�h:
@n�t	��t|������@������LO�>�Z��>2���o����S.G�7���[�����K��"��
?gO�����@V�����eE���q"Q���m������(�A�GX}�@n�G7

�������r������%�#����A��'d����x��l?V�\)������OLOn�i{�%��9G�.���U�V����*�����c���c�	~%�[{�YQ�)���%(��\IfN�rS��r��Q�10��������e����@N8�Dj���7t�&��@^#��OqZ"��Z���i� ��L��WX�=3�l��(���)����k{z{Y2x�S���(�Ns�����>e�zK�~����d��A�X�:k{f2����mf�l���S��g��fU�����%�qJH1��/��]��o���������o7���l��7+-���C��vH�2b�u^�uyz�f�k�3gE�n��Gx�ut����Y��'�K;eX�y1�qu5��w-�=�t����#�<b:
dWt���=�����<�>��������M`@������-�]�Ze��z]�����S9�<��*�3����J�
����e�MJ*]�������*K6�t���g��v]�=��f����������F����SN��;�(�����3b�,9c�x(�m���77w�����D	�1�<?rk�tj�Z/��H�)K&�sJ�P���$�y"P>^`���N���
x
A7�d�&K�EZ���@90+Hf���q���J{t>.���2�k�����!�� �WZr(i� ���-�0�����mN��L���>&�._J�s+�<��a�u�[�J	
�g���X�\�Ht,]�o!���.�zD��l���	�2����6�d�_"�.K���uH��	K��T������@AC�
�
��J�����u�,It�&��y�%5�8$������m��tH��"�+?���H�2��%�E�TY��%�����8 &�uxA7|�+D.$���C�Za��,L�Yj����m-fy���)-��;W���6�J��m�2�)�XuJ���3����CJ��OnOZ��"�}�4=�u��sk���=���F�
����CF� ��;�J�yg�SF= �����D�C�����="	��)�mIO� �����8�R����Z�[����������%�>� C�w��wH����u��<�������P���L5��[44��r9���=���G���\���[P�9{
����7���}�����Fc�����5�@��G7
1���F��
d�(����G�
�i��F�
�i��F�
�i��F�
�i+�=
J��X{��-bO�i����o�#W�S.	���S�-(��=��������sd��_$��i����	����K��n�Ze.�[���~����
���}@n�G7^�!������.���[E��5�����@^"����'w|�Y{�����9����"-W��V\oO�7��"_(W�O��2&�%�n|���L�d[�������_p��9��l�Y�M�
�%���
%�����e�>�5w~!K�M�q��6�F�u��$q��k�.s;M��w|&3oz�ny���f���o#���w���\_��$���/��,�/nd���`_h�����f����=��3A������a��D��?��WM8������pF��i�����P�3�@�3�:ek�����=�������?�g��v]������6���,��6�u��{>#6L���3���c;���?�y��>(���������6�����	�K�(�Rg��J^"���d�!�K8m��Y��]���l����w�����^/����K�&�#T~��*y2n�7tPY)��C��G�-�x�)��%�|v��^�UB+�� y�������e�3e��c�Q���:�{����.��U��������+�V�~M���JbO@�>2���o����S.G�7���[�����K��"��
?gO�4���=u>�AkO������eC^Y��Y�t �/�N���l���l}6���)VR���#_�X(�+���/i";���q��c��W-u�E����O������\	����� ��w�'[������o����"!��r}�}��R���v+�=������RaEC�XPQ�ur�<��M��������Z��W�6��)�I�����/?���n�
�X��|�w����*�\�9'=7=G��B�
@w<��$8�9�dg��3'd���������$���r���-.���+J]&���)��j�r����O<k��=7=G��B�
@���^���K�rJ���f��b���i%-��vK����;��	,j/�0-i`�R�]������z{
 �tP`�J��>�����6�Dt0H���C�K���}���x���(9z��T	�$m�43��ja�H��)��m��J�4,_G�E�ov�l�r���J\�gf"��9�&#�p?Z� �tP�m>�C��$�T�!���Wt�A
��%!e����{�r
�'�{I���e3��6�\-��BB�������ku�Q�
�f�����{��}+�:��%M$�h��<����)�&d ��l�Q�����'�����H�X'u/�R�4~HV�#K��q�>4�Bh�R�D9S��T��,�qo�-S��I�>D�����'Yw�wyu���@���U�i��u����W�Q���B��������n���;��%����VO�v#��:�m��c���o���&��;>��7�i��������JbO@�>�p|�u��{��H����+�60MXT���oA���)�&�u��r�����W��U<-
D���~����xz?x��������o��_k�7n�U�V"����W�
��.��sI���������U[��������b>0u��k���9�}��q�[��
��r3���G��W��@
����:ek�����=�������?�g��v]������6���,��67���w�32a�4Y�*�����ny��_�Ar���k5{��6d�ca@~I})�V�)&�c%���g��v]�r�M����1���U�z9���������&�����$m ��|�������H��x�)��%�|v����UB+J\�y�������e�3e��c�Q���:�6�/!�����A�	.%A���ds�r�JKp`�\ZQN'������WI��Ue@�{�DPq�_�C��t@!V4D��]'��A%]��_�U�6��$����^��"�p<��$8�9�dg��3'd���������$���r������A7`�B$1�����R&���w�X�"qZIK,����m4��;wF���K������n(�\u�w��KN�GK����ea�6}�R^�����������m�D��3��Jh%i[��YGU�D�N�+��l�6@�#����Ysx�\RA�4zP�^�A5�#��T�uG6��I�5|���%y����tV��r�h�
	)R\�_�S���IF]7H�U�';N�����x?�B�
>��_>���I����!������ke��M��"%�b�r��H���Yn�*p"����2e����C������K�������k&I\�i�����m+�=
J��X{��-bO�i����o�#W�S.	���S�-(��=����n��[s���}��������y��%	�����}@n�G7����y��|���������rn}�7�������x�Y����rnu�����2�n�O#��4������������4���=������=d��������9����@�?���O���e��s��V�V$D�W�/���]j���n�=�/����E���{��M������s�s�s�A7�E��;>��=������D�
x��+�w+�����A�
x�?�+���1�A7��t����3����l�]V.���_���3����l�m�� ��P�D9y����������/dI��2�������cuv��[�J��i���u�������U�Z��~�,�>������R�C�������#-*����7�����/�7K���v#��9���]��T)o��_v�j�G�(u������X=Q���v?��tP���u���k�]){N�g���Y.4�:���\e��-����������v�K�K���{��_LX�a���~$b��}�2����(ug�j%/�R�a�?���%�6m������U��qmQ"���z�
s.V�����-9�V^Y��L��+�%E�#@B�����!�
"+�����	��������9�&�C��!�JV�ov�,��g�V�]���w{���c;������NW�X���9�)��%u�r �t����	.%A����`��*7����m'v���s������:H��[��������~����^����7�O��BLks�y;H.q����5�L��
G�J��
��F}���wt������L��<��6w]�/iX�jY~�W3�������{��A-��y���>(M+��&I�����]�$�\\��S��4�nK\$N+i���[��M���HH����jkYs��q[�p38�K�������:ek�����b��5�e��f��������t�����S���h�Z��,�b����C�K���}���x��G[��WMH����B���{�>��7L��/i"/4�'w^�/��Ax�b�%�l��=l��C�
p����5�7��!dH���dP�>rIHEYwd����\C�I�^��ox�Lg��Y��F�f�Oi����^����e��u�6��A��������o�Q�
��%/��G���G���"�n����G&x�{��2��C��BYr`��[����Z��T,Q��&)U44�m<�a�B�5����(/4�'������.7�@A�����P����������!�����M�)�V>r�=�����=������S.M>�fO�7�����������9�����K��4�s�/���k�s�����
�+�u~��@^#�|��4�v�f���6��#�|��]�]��+�m�A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t|A7��t��#G������a���p8�����A������5�''�s:����?�w�!�����%K�M7�$�f����x{���>u���Z����zPPt�����K�>}��g��c��I���M�������o�+V�yO�d���2u�T�������?�+��R���/��-�n����/�,�����)��_/w�u�������������l������t����O��1cd���2|�p��e�|����a��<y����S���q����o������/J�:ud���&����M�}��7��	������v�}h�y�f��M�6��������.\(o���9&�6��<�q�FS�s��2`�)^��i���{K���e����d������O����9S���#O?��4j��^"R�zu9r�T�R��s��q{��}�������

2�!!!2p�@���{���1��t�-
r��A����2e���.k�Z�t��={�L_HN�w��!���m����ys��Z�5�e������W���Cu�����i}���ps,zL�m�y���3�c�3]�vm��^���^�z�<Htt������O�h�u��v���M[j%J���.����u�V�����m�&u���K/����w�W��?����%���;h����K���M���\t�ER�R%������vk�r�?�FC������#�i	���}��)S�D�q�GIO_KC���wK\\��
�A��L�\�B	

�[s.��s���R'E�5�Y�����[���8v�k��������%QQQ��gOSF���>�Z�j�KRx��[N�7}�t����������/�k��~��}��w�2(��\�r��)<Y�B7nlO@Zk����<G��<�eB���������������t���#�tf�5��d��:������'���������A�d�����O?I�6m�%)��+:��[�r�t�������[R�D	{�#F��a����i�L�p���{���{����3������L�l�2i���<�����od:h%�����b��I�5���-[�sz$o��A���czQ_HN�W�re�[�����CN�8a�R��*�o�n�s�!!!r��W���e��}�-�M�6�g-�B�
����H��-M��`��K:���Y�x�����Z�$+9�����z��L{w��k�.S��y��R�zu����>��_}^��S�N�������1��t��I��kW�={�)k�58���Od��	r���&��Y��������;���U����ce��5��T�s�N:t��!�����n�[����;�G}dJ���n}������)S�k�1��Q�;iY��{L.\h���5k�r ZO�^�z����J�-��]���#""������io�I�&�P[K�h��"E���������CK���b�
y��GM9-Qr�������g�����;���o#��cG��?������������o����K�j���R\(�V���r:�fK
�-Z$���3eG���+�:u2=�3�{�n�S|���&Lo����!��H��������>���F�
�i��F�
�i��F�
�i��/����d��uv #�w��w�yGN�>m����d�����sg)Y��8y��%::�^��F�a�k���v���e�L�.�'�N�DFF��y���;]���#������K��=���3v �;vH�n�d��Mv����y����2g��Q��t��A.��	���o����x��*�(H�~!!!��d&11�g��'N��-[���d���`�>|�-Z�^�`j�������/�-9�}������.���������%�����t�M�5R�U�&}��5=���hzG����GK��
������;��C���;S[;#��q���
L�8�?��C���/��
3��Z��� ��k�^�q�����uIm��5���Z��^g�����z��U�m���������V�X�:��{�I�%�c�������Z�Zk8�\���}������{��W^��:��q��w��{��k���z����M3eM���Zs�y��d��}f��}%5�9�����������g����Jll����eV�;�}���{�����|����W/3��������M�(�V�C�3�v�/�����r�5Nz�������N?����]�5�}�������c7�>���������/�L����>���C�
��~e���o��~�L7k�����>��C����3f��eS����[��g����8SS����6!��1c��9/�pH���6��3�Q�re��[�����?�_�.R����U�<T����k����y�oz}7n�\w�u���oK�2e�uI�O?�������������������^�z�4n��\�4<������4���?�u0F�F�T
t;�^�tY�?�K�.m��aaaR�jU��K/�T���ZFDC�O>�D�W�n�u��E�8��H����#4xh���)���7�-[6�:��k�%_��+���)����>�=������'�xBBBB�}F�S�� ;�7�^���n0��������I�~��K�.&��	������x���������[���~3���GN
40��������^[�w��s�J�z������>2��O?�4�
�K��A�^��:��];3&����*����e�����{ws��Y��\|���������,KO�;z/����d������c��}@�G�F���.Jd)&&�J�EQ���{�=+�E{�e��x�
����o��~95�I�[I�[aaa���S���D��6n�h5k���\���j�*���v��e�l��l3y����q:���u���?����v��_~�����K�����Hs������o�n��l���J���\�f��a��l����Z�N��[-3�m�����n��J���
2�>��u��a��t����2��j�;w�^bY[�n����g=���V\\���s�k�>^|�Es�Pz��-��V�j��k��5�j��
V�����e�Z����\���������~j��t����fj��MK>��?�8�^o�sr_�eo����������O{��~8n�8�����k�<y�^��~�1�et��6m2��m��f����nu�\G�i�0`@��*�y��7�����~��%�g����_���>��Yc�������G���)2Z��:�����}���{��1�L��V�P!�}F���c|��g��*���_�u[��.���{��,�=�����B{R�#u}T���^���u��_�M���g��~�P{��5x�5�H�>}L���;w��"�g�6=�~�i���w�������s��z������@~q:��~�z�Q��<��C�9���k��w�i��m�f���C�L�5�����X��MIDAT���*f��G1��\{��M�6����?�-��?��|���]�����M�`���W���~}\��hoj������^���n9x��������`�_|���I��SO������:p���c����3����f���\����=HeN�+Z:D__{/j�k���������t�k����x�=�����^�nzlz�z.'O��S�N�K�;�^���/&�g�����\;���#���3�����
������.��������R�g�z����s��5111�=5�FT���n����{�~Sg������ �xD�"�_Y�?��k���/��_"�M
��7���hk��%P�����C��������V-kd�S�[��	&t��@A��4H~��S�"#�Vr��Zo���6�i�i�Vj�'�����^����R�J�-�
*�p\�T
��Z�&M��7�=�}^�� �����~�\Ci]'=-�a���� 4+F�osz_�!���ZZ��+�0m�/^<�k�Z*&����~�����N�����~��|���i>�o�0Y�)���Z�D��z��������NM�n�e���g����������������}������~j�R�A7 GN�>mz.Y�D�|�M38V��x���k��&$��7LXP�N�c�������_R����x��'M����=�5�Y�n]���P��{�+Yg���=�3D�E������MOh�]�w�k[{{��C\{�i/:5u�������j*u����"�K��A��
�P��K.�[��YkRk���_��Y��_��rz_q��|������vz�S���{bv��P�:����t�:���/����M�M���q��\�os��
��.���������fD�m���z��3
�?t<����������
:������{�A���k�:����eJ�+�:8����d��a&�P����>k���O�����x� �PZ�����:�����L��s���k���t`��'�mkP��gO���~h�u�V������J{+gt���.�E�!�������S9���C�ge��>��yNi�?z�hS�E{�����R��FK�����4�m}��d�mS����
�������E�y����t�RhO8��zF���������h���kP�=����P��x���F��Z{����@A��Gn��6b6m�T^}�US�b����������^�}�QS�C{3k��{�����\k�j��,��1cF�����^x���+4�w��N�u����}�]e������1i�$\g�0���z?\�`������������o�����)�a�7��
�n�G-Zdj
o~��'���#<��	�5D=�EX�F�A���o�SQ��jx^�ti�H��=eI�"@��w�1���Z�b�����~�[����Z�T�1�%-�L�bz����jI
����=�;�����3�}=�������w�6�i��O���������$9�=��f�~�Ik�k���nt`S�&�gD��z-���^�=�e�r���'���w!=�9��9��@�E�
��-J{�U�X�L��_�_�j�=����j�����;�w��9�������h�Z�
��C��Q�5r�!��sg�@A��>���5��z�~P-�K?8LM��j�k��0��
5@�R��=���j��?�����>��#���=�

t��?���??����5&��z�B�9���kZBF{�gt���5j�������v��	&�����/��:�gf5� ��=+�`����?�h��Z�P?TT���v��{���GM��n�Wf>�6=6��$���z.Z~J��z��_���F����{vhh��,jY
R�@\�2��������{�����?�5@���k���5�A�4\�@�J=`�v�%4��v���=���cY�m��V��{-����S��Z�Ik2k���{��K\���2���n�p� V�^h�0x��4�e������f K�:y����%)�Z�=���O�n�L���^{��z?Pz�z��������?�,��}��W{U�����;,����Y�����3=�����?��f��m1v�X�����1:0�~�!�~*=
o�7�W�:�%��N�`O?@��������?�����Z��@-����_���z�����u�F�9���9iG�������#I�$��C�Y��v��em���Y�[����oo%�!n����~����2�V�XaoeY�W�����g�)[����]�4�u�����m����O?�d%�qo��z�����[��w�n�'�i���y
�������5}�����^��:��y���^x��\����_?����f��G�Z�{�6��C���4h`��Z8k�,�7���P�gt]����ijG��:v�h����5�_O����_6������6�^������8S������c�s���[�,]��,��L����S��}%&&�z����r}�&������O?M�3�����������C�&�[�=��=�	&�yt?���Y�3�JHH��@�r_���ys�t_;S��-Z����Z�����z����^�"""�}O�����[�n5m�]����v��f��V�.]��z����7�|��{�n�>�B�n�G�w�|`����Nu�,�U��K�������mkzu�|�=�l�_Q�7o���x��W�����t���5��Ww�/�f]�6m������+����)�^s��d�����O����e����k�������.�6��v�]w��$���eE�[/:���(��ZR$��~3���+��\�>�>}�t����������d����x�bI��=M)�~��'2s�Ls}���^/����O�N��45�!���/�����^��{L===����������u0F=���z����[rr_�o$i�s]�{���?�?����:���SZv��a�����_z��o����_�{���;�{�='u�f�=X{~���K;v4��k�-���'�|�\�o������z]��uz�!S�K��������O<�<�r^����
��M:��^��������g����X
&����4������g���N����2�����F�
�i�@!�_����Zc5;�F�

jt�TLL�L�6MN�<i�x�T�R��W/	

�[�>jt�A7��Q����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>�����>������<��H����c��e���1B�L�>�n��p:�)����[r������g� �>��s���%::�n�7t@6%$$�Sy�g��bY�y��t|A7
-w��e����������r����No�����MO�3�X�B�q�����>���m[y��w������{��+��"
64��V�����7�cR�������On��&)Y��y���Y�$>>�^���u��M�&s���k����<������^K���#2z��l�{�{��}{���/����6���[��B��{|�3�����;�0������3�����g�^���>��z,���{�4(�=p��{�cH-}�n�kk��Z^�������/!�@���?H���e���R�^=�Z��|��G��uk���O��Z�b��qr�u���o�-e���:H������~�����PX��������M�6��/H\\����e��?4���k��W:C_�O�>����v�C��u�&4A�����+y��'$$$D���j�mhh�Y�����~��g���c&��\�r����?�9���l�"��r�����3f����K��]e����<sKv���3�������3������n��V������r���J�
��>���K�6���>������K�%�v�����0��.:�_[�K��s���z�����[N~^>'�)�Pz���5U4�|�:|��iOLL����k�j����f���"##���0+""���}����y�f�]�vf3f��[-k���V�z��6G��[S^��iv�em����]��U�lY�����u����t:��K���������]�:�
:�<������n��,�<y���9=]W��y/T||�y��k���eKs�����n�:�C�f��L�:v����{w��_|����1������N�j�U�y���������M��{����_n����#�k0 y_��>evl��9�w�}����E���(}�ymo����s�N�����/N���;t�������
�B�]�v���/J�����������%�=��������NLL4�%��_/5j���z�<�V�vm���;���m��sv�k|�����SO=%��{��eZn�e��2v�X3?s�L9~����2���;�<�%�\"C�5=�8 g����\��������{��-j�g��mz�?�����w�4��e1�=���������_~1=�����{�^�*((Hz��a�5����N�8a�s����6���>��8y���:u�^�}g����J��5�n���x�>_���'����3�����K^z�%�[[{���C�����������@85
�|��2Y�f�	5�����v���^3-wy�����2�j�h/I��Y3S�b����s�N�������M���A�[��MM��e8��uV��E�s����_�������T��!��Z�\�]�z�j�mxx�)���������?���+V���Fk����i_}?���[�����|��]z,aaa�j�*���+�J.S������*?����0#;�X������Z�������E�(����{*��.�H*U�d��:=������7�����������������9}��V�xqS�YCI�����������{
�Sz�����H#O>��	��?t0�M�6��u�L�p�������WF�\3[�99>U�N�_f�z��`��w�5������R�J��.R��2��F�F����A��=�S���:�����_�~��c��	�s�~�"�nz:0�������������iP�=��N�*�������#��+Z��������>/Y�D.\x�#2229xW�~iI���9'rr|J?<=z�)���u{-���q�j�����/��@�����:�i�>}���m��+��^�W^y�	������/L�E��hO��n�M�
�\�-���/"�@��b=��%4X�PTK�h���.6o�lz�������yC{��ksW�P�<����a��D�R)�^��e8�,�;����3�y%'������v�j�_�Y�`�����T�Z���}��5?�B{�k)-]�=���]+c��1A����O�6�^������^�YF�%+V����'���!�@���G2r��QSK�J�*&T�	�T���&M�o�����ZcZ����	��+W��)i����eI�����w�6�i�`��w��e��t�C-�q�}���(9U�tiS�Z������@�R+JKddDKkh���H�����2�=�;t� o����]�!���Z��E�
�u�H�-�5����?}������3�<c�5(O=�dj��~��nz4�:u��s��X{�j���d��&����ZOZC����u�-���O�&M����?~������X{koc��qc|kY
����Y��:�z9���h�����k���������s���<������F����}���3H�}~����sz|&L0�}���f>5@���.��rNk`�`�<�<�rZ�F
{��4�~��g���Z�U�V�������E�(��L�"���7���>����%�@�g�������������D���)}
���=t_{�5�/w(�A����X������t0����[~��Gy��������u�����jJsh����`��[n1�64~��7��3��N�{�a�W\��{�������v��}~����|z�
��4�/��b��Bn=O=��rz|Z������c��o���&T�	��5t�U����B���!|F�g��|'O�,�}�Y�;DO�>�L��>� �nz.Zg��������R'�������II�����/��	�u�wXU�V5����[
40�:��7�XN�������O������Z�jY:t��7o�<��/X�+W����g�>}�l�u�V�^�zVDD�u��Q����?m�4����f�2���5����=v�X���s�.���w�n�)[����];�u��f}}L�0��m2�����c��O?�d�K�s�g���6==��2:>�9u����Y��y�O\\���c��ut��������g�1�R�*����3t�P�Lz\���K�.5���3f����`/�����������A�m���{����k��,��7c�������c��1g��������t��r�S�N���[g?
�4|��'�]�v�k�HLL�~�����oO
���z�jH��_Ym����5kf����l����M_����O�����Z�~}r��^LL�	�����6�����Z�d�9nOxt�X�F�J�w������/�3g��k���7q�D���6����������2|�����y�u
l�u5N������o��6�C�F�x���s
�'M�� kH}��){i�t�7o���q�??��'���IN~^����I�E(t������6m�)O��:�w�y�T�T����1@vP����>�����>��(>���F�
�i�&�s��R5�PnIEND�B`�
0001-Modify-AllocSetAlloc-not-to-look-at-freelists.patch.txttext/plain; charset=US-ASCII; name=0001-Modify-AllocSetAlloc-not-to-look-at-freelists.patch.txtDownload
From 774edcd23cc68b526bbb971db55f6eb30bf8a763 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Mon, 25 Mar 2024 22:04:51 +1300
Subject: [PATCH 1/2] Modify AllocSetAlloc not to look at freelists

---
 src/backend/utils/mmgr/aset.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index 751cc3408c..c683e2a928 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -998,6 +998,7 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 	 * doubling the memory requirements for such allocations.
 	 */
 	fidx = AllocSetFreeIndex(size);
+#ifdef ALLOCSET_USE_FREELIST
 	chunk = set->freelist[fidx];
 	if (chunk != NULL)
 	{
@@ -1033,6 +1034,7 @@ AllocSetAlloc(MemoryContext context, Size size, int flags)
 
 		return MemoryChunkGetPointer(chunk);
 	}
+#endif
 
 	/*
 	 * Choose the actual chunk size to allocate.
-- 
2.40.1.windows.1

#30Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: David Rowley (#29)
Re: Add bump memory context type and use it for tuplesorts

On 3/25/24 12:41, David Rowley wrote:

On Tue, 5 Mar 2024 at 15:42, David Rowley <dgrowleyml@gmail.com> wrote:

The query I ran was:

select chksz,mtype,pg_allocate_memory_test_reset(chksz,
1024*1024,1024*1024*1024, mtype) from (values(8),(16),(32),(64))
sizes(chksz),(values('aset'),('generation'),('slab'),('bump'))
cxt(mtype) order by mtype,chksz;

Andres and I were discussing this patch offlist in the context of
"should we have bump". Andres wonders if it would be better to have a
function such as palloc_nofree() (we didn't actually discuss the
name), which for aset, would forego rounding up to the next power of 2
and not bother checking the freelist and only have a chunk header for
MEMORY_CONTEXT_CHECKING builds. For non-MEMORY_CONTEXT_CHECKING
builds, the chunk header could be set to some other context type such
as one of the unused ones or perhaps a dedicated new one that does
something along the lines of BogusFree() which raises an ERROR if
anything tries to pfree or repalloc it.

An advantage of having this instead of bump would be that it could be
used for things such as the parser, where we make a possibly large
series of small allocations and never free them again.

I may be missing something, but I don't quite understand how this would
be simpler to use in places like parser. Wouldn't it require all the
places to start explicitly calling palloc_nofree()? How is that better
than having a specialized memory context?

Andres ask me to run some benchmarks to mock up AllocSetAlloc() to
have it not check the freelist to see how the performance of it
compares to BumpAlloc(). I did this in the attached 2 patches. The
0001 patch just #ifdefs that part of AllocSetAlloc out, however
properly implementing this is more complex as aset.c currently stores
the freelist index in the MemoryChunk rather than the chunk_size. I
did this because it saved having to call AllocSetFreeIndex() in
AllocSetFree() which made a meaningful performance improvement in
pfree(). The 0002 patch effectively reverses that change out so that
the chunk_size is stored. Again, these patches are only intended to
demonstrate the performance and check how it compares to bump.

I'm yet uncertain why, but I find that the first time I run the query
quoted above, the aset results are quite a bit slower than on
subsequent runs. Other context types don't seem to suffer from this.
The previous results I sent in [1] were of the initial run after
starting up the database.

The attached graph shows the number of seconds it takes to allocate a
total of 1GBs of memory in various chunk sizes, resetting the context
after 1MBs has been allocated, so as to keep the test sized so it fits
in CPU caches.

Yeah, strange and interesting. My guess is it's some sort of caching
effect, where the first run has to initialize stuff that's not in any of
the CPU caches yet, likely something specific to AllocSet (freelist?).
Or maybe memory prefetching does not work that well for AllocSet?

I'd try perf-stat, that might tell us more ... but who knows.

Alternatively, it might be some interaction with the glibc allocator.
Have you tried using jemalloc using LD_PRELOAD, or tweaking the glibc
parameters using environment variables? If you feel adventurous, you
might even try the memory pool stuff, although I'm not sure that can
help with the first run.

I'm not drawing any particular conclusion from the results aside from
it's not quite as fast as bump. I also have some reservations about
how easy it would be to actually use something like palloc_nofree().
For example heap_form_minimal_tuple() does palloc0(). What if I
wanted to call ExecCopySlotMinimalTuple() and use palloc0_nofree().
Would we need new versions of various functions to give us control
over this?

That's kinda the problem that I mentioned above - is this really any
simpler/better than just having a separate memory context type? I don't
see what benefits this is supposed to have.

IMHO the idea of having a general purpose memory context and then also
specialized memory contexts for particular allocation patterns is great,
and we should embrace it. Adding more and more special cases into
AllocSet seems to go directly against that idea, makes the code more
complex, and I don't quite see how is that better or easier to use than
having a separate BumpContext ...

Having an AllocSet that mixes chunks that may be freed and chunks that
can't be freed, and have a different context type in the chunk header,
seems somewhat confusing and "not great" for debugging, for example.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#31Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tomas Vondra (#30)
Re: Add bump memory context type and use it for tuplesorts

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

IMHO the idea of having a general purpose memory context and then also
specialized memory contexts for particular allocation patterns is great,
and we should embrace it. Adding more and more special cases into
AllocSet seems to go directly against that idea, makes the code more
complex, and I don't quite see how is that better or easier to use than
having a separate BumpContext ...

I agree with this completely. However, the current design for chunk
headers is mighty restrictive about how many kinds of contexts we can
have. We need to open that back up.

Could we move the knowledge of exactly which context type it is out
of the per-chunk header and keep it in the block header? This'd
require that every context type have a standardized way of finding
the block header from a chunk. We could repurpose the existing
MemoryContextMethodID bits to allow having a small number of different
ways, perhaps.

regards, tom lane

#32David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#31)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 26 Mar 2024 at 03:53, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I agree with this completely. However, the current design for chunk
headers is mighty restrictive about how many kinds of contexts we can
have. We need to open that back up.

Andres mentioned how we could do this in [1]/messages/by-id/20240217200845.ywlwenjrlbyoc73v@awork3.anarazel.de. One possible issue with
that is that slab.c has no external chunks so would restrict slab to
512MB chunks. I doubt that's ever going to realistically be an issue.
That's just not a good use case for slab, so I'd be ok with that.

Could we move the knowledge of exactly which context type it is out
of the per-chunk header and keep it in the block header? This'd
require that every context type have a standardized way of finding
the block header from a chunk. We could repurpose the existing
MemoryContextMethodID bits to allow having a small number of different
ways, perhaps.

I wasn't 100% clear on your opinion about using 010 vs expanding the
bit-space. Based on the following it sounded like you were not
outright rejecting the idea of consuming the 010 pattern.

On Sat, 17 Feb 2024 at 12:14, Tom Lane <tgl@sss.pgh.pa.us> wrote:

If we do kick this can down the road, then I concur with eating 010
next, as it seems the least likely to occur in glibc-malloced
chunks.

David

[1]: /messages/by-id/20240217200845.ywlwenjrlbyoc73v@awork3.anarazel.de

#33Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#32)
Re: Add bump memory context type and use it for tuplesorts

David Rowley <dgrowleyml@gmail.com> writes:

On Tue, 26 Mar 2024 at 03:53, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Could we move the knowledge of exactly which context type it is out
of the per-chunk header and keep it in the block header?

I wasn't 100% clear on your opinion about using 010 vs expanding the
bit-space. Based on the following it sounded like you were not
outright rejecting the idea of consuming the 010 pattern.

What I said earlier was that 010 was the least bad choice if we
fail to do any expansibility work; but I'm not happy with failing
to do that.

Basically, I'm not happy with consuming the last reasonably-available
pattern for a memory context type that has little claim to being the
Last Context Type We Will Ever Want. Rather than making a further
dent in our ability to detect corrupted chunks, we should do something
towards restoring the expansibility that existed in the original
design. Then we can add bump contexts and whatever else we want.

regards, tom lane

#34Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: Tom Lane (#33)
1 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Mon, 25 Mar 2024 at 22:44, Tom Lane <tgl@sss.pgh.pa.us> wrote:

David Rowley <dgrowleyml@gmail.com> writes:

On Tue, 26 Mar 2024 at 03:53, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Could we move the knowledge of exactly which context type it is out
of the per-chunk header and keep it in the block header?

I wasn't 100% clear on your opinion about using 010 vs expanding the
bit-space. Based on the following it sounded like you were not
outright rejecting the idea of consuming the 010 pattern.

What I said earlier was that 010 was the least bad choice if we
fail to do any expansibility work; but I'm not happy with failing
to do that.

Okay.

Basically, I'm not happy with consuming the last reasonably-available
pattern for a memory context type that has little claim to being the
Last Context Type We Will Ever Want. Rather than making a further
dent in our ability to detect corrupted chunks, we should do something
towards restoring the expansibility that existed in the original
design. Then we can add bump contexts and whatever else we want.

So, would something like the attached make enough IDs available so
that we can add the bump context anyway?

It extends memory context IDs to 5 bits (32 values), of which
- 8 have glibc's malloc pattern of 001/010;
- 1 is unused memory's 00000
- 1 is wipe_mem's 11111
- 4 are used by existing contexts (Aset/Generation/Slab/AlignedRedirect)
- 18 are newly available.

Kind regards,

Matthias

Attachments:

v0-0001-Add-bitspace-for-more-memory-context-types-in-Mem.patch.txttext/plain; charset=US-ASCII; name=v0-0001-Add-bitspace-for-more-memory-context-types-in-Mem.patch.txtDownload
From 4c0b4b1af98fcfecf80a30ea1834668b59d543a5 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Thu, 4 Apr 2024 21:34:46 +0200
Subject: [PATCH v0] Add bitspace for more memory context types in
 MemoryChunk's hdrmask

Assuming we don't want to use patterns from unused memory, common
glibc malloc patterns, and wipe_mem-ed memory, we now have 18 new IDs
to work with again, from 0.

In passing, simplify/clean up initialization of unused memory contexts
in the mcxt_methods array.

Inspiration: https://postgr.es/m/CAApHDvqGSpCU95TmM%3DBp%3D6xjL_nLys4zdZOpfNyWBk97Xrdj2w%40mail.gmail.com
---
 src/backend/utils/mmgr/mcxt.c            | 55 +++++++++++++++---------
 src/include/utils/memutils_internal.h    | 35 ++++++++++++---
 src/include/utils/memutils_memorychunk.h | 15 ++++---
 3 files changed, 73 insertions(+), 32 deletions(-)

diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 5d426795d9..9373f782ce 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -102,26 +102,41 @@ static const MemoryContextMethods mcxt_methods[] = {
 	 * seems sufficient to provide routines for the methods that might get
 	 * invoked from inspection of a chunk (see MCXT_METHOD calls below).
 	 */
-
-	[MCTX_UNUSED1_ID].free_p = BogusFree,
-	[MCTX_UNUSED1_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED1_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED1_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED2_ID].free_p = BogusFree,
-	[MCTX_UNUSED2_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED2_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED2_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED3_ID].free_p = BogusFree,
-	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
+#define BOGUS_MCTX(id) \
+	[id].free_p = BogusFree, \
+	[id].realloc = BogusRealloc, \
+	[id].get_chunk_context = BogusGetChunkContext, \
+	[id].get_chunk_space = BogusGetChunkSpace
+
+	BOGUS_MCTX(MCTX_UNUSED1_ID),
+	BOGUS_MCTX(MCTX_UNUSED2_ID),
+	BOGUS_MCTX(MCTX_UNUSED3_ID),
+	BOGUS_MCTX(MCTX_UNUSED4_ID),
+	BOGUS_MCTX(MCTX_UNUSED5_ID),
+	BOGUS_MCTX(MCTX_UNUSED6_ID),
+	BOGUS_MCTX(MCTX_UNUSED7_ID),
+	BOGUS_MCTX(MCTX_UNUSED8_ID),
+	BOGUS_MCTX(MCTX_UNUSED9_ID),
+	BOGUS_MCTX(MCTX_UNUSED10_ID),
+	BOGUS_MCTX(MCTX_UNUSED11_ID),
+	BOGUS_MCTX(MCTX_UNUSED12_ID),
+	BOGUS_MCTX(MCTX_UNUSED13_ID),
+	BOGUS_MCTX(MCTX_UNUSED14_ID),
+	BOGUS_MCTX(MCTX_UNUSED15_ID),
+	BOGUS_MCTX(MCTX_UNUSED16_ID),
+	BOGUS_MCTX(MCTX_UNUSED17_ID),
+	BOGUS_MCTX(MCTX_UNUSED18_ID),
+	BOGUS_MCTX(MCTX_UNUSED19_ID),
+	BOGUS_MCTX(MCTX_UNUSED20_ID),
+	BOGUS_MCTX(MCTX_UNUSED21_ID),
+	BOGUS_MCTX(MCTX_UNUSED22_ID),
+	BOGUS_MCTX(MCTX_UNUSED23_ID),
+	BOGUS_MCTX(MCTX_UNUSED24_ID),
+	BOGUS_MCTX(MCTX_UNUSED25_ID),
+	BOGUS_MCTX(MCTX_UNUSED26_ID),
+	BOGUS_MCTX(MCTX_UNUSED27_ID),
+	BOGUS_MCTX(MCTX_UNUSED28_ID)
+#undef BOGUS_MCTX
 };
 
 /*
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index ad1048fd82..fbf5ecd111 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -104,21 +104,46 @@ extern Size AlignedAllocGetChunkSpace(void *pointer);
  */
 typedef enum MemoryContextMethodID
 {
-	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
-	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_UNUSED1_ID,			/* 00000 occurs in never-used memory */
+	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match XX001 */
+	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match XX010 */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID,			/* 111 occurs in wipe_mem'd memory */
+	MCTX_UNUSED4_ID,			/* 00111 */
+	MCTX_UNUSED5_ID,			/* 01000 */
+	MCTX_UNUSED6_ID,			/* glibc malloc'd chunks usually match XX001 */
+	MCTX_UNUSED7_ID,			/* glibc malloc'd chunks > 128kB match XX010 */
+	MCTX_UNUSED8_ID,			/* 01011 */
+	MCTX_UNUSED9_ID,			/* 01100 */
+	MCTX_UNUSED10_ID,			/* 01101 */
+	MCTX_UNUSED11_ID,			/* 01110 */
+	MCTX_UNUSED12_ID,			/* 01111 */
+	MCTX_UNUSED13_ID,			/* 10000 */
+	MCTX_UNUSED14_ID,			/* glibc malloc'd chunks usually match XX001 */
+	MCTX_UNUSED15_ID,			/* glibc malloc'd chunks > 128kB match XX010 */
+	MCTX_UNUSED16_ID,			/* 10011 */
+	MCTX_UNUSED17_ID,			/* 10100 */
+	MCTX_UNUSED18_ID,			/* 10101 */
+	MCTX_UNUSED19_ID,			/* 10110 */
+	MCTX_UNUSED20_ID,			/* 10111 */
+	MCTX_UNUSED21_ID,			/* 11000 */
+	MCTX_UNUSED22_ID,			/* glibc malloc'd chunks usually match XX001 */
+	MCTX_UNUSED23_ID,			/* glibc malloc'd chunks > 128kB match XX010 */
+	MCTX_UNUSED24_ID,			/* 11011 */
+	MCTX_UNUSED25_ID,			/* 11100 */
+	MCTX_UNUSED26_ID,			/* 11101 */
+	MCTX_UNUSED27_ID,			/* 11110 */
+	MCTX_UNUSED28_ID			/* 11111 occurs in wipe_mem'd memory (0x7F) */
+#define MaxMemoryContextMethodID (MCTX_UNUSED28_ID)
 } MemoryContextMethodID;
 
 /*
  * The number of bits that 8-byte memory chunk headers can use to encode the
  * MemoryContextMethodID.
  */
-#define MEMORY_CONTEXT_METHODID_BITS 3
+#define MEMORY_CONTEXT_METHODID_BITS 5
 #define MEMORY_CONTEXT_METHODID_MASK \
 	((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
 
diff --git a/src/include/utils/memutils_memorychunk.h b/src/include/utils/memutils_memorychunk.h
index 38296abe1b..86ed28afab 100644
--- a/src/include/utils/memutils_memorychunk.h
+++ b/src/include/utils/memutils_memorychunk.h
@@ -25,14 +25,14 @@
  * used to encode 4 separate pieces of information.  Starting with the least
  * significant bits of 'hdrmask', the bit space is reserved as follows:
  *
- * 1.	3-bits to indicate the MemoryContextMethodID as defined by
+ * 1.	5-bits to indicate the MemoryContextMethodID as defined by
  *		MEMORY_CONTEXT_METHODID_MASK
  * 2.	1-bit to denote an "external" chunk (see below)
  * 3.	30-bits reserved for the MemoryContext to use for anything it
  *		requires.  Most MemoryContext likely want to store the size of the
  *		chunk here.
- * 4.	30-bits for the number of bytes that must be subtracted from the chunk
- *		to obtain the address of the block that the chunk is stored on.
+ * 4.	28-bits for the multiple of 4 bytes that must be subtracted from the
+ *		chunk to obtain the address of the block that the chunk is stored on.
  *
  * In some cases, for example when memory allocations become large, it's
  * possible fields 3 and 4 above are not large enough to store the values
@@ -91,7 +91,7 @@
  * The maximum distance in bytes that a MemoryChunk can be offset from the
  * block that is storing the chunk.  Must be 1 less than a power of 2.
  */
-#define MEMORYCHUNK_MAX_BLOCKOFFSET		UINT64CONST(0x3FFFFFFF)
+#define MEMORYCHUNK_MAX_BLOCKOFFSET		UINT64CONST(0xFFFFFFF)
 
 /* define the least significant base-0 bit of each portion of the hdrmask */
 #define MEMORYCHUNK_EXTERNAL_BASEBIT	MEMORY_CONTEXT_METHODID_BITS
@@ -135,7 +135,7 @@ typedef struct MemoryChunk
  * optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
  */
 #define HdrMaskBlockOffset(hdrmask) \
-	(((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
+	((((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET) * 4)
 
 /* For external chunks only, check the magic number matches */
 #define HdrMaskCheckMagic(hdrmask) \
@@ -157,11 +157,12 @@ MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
 	Size		blockoffset = (char *) chunk - (char *) block;
 
 	Assert((char *) chunk >= (char *) block);
-	Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+	Assert((blockoffset % 4) == 0);
+	Assert((blockoffset / 4) <= MEMORYCHUNK_MAX_BLOCKOFFSET);
 	Assert(value <= MEMORYCHUNK_MAX_VALUE);
 	Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
 
-	chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
+	chunk->hdrmask = (((uint64) (blockoffset / 4)) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
 		(((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) |
 		methodid;
 }
-- 
2.40.1

#35Tom Lane
tgl@sss.pgh.pa.us
In reply to: Matthias van de Meent (#34)
Re: Add bump memory context type and use it for tuplesorts

Matthias van de Meent <boekewurm+postgres@gmail.com> writes:

On Mon, 25 Mar 2024 at 22:44, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Basically, I'm not happy with consuming the last reasonably-available
pattern for a memory context type that has little claim to being the
Last Context Type We Will Ever Want. Rather than making a further
dent in our ability to detect corrupted chunks, we should do something
towards restoring the expansibility that existed in the original
design. Then we can add bump contexts and whatever else we want.

So, would something like the attached make enough IDs available so
that we can add the bump context anyway?

It extends memory context IDs to 5 bits (32 values), of which
- 8 have glibc's malloc pattern of 001/010;
- 1 is unused memory's 00000
- 1 is wipe_mem's 11111
- 4 are used by existing contexts (Aset/Generation/Slab/AlignedRedirect)
- 18 are newly available.

This seems like it would solve the problem for a good long time
to come; and if we ever need more IDs, we could steal one more bit
by requiring the offset to the block header to be a multiple of 8.
(Really, we could just about do that today at little or no cost ...
machines with MAXALIGN less than 8 are very thin on the ground.)

The only objection I can think of is that perhaps this would slow
things down a tad by requiring more complicated shifting/masking.
I wonder if we could redo the performance checks that were done
on the way to accepting the current design.

regards, tom lane

#36Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: Tom Lane (#35)
5 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Thu, 4 Apr 2024 at 22:43, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Matthias van de Meent <boekewurm+postgres@gmail.com> writes:

On Mon, 25 Mar 2024 at 22:44, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Basically, I'm not happy with consuming the last reasonably-available
pattern for a memory context type that has little claim to being the
Last Context Type We Will Ever Want. Rather than making a further
dent in our ability to detect corrupted chunks, we should do something
towards restoring the expansibility that existed in the original
design. Then we can add bump contexts and whatever else we want.

So, would something like the attached make enough IDs available so
that we can add the bump context anyway?

It extends memory context IDs to 5 bits (32 values), of which
- 8 have glibc's malloc pattern of 001/010;
- 1 is unused memory's 00000
- 1 is wipe_mem's 11111
- 4 are used by existing contexts (Aset/Generation/Slab/AlignedRedirect)
- 18 are newly available.

This seems like it would solve the problem for a good long time
to come; and if we ever need more IDs, we could steal one more bit
by requiring the offset to the block header to be a multiple of 8.
(Really, we could just about do that today at little or no cost ...
machines with MAXALIGN less than 8 are very thin on the ground.)

Hmm, it seems like a decent idea, but I didn't want to deal with the
repercussions of that this late in the cycle when these 2 bits were
still relatively easy to get hold of.

The only objection I can think of is that perhaps this would slow
things down a tad by requiring more complicated shifting/masking.
I wonder if we could redo the performance checks that were done
on the way to accepting the current design.

I didn't do very extensive testing, but the light performance tests
that I did with the palloc performance benchmark patch & script shared
above indicate didn't measure an observable negative effect.
An adapted version of the test that uses repalloc() to check
performance differences in MCXT_METHOD() doesn't show a significant
performance difference from master either. That test case is attached
as repalloc-performance-test-function.patch.txt.

The full set of patches would then accumulate to the attached v5 of
the patchset.
0001 is an update of my patch from yesterday, in which I update
MemoryContextMethodID infrastructure for more IDs, and use a new
naming scheme for unused/reserved IDs.
0002 and 0003 are David's patches, with minor changes to work with
0001 (rebasing, and I moved the location around to keep function
declaration in order with memctx ids)

Kind regards,

Matthias van de Meent

Attachments:

repalloc-performance-test-function.patch.txttext/plain; charset=US-ASCII; name=repalloc-performance-test-function.patch.txtDownload
From b65320736cf63684b4710b3d63e243a0862d37c6 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Fri, 5 Apr 2024 15:13:52 +0200
Subject: [PATCH] repalloc performance test function

---
 src/backend/utils/adt/mcxtfuncs.c | 231 ++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat   |  12 ++
 2 files changed, 243 insertions(+)

diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 4d4a70915b..88de4bbcb5 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -15,8 +15,11 @@
 
 #include "postgres.h"
 
+#include <time.h>
+
 #include "funcapi.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/builtins.h"
@@ -185,3 +188,231 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(true);
 }
+
+typedef struct AllocateTestNext
+{
+	struct AllocateTestNext *next;		/* ptr to the next allocation */
+} AllocateTestNext;
+
+/* #define ALLOCATE_TEST_DEBUG */
+/*
+ * pg_allocate_memory_test
+ *		Used to test the performance of a memory context types
+ */
+Datum
+pg_allocate_memory_test(PG_FUNCTION_ARGS)
+{
+	int32	chunk_size = PG_GETARG_INT32(0);
+	int64	keep_memory = PG_GETARG_INT64(1);
+	int64	total_alloc = PG_GETARG_INT64(2);
+	text   *context_type_text = PG_GETARG_TEXT_PP(3);
+	char   *context_type;
+	int64	curr_memory_use = 0;
+	int64	remaining_alloc_bytes = total_alloc;
+	MemoryContext context;
+	MemoryContext oldContext;
+	AllocateTestNext	   *next_free_ptr = NULL;
+	AllocateTestNext	   *last_alloc = NULL;
+	clock_t	start, end;
+
+	if (chunk_size < sizeof(AllocateTestNext))
+		elog(ERROR, "chunk_size (%d) must be at least %ld bytes", chunk_size,
+			 sizeof(AllocateTestNext));
+	if (keep_memory > total_alloc)
+		elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")",
+			 keep_memory, total_alloc);
+
+	context_type = text_to_cstring(context_type_text);
+
+	start = clock();
+
+	if (strcmp(context_type, "generation") == 0)
+		context = GenerationContextCreate(CurrentMemoryContext,
+										  "pg_allocate_memory_test",
+										  ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "aset") == 0)
+		context = AllocSetContextCreate(CurrentMemoryContext,
+										"pg_allocate_memory_test",
+										ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "slab") == 0)
+		context = SlabContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_MAXSIZE,
+									chunk_size);
+	else
+		elog(ERROR, "context_type must be \"generation\", \"aset\" or \"slab\"");
+
+	oldContext = MemoryContextSwitchTo(context);
+
+	while (remaining_alloc_bytes > 0)
+	{
+		AllocateTestNext *curr_alloc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/* Allocate the memory and update the counters */
+		curr_alloc = (AllocateTestNext *) palloc(chunk_size);
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		/*
+		 * Point the last allocate to this one so that we can free allocations
+		 * starting with the oldest first.
+		 */
+		curr_alloc->next = NULL;
+		if (last_alloc != NULL)
+			last_alloc->next = curr_alloc;
+
+		if (next_free_ptr == NULL)
+		{
+			/*
+			 * Remember the first chunk to free. We will follow the ->next
+			 * pointers to find the next chunk to free when freeing memory
+			 */
+			next_free_ptr = curr_alloc;
+		}
+
+		/*
+		 * If the currently allocated memory has reached or exceeded the amount
+		 * of memory we want to keep allocated at once then we'd better free
+		 * some.  Since all allocations are the same size we only need to free
+		 * one allocation per loop.
+		 */
+		if (curr_memory_use >= keep_memory)
+		{
+			AllocateTestNext	 *next = next_free_ptr->next;
+
+			/* free the memory and update the current memory usage */
+			pfree(next_free_ptr);
+			curr_memory_use -= chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+			elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes);
+#endif
+			/* get the next chunk to free */
+			next_free_ptr = next;
+		}
+
+		if (curr_memory_use > 0)
+			last_alloc = curr_alloc;
+		else
+			last_alloc = NULL;
+	}
+
+	/* cleanup loop -- pfree remaining memory */
+	while (next_free_ptr != NULL)
+	{
+		AllocateTestNext	 *next = next_free_ptr->next;
+
+		/* free the memory and update the current memory usage */
+		pfree(next_free_ptr);
+		curr_memory_use -= chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes);
+#endif
+
+		next_free_ptr = next;
+	}
+
+	MemoryContextSwitchTo(oldContext);
+
+	end = clock();
+
+	PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC);
+}
+
+Datum
+pg_allocate_memory_test_reset(PG_FUNCTION_ARGS)
+{
+	int32	chunk_size = PG_GETARG_INT32(0);
+	int64	keep_memory = PG_GETARG_INT64(1);
+	int64	total_alloc = PG_GETARG_INT64(2);
+	int64	n_ptrs;
+	text   *context_type_text = PG_GETARG_TEXT_PP(3);
+	char   *context_type;
+	int64	curr_memory_use = 0;
+	int64	remaining_alloc_bytes = total_alloc;
+	MemoryContext context;
+	MemoryContext oldContext;
+	clock_t	start, end;
+	int64 **ptrs;
+	uint32	lcg_value = 0;
+	uint32	lcg_c = 1013904223;
+	uint32	lcg_a = 1664525;
+
+	if (chunk_size < 1)
+		elog(ERROR, "size of chunk must be above 0");
+	if (keep_memory > total_alloc)
+		elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")",
+			 keep_memory, total_alloc);
+
+	context_type = text_to_cstring(context_type_text);
+	n_ptrs = 1 << pg_leftmost_one_pos64(keep_memory / chunk_size);
+
+	ptrs = (int64 **) palloc0(n_ptrs * sizeof(int64 *));
+
+	start = clock();
+
+	if (strcmp(context_type, "generation") == 0)
+		context = GenerationContextCreate(CurrentMemoryContext,
+										  "pg_allocate_memory_test",
+										  ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "aset") == 0)
+		context = AllocSetContextCreate(CurrentMemoryContext,
+										"pg_allocate_memory_test",
+										ALLOCSET_DEFAULT_SIZES);
+	else if (strcmp(context_type, "slab") == 0)
+		context = SlabContextCreate(CurrentMemoryContext,
+									"pg_allocate_memory_test",
+									ALLOCSET_DEFAULT_MAXSIZE,
+									chunk_size);
+//	else if (strcmp(context_type, "bump") == 0)
+//		context = BumpContextCreate(CurrentMemoryContext,
+//									"pg_allocate_memory_test",
+//									ALLOCSET_DEFAULT_SIZES);
+	else
+		elog(ERROR, "context_type must be \"generation\", \"aset\" or \"slab\"");
+
+	oldContext = MemoryContextSwitchTo(context);
+
+	for (int i = 0; i < n_ptrs && remaining_alloc_bytes > 0;)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		/* Allocate the memory and update the counters */
+		ptrs[i++] = (int64 *) palloc(chunk_size);
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+	}
+
+	while (remaining_alloc_bytes > 0)
+	{
+		uint32	index;
+		CHECK_FOR_INTERRUPTS();
+		index = lcg_value % (uint32) n_ptrs;
+		lcg_value = lcg_value * lcg_a + lcg_c;
+		Assert(PointerIsValid(ptr));
+
+		/* reallocate, and update the counters */
+		ptrs[index] = (int64 *) repalloc(ptrs[index], chunk_size);
+
+		remaining_alloc_bytes -= chunk_size;
+		curr_memory_use += chunk_size;
+
+#ifdef ALLOCATE_TEST_DEBUG
+		elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes);
+#endif
+	}
+
+	MemoryContextSwitchTo(oldContext);
+	MemoryContextDelete(context);
+
+	end = clock();
+
+	PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 153d816a05..15d191db8b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8288,6 +8288,18 @@
   prorettype => 'bool', proargtypes => 'int4',
   prosrc => 'pg_log_backend_memory_contexts' },
 
+# just for testing memory context allocation speed
+{ oid => '9319', descr => 'for testing performance of allocation and freeing',
+  proname => 'pg_allocate_memory_test', provolatile => 'v',
+  prorettype => 'float8', proargtypes => 'int4 int8 int8 text',
+  prosrc => 'pg_allocate_memory_test' },
+
+# just for testing memory context allocation speed
+{ oid => '9320', descr => 'for testing performance of allocation and resetting',
+  proname => 'pg_allocate_memory_test_reset', provolatile => 'v',
+  prorettype => 'float8', proargtypes => 'int4 int8 int8 text',
+  prosrc => 'pg_allocate_memory_test_reset' },
+
 # non-persistent series generator
 { oid => '1066', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
-- 
2.40.1

image.pngimage/png; name=image.pngDownload
�PNG


IHDR����� IDATx^��
�]U}�������C���W�H �(&������[yl4�[��Z�VQp���`��3Z�x�^h������,��C��� P,�8{�����K`$3�9���2s^�>k�}�9g����<>�$k���g���ov��'��	_ �����?%��r����V�9����~�]�l�5y����&�  r���%C0�@�d	���_�� ��(��L����m��eff�jf,(�1����;����ATk�\�s��&�~��������8 ����m�f> �
.��s*@@8cn�.0@@���� � �6�m��� � �����@@���
��@@���^�d�e"���o\����� � � U@/���^���;�	�<��@@i�9���g�e0;$��^,@@@�	��w��a<�Yd(��c���� � �������r��"B@@ �hw��[�=c � � 0'!���-��g��������/�����X�p!��	�X�j�,[�R-`�K�N���GY�<��#���/6���?���T
,Y�D<
 0'��������w��� �j�`��g�{0���>���dttT6m��j\� �����+�m�6Y�n]��1����l��d���)D0��"� ��	�-���2�`�����	�-�OF@ u��m)
0��g��^>����
��G@� ��J�.���<����qd@\ ������ �S � `�����ar1
�c�d(@�_�`�)#�)@0�s_� �����+sWv�u"� �P�yB7�i����� �� ��S�kuR�`�I}�� �J����) ��d#Y �i ��ugYW����@@��Vo��Q�`#&C!� �@���M�N������@@�$@0�\ �����@��
��qL�X�`nLF@h�����\���N�sm@P
��D4H��<%�2@H��<�;��j�� �X-@0�z{�\��1
@� ��o��v
���f� �%�9�������f� �$T�`���c��sc2: � �@;����Z� �wR�k#� ��R�`�$�AJ�)�H�� �@Z�i�Y�U+@0�&@@�j������b ����P � ��<~SF�S�`n��0+@(	�)W���4�D@ ���n�6 ���@�)@0o�6�������\@�s%
R"@0O�F�@�*@0O����Z�95� �V���&��<FL�B@�����2��s;��Y!� �@I�`N)�"@0we�Y' �	 �'t�������� � �N�y;��V'����� � � �+�h��yJ6�e � �V�yZw�u�
��	@�Z�`n��0��1b2 ��/@0���� ���/�
@JsJ���+;�:@H��<������'��n�(_�K��=��]wjjJFGGe��Mfi� ��	���rgl���7�Y��q�JI|�7#�G��9���zc� ������P�`��g�{�W��1�A^GC0Oh�0m@: @0�:����f0/a���[��l	����@��n�������c,24.�~���\0�@�v��-��:%���~�v������\��yq��1���q]@� �������,��I�������
�������w��:���Y����v�i���~�X�d�<x@`N���099)�\r	*�Z�(����8�D��J������^�������}����������N5.�Cs������yGz �@j8 {��!��v�YXY@/���o���y�'� ��e���]�4�y�<��W����GY�I/�� �@����f��`.�7�Vg)���G�r��n0_��`�@����C�������9 �P�w%�;Ta,@�&�M�=1��<�u��qd@\ ������ �S � `�����ar1
�c�d(@�_�`�)#�)@0�s_� �����+sWv�u"� �P�yB7�i����� �� ��S�kuR�`�I}�� �J����)�2��������/;|���:��@@�^���{����2����N����+=��������@@�^���{��� ����h � ��<fP��V�`n��01@����+sWv�u"� �P�yB7�i����� �� ��S�kuR�`�I}�� �J����)HU0�����n8mk�\!r��h}�� �- �����-HU0���Cr��[��"��h���B@��	�[F���	��B0��4� �E�9�����`�J��N@��
��qL�X�`N07.: � �N�y;��V'��N��F@@)@0W� %s�yJJ�e ��U�`���e]�s�9�
@� �[�=L.F�9�<�rb(@� ��o��v
�	�vV&�B@�$@0�\ ��]�u�� �P�yB7�i�	��EC@�)@0o�6�����`����� �(�J"�D�`N0OI)�@��
������V�`^��gD.�<Z�,[��@@@)@0W� %��F��Qd�X�m=�'"�#��� �4 �S ��	���:�DH��<�����s��� �� ��S�kuR�`C0�y�Z94;c��]�����`�� ��$@0wi��^+�<�`�ul�L�0��O�R���v�~t@@�%��K���Z	�s�_�@�z���[�c ��c*%�A@�5���2�}s��}U��@� �S��	���:�DH��<�����s��� �� ��S�kuR�`N0�d�qm@�s%
R"���{3�y$h�=2��^���)�M�6�Zw��r�;F,6���Cr��[��:��Qd�X��<.1�@� �k1�(f�\�$7l����E�c��WK@h�����\��`�I�[���}���%��4@R)@0O�����y��,�@@�R�`n��0���l����L�M��|����,E@�����@ u��m)
���7wf6�HW��{�-V:�2a�	�s^� ��
�u�h�t�`��b`����(�������_���W���\��gK�.
���y|�����GM��/}����a�'����o���g�Pn���)G��+W}���+P���^���y���������#��d3��OtvvV-Z$6lH�R�?
������k��o�Q7���i��.�,pr���G��o��U����o5��w����e��G������x�||]�_$"M�NX$@�i�f�f{��Z����211!����w`FC�2�����r��(�s�^
L@�J��X�-L��s�_���"!GY��w�`���fH@��	�S��,(D@/���DO�s�y�g�4�Q�9�K@�s�-�!��k-=��r�UOi��(K��`�_3�DpW�`�����r�`
��`_51 �@��i�a�W ��%��O^ �X)@0�r[�T���C"� ��<>KF�[�`N0��B� ���s�K��9���bg� �� �'s������`n^5�@@���6bs��
�	�-@.� ��J�`����"@0'����Y �@J�)�X�U'@0'���@@�j������b ��c,'�B@ ~�y���h���`nge2+@JsJ��9���Zg� �	 �'t������`n\4t@@���vjs�N
�	���?�� ��R�`�$�AJ����2�@H��<�;��j�s^ �X-@0�z{�\�s�y���P ��/@0���� ����Lf� �@I�`N)�"@0'��R��@ ���n�6 ����� ��S�`�Nm��I�9������@P
��D4H���`��Rf �i ��ugYW���`��@��Vo��Q�`N0���
@�����2��s������@(	�)W�sWj�u"�$T�`���c��s��q��@�v
�����:)@0'�w���6 �J����) ��SR�,@ ����,�� ��yU � `�����ar1
�	�1�C!� �@+������l�e�d������e�v-m0�+����{�m��Z0x�����s���F�{@:*��`~������=�\�c_���{�Je�V6����;p���<�V�i��s�y'��k"� �-`g0��?�-��������������l��T7�\>dW~�k�@]}�^���q�����f�s��s�b���q�g5��<����GJS���qO��nv}���Z�?��[�����k�.�D7$��]�L@ ���R��r��� }3�ed.T���
��V����~l?xo�*�U���`�g��@_�g�te�+$��k�����7^�����/<R9���xy�s��;��J@)`_0f,N)�y����l������Ss�t�����`^�D�W?��T��/�=[}��f���+]���
���%���KD��>$�r�'M0'��W
=@h�@R���"��+	�8�Q��v5|���7[�u�<��L�~���T�Sn~�q07^����w��k���y�����XfV\�`N0��� �av�����IW��?K]H�g�V_
�M�1�	�z�<��s��	��v+������Q��iS���]w�����i]s�!�{�-��
n�(�|,R��c�dj��q�O�R���v�~t@@�%��y�y��nT�1�����������}��cZ_P0W�9�.�+����N�]�y�� �0��y��o��B�g�k�nR�]����7��oN��yl�?c^����5�#�1/_�G�p��X��1O�O�� ���y��o�0�������eed�t��+S���o|�zCe�)�5�X��7%���~l��&��5����s��Z���(��$��
��� ���"�o��yiR}uM������u;P{v��8B����|�����?C�F��s���U�����ao$
����<?Vl��{���cnx��������7�zP�w�9����1,@�@�����4E�m�g��$!���<��|N�y���(K���!�$X�`���c�Ff�����Z�'������ ��	�)
W�y�;����	'�(K��;����X' �@3�f���$�`���M������7�x�����C������
�~�w�#x?7v�z��������;���e�����7�s��~����w���tB ��LFr9�����u!�@A��?��Oe��u� �j�`��|�;�<�����K����e����'�|B�1i�Q�r�K_5��w���5r���#����9��6�{����U�0�G�,p��G������%�60��������3��X+����,_E���G]4��Q�"GY4�f �Np����wj�Z�<PD��y�$��`�����"�4%@0o���	 ��7���"��"m]�3��1��M'@���m���%��.�� ��yv�9�!=�7qu����c�D�@���3[��B	��1w�E ���s�����'@0'��WM�� �@���c���2�
H��i���`nO52@Z�?�Yd������~����n��^>�w��9�����M0'�[R�L@�`;��'��n���s����-]�dHr��]�U��u��Bx�5������yZ_+s�yZk�u!��D��`^
�R�>t��3�ed.T���
��V�=C9)������U^+%�2��s^ �X-`_0�*�l��qO�_�������>�0�Ss��C	�V�g��#��c-(C@ n��s��wf�W���c(����)����b`'��]Q��G0'��[��@������t��n��J�������Y�#�{\
�sw^s��;��J@)`_0�=O^b��c@]~3h�z�����D�m�I�	��
�N ��K��`^��=����D!@�T�1�5�~�a�D�U	�����_�`N0�|2@X�Kw��gV�]��~/+##�3�^��Jux��g�F�OA�	�sw^s��;��J@)��`�g�'2�c��
��Z]�������{�G&�[�{m���7���C���
���C{#�z%�H���`��2e� �.�$����� ���-N&� ��/@0�\ ��]�u�� �P�yB7�i�	��EC@�)@0o�6�����`����� �(�J"�D�`N0OI)�@��
������V�`N0�U� ���s�����(@0'��XN� �@���M�N�9����dV �����+s��+��:@*@0O��1mc�9���h�� �@;����Z� ��;Y\@��\ID���	�))e�� �V�y�w���l�f�$7����;}=�9����G�_�%�|��"o<�^���"���n���������7 �$�#��w��`N0���@���$�?�Ud�������9�[�.�-�|(���Q�yq;�q����9�<��J�: ��v��q���t����Ho��d�rR>Ry��F��~��;��1�{��[�/��#s��S1�\X�g�����5�,\�G�r��+�������e�[��8�j������L���������3��+����=��%}���wo���U�~����bG|����0���t��~�^�]t!�|����"����jZm[$S3��^x��r���� �.	���P�y�p�M���d>11��A�.$K9�W��pX�2:���.���7w.�4���������f��������3��9���0R���Z*��Q0�)�������+�W�6�����������z��P����=��^x�j�9�\�`h� �@'��A�6���dE�.���������6�LT���`�jC�:������o�w��~	�]K����q�~*����-��^p�Z�x���_4�z�{��A�%�W�p���k�& �J��yU���~��E��j6,V>�$������'L�0Oo���2����|�����T#���4�p���0�w�s�L��Z����2�]���+�{���`nP.4E@�3���^�4���?]yf�N�p']#�63N�����u�?���a�5
�5O^lx���Q�z�\�V���Y�����=U��^����%C0'��M@���u���;���!���c�1N�y�bV��������Ak�����A+���1������`�_-�Dc� IDAT@�C������'�T�I3�\v��F0�����
��2N����ym�;���_����������3��Jz�xg:��,�n��c�����}<b��������k�`N07(�"� �����cu��R�$�������u�y�SZ����S��B������1/dp�����j
6Q��_�oX�V���|��7v�_+�^�d����<�e��7�M@h�@K���q��W��8�\�K���g�}�y~��G�i���g�k�3���nY|�W|db�����o�?���yi��x����~
����<t�;��k�y^|aF!O�)��=��������YrE>`��A07,�#� ����VL���v+.��� ���]��@ ����c�*��L�����v
�	���7�� ����}������(+�S�i����y�`^�I��j����dttT6m��	���S�{b��]s�!�{�-��
8���^ ��X��`��e3����<��P��L��`^T���+��+n�!�
 ��
���z���AS�j�l�F�s��s�4� Y�`���	��a�
����
s�y�^'L@����s��
D��P^��G���	�eB@
s
��`^�P������E25s��f8cnLF@�n��K6��NA�Z��O=�����Ku�����{�n9�������{���/o��W����q?����ur�	OD���G���A��g�X'<������@�.\(333i^"kCC����{e����=i�@��
��'x?����LK����R������/��vZ���/�'�>U�U�W.]�a��U����.�eo�<o��>� ���{v�Ny�����H���%K��A�_t�l��p]���099)�\r���?�M���\e�$�?s�����GYt+�v �.p����-��{�i��^0}�J����7.�@V��`N0�."�8/��`��K����}J�3�=O�,Z�l��~�����7 �i.�+�v�z�\J�9MT����C]�7��A.�9���WC�N�����"�v��r����r��ne�$7(�g0�	���E�`^�h1�WN����<�����@�+`g0/��,�w����Ho��d�r��T��������o7wg�8�po����~�T����1L���Ym��<�*�����*�����k�*��en������Jm~eF����G��y��3�qU� ��Y��`^�YYq���4��|2����� [���+�q��4j�z�=�tIWv@���������_%�wT��PX�W�Ce0z_���Z�\�q��`^V�q����H��1��F'@��������!O����]����'�jc0N���0�����!�),�3�y�.{�/�k��n��	�N�X< �������\�W���le�4�|�I6��HU0of���-���{��2�����������?����l�	����"� ��u�<�iu�V��[���j����0��8�+����m���;��<��|��d��/�9�\Y$4@@���������=�H�����1�'�<y1�s�������s������@(���g����t���g��|�O��H�����}����y)���!��y��N������s^ �X-`]0�|����H���T�e�`^~�b�q*��y���22bp�<_�Oe!�7�Z"���* :#� �j�V���we��3���R��r����9�����Hu�O�����g.PO�����r�����	�����'�Z58���L��g�W?aF��|�9��� �v�"��d�5g�[r
M���`��gq ���/��?L���C�I�|~V�F�9������@0�/��P|e�z*>M�|��@@�`N0�u� ����s���\"��D.�FpG�`��^��R�9�����G�\�`n�1��������@h�����i���`nc]2'@���+s��+��:@*@0O��1mc�9���h�� �@;����Z� ��;Y\@��\ID���	�))e�� �V�yZw�u�
�	��*@�Z��`�I�[�C������%K�`N0OV�2[@�Z����/���lSZ��1(��~rM;������H�C'@�%��`��+������K��c��U�f	�J)�!@0'��QG�� �@�l���~���[{�PN��[B��{����l�]��p��Ko�8=2���I~������]�7��@�w��~"��o�v0p���C#� �����<������+�e����p-����\??N>�g6�HW�te�+���X�'���d��]s����l����3B{���VWC@�P��`>��*�j��%�
�w�k�)��]������-�I��A��=~c�N���	�)<.� ���������,�w�����f�B0��w��J�����yZ�n}���`N0��� �us��+/�9���Zg� �	 ��Z���&����� ��S��`��y�H�D��)�o�>�����1og��u-�9�<�Zb@Z"`m0W<��M��7~�W,>���`�SYZRo��`N0�d�qm@�����6l0���c^������cu���9,�[���&���������� ���_u\@�����[�4��-���S�`N0og�q-@���ds�"JH�9�<!��4@\ ��]�}�9���Zg� �	 �'t������`n\4t@@���vjs�N
�	���?�� ��R�`�$�AJ����2�@H��<�;��j�s^ �X-@0�z{�\�F���1������\����������Q��iS`�u��)�=��pT�k�;$w����_���E��E��ul�L�0�{��W��W�n�� ��$@0wi��^�v0/�r��qO�>Z�����`^,:���/>V� �'@0�s�U�4�y�#e��q�������Kv�ce�1�s�j�% ���s�+���k��y��,��`X) �@���7���h.��Jf�Wq�Eo�1���^��
@�9U��@��x�e�gHr��n�E0'���m@� ����.�>b0/���>����y��������������b���������c��b��yMF_��q?�����9������/�C�)��g��U���q?���� +�y~]�\.i<:!`���e����^�e:��(��d�v��!���a��6��K!�~�`^8�2"�q�|��=����U�/�'�|R��]��M7�-=��X���Sr�������a��d��m��n[�\�A��g?u��C��������DN���H}�d&���lZ�
������E�N
P����Y��gd�����(WB�f����j��b8q����{n���qC�b�-��!g^ui��tB@ IeI�n1�f��y�	,�wzV?21��	��eC@G��n��������g�u��s�Z� �sj��`^�[>�bz��`N0w��:@� �7o����1��`��`���]�F�������^ ����/g����q����H��?;�����^����P�u~i��r�������y�U���@�i�������	���{'��-�E*�������"�%�Gc� ���I
�#@0'�kW;�\��� �1
p�<FL��Z�`N0�.P��6
@b ����PV�	��J0���! �@��1�j�9�\�@	��T4D�Q�`#&CY-@0'�k(�\��� �1
�c�d(��s�%�kS�@ F�y��e���`�]�sm*"��(@0���� ����`�MEC@��1b2��s��v����h� ��<FL��Z�`N0�.P��6
@b ����PV�	��J0���! �@��1�j�9�\�@	��T4D�Q�`#&CY-@0'�k(�\��� �1
�c�d(��s�%�kS�@ F�y��e���`�]�sm*"��(@0���� ����`�MEC@��1b2��s��v����h� ��<FL��Z�`N0�.P��6
@b ����PV�	��J0���! �@��1�j�9�\�@	��T4D�Q�`#&CY-@0'�k(�\��� �1
�c�d(��s�%�kS�@ F�y��e���`�]�sm*"��(@0���� ����`�MEC@��1b2��s��v����h� ��<FL��Z�`N0�.P��6
@b ����PV�	��J0���! �@��1�j�9�\�@	��T4D�Q�`#&CY-@0'�k(�\��� �1
�c�d(��s�%�kS�@ F�y��e���`�]�s5�M��W��_��n�b��o��]O��=�]"|�y?z �	 �'d��f�s��v��Ts�-@S�����*@0'�k�nS���YtP�Zs
�x���O���P�y���,�Z�`���eqs�����`~�[Ef^��V���%���<l�q?��[W����X�o�N��r�C��T�+s��v�w"�?z���c?��ce��l�Y}���F�D0�*G?@�`N
 @0'�k�
�j*���� ���w�M�h�T�`��g�{pB��O���FX���������M�{���N����#_s�!�{�-��
n�(�|,R��c�dj��q���[-nz����a�=w��W]j�w��I9i�m���s5�\mD@�T�`n*F��
�s�_���2!=��3"#����s���x���,�<.Q�& �d�y�w������^�>8 �@V�{3��`>�L0�(9�����+�h��
��x���+p���B0�x���Hs%
@�Q������	��M�����9c�$����h��
�M�h�T�9�\�v	�j*���� �����T��Ihy0��������r�����W_���S�SY�k{�������F�����W�04k�C����co�U����x�-��g�2�/��i���;��?���k��lx�����v=m�7����[�g����LFL�-]�r@��U��#���\��[��r:$��`���P&''��C������������\~��r����������_ ���H}��[Ffd���)��&��������z�o�1k�f|�&��C>n�����NxT6?7���[O�%������'�7���/�}�9�����\��H9�[�7��w��w�_?�;R�}��B�z����o���������Na��c�9F^~��NN-�����Tn�S�Z�t�\z��c��Bb��hi0��9�E�c�~�4s����.�]�nS_����NX)��|������,����4�(��������SY�Me��SY��-� ��y"��I� @0'�k��\M�s�Q3-����e�����x���r�O���_����T���F�K/�E�`#�$@@;��]>���!��?�H���,E)���+��,j#q��?���7d�9�7�������y�7D6^��4A�V	�[%���	h�8'N0'����\C�`�D"�+�h���s�����(@0/cr�\YVeQ��O5QS-�c��H��<�[��
�s��!���8c�6j���=�"�\�yr����	�	��C0WS��F�� �7�G_�+@0O��1s3�9�\�b�j*������f���@r���;fn&@0'�kW�\M��`�q�H�+���81�	�G�'Z��"�w�K"`����M`
m ����`���H0��|���QO.����Y#�|����EK���3��l�y���G ��d��6 �����`��JZ0�m��r���V����R���5���g������l�����Gr�I �'}�����`�[+B0WS����!���!5H@�w\����?��H}�c��N$^�`��-d�s��f��\C�`�F"���h��s*��9�\���c��"����j#Z ���pS�`N0��|����`�6"���h�sj�M�9�\��	�j*����`�6���7�s��'����j#���� @0�� ���+�`��"����?{�����V�"#�����������pE�`N0��u����`�6J\0�\���V�b:�D���������y�����"��i ��qWYS���`��� ����j#W��T>�o}�55H@��������~.R_:!�F�yw�5����E�G����c�dj��q����j9p������r�r�U����c�����6����j6����`�6���_���yI�0���?��{�[#���
�m���w��c�]Ss5�\mD0W]�}��p����-~��!9���"���
�m�����`�]Ss5�\mD0W��F�pK�`��~��Z�9�\��	�j*����`�6"���h������vy�s��v���Ts��\m��`���"�F;/��M
B�����3s��v���Ts��\m��`���}��/��zr-�&����m���O�D�G/��Nm���%���_s5�\mD0Wu"�?�}�����PO.����=9g�p���hl��"�������`N0��~����`�6"����j#Z�%@0wk�]^-��`�]�s5�\mD0W��F�pK�`��~��Z�9�\��	�j*����`�6"���h������vy�s��v���Ts��\mD0W��-��[���j	�s��'����j#����`�6��[s������	���O0WS��Fs��\mD��n����%�����`��"����j#����~S$7�F�i�������N7��w��F�vt��t����3�\��s��v���Ts��\m�T0���j��?|}�|�?�-R�������_8������Q'�`�����7d�d�	���J0WS��Fs��\m�L0��kg��<��HM�U�f���}��� �7o�� ���+�`��"����j#����`�6JS�y�v��4 ���_!s5�\mD0W��FI�_���2�#�������#���F�mq�Z�|����=�n
Sj���`�]Xs5�\mD0W��FI����Q����za-f���d&_6���G��o�������MaJ- ����`��"����j#����`�6��M�K�`N0��H����`�6"����j#�����?{�F��&�����E��c�"X��N�`N0�.J����`�6"����j#���H��N���F��&_�u�<�w�q�7/;^n��{���:�UB�}Z�s�Z&����j#����`�6"����	�7?{�l��M�"�MV�� ��:����a���'�n�
�K0�DJ�
���^�l�_fW��{�5\���������M�{���N������\s�!�{�-��
n�(�<�s��-�����=���r��������s��y���}w������f���@0W���Fs��\mD0Wu$��q���4&W����Z����$�G��SZ��y)���$��_��g�eP��9��X>s����������a@�K��@v�k~��M'���w>��s5�\mD0W��Fs
#� `��f0/�����������K�{P�sa]ous��^���5��<_��g4�7�m��r���]2s��������j6����`�6"�k����yh���f�����?�B0'����Rs%��N=(_��'�v�
.9��_m�rKd��>&?	��
�IDAT�z��j#���M�L@/���x�7�Iu����X$��`��: �kH��Hs%�<�}�����P7h��lO��6�����H��lM<�%��?9c��14A \ �`nx��`N0�}Q�5��J$����`�&�U�f���}_�e}��|�'�<�^�	�N
�4����G&''��733#O>���~���k�������_5v9o����!�~~��_9C��7R��O,���i���?�J};�������7��Uw������xy��|�/1�����?�
{��)r�#�d�"�'�<����i���8�7�=��7�;n��t������{.R���y��/��>+_G�������������5�.��#�y��w��^z�E���C�����������|/{����+���<}���x����\f�|~<�������i�N����~D�t�[���x�u����n����Go�����������_wlr��`�y��:��V���������U��s7��w�������=������7��N_�Y���~~���q�<�������~A���hu��l�S7?m�����?2q��
��C'�"O0�}Shi�ccc���/���i�d2IqJ�<s�\b�������oL��f��m�ky����7�����o��a����������n�"����z���o���+S{��G���_����'b�I��~�9�����SNI������/�,?��������~�I��o�`�Y�vm�o�����������c��8{��4&�s�N��m���=��hM�+����,|\b��,uo
m�:c�\N;fN0o�>�[�K0o��?2��������[g��v	h����>`(�K�e�[[����[�K0o�-���������[g��v	�s��p>�����\�\*b3�yD8�nsM�����p��+BS�y4�.sM(�%^�,���\�yL�!��[�K0o�/��u�������[�K0o�-#�%@0�k?b�
�<��A���%����`�Z[t�y��	���ed�:�}�Ev��'��������_��Qs�1)Y�]������G�a�f��R0����#V�;�����%�����T�#�<����`F�v��+V��W�������r��xf4,�X0���� � � �Q�yG��8 � �E�9�� � ��s6�) � � @0�@@�@�`n�&0@@�
j���J��DE��
K/uI`���O������I�[����o���������N��:����#"=C9�}�R����:H��uO���H��V�Vu��gB0 �����W��?3w���/��A�����/(����l�o\�R/z�U5S�*����N�/��$��Sy��`����/.���{���#-_��n����^�S�:5�H��L�.@0$��aQhV����.�Y���f(��7��`^�a�o|'��rn�6��b{��opP�e����/���z�~a��^`�nUh������]e�(�����N�Lh��
��H���*���`�������������� �V�������`�FJwn���/T�����]�Z
�	8JU��9?���i�@\����+��v�1���#����(�	�l?g�c��Bp����W����#h��S���^;T�D�\z����]�20
�5���?cn: ���!e��A�
�q����s�"��<s�<�w�����n�1n���To��h|�w�~�S�Q�@?���*?�����f�!��o~����]�`^w2��c3H}����'��������;���ASLc�r0W����h���|�|�?����� �Y������Vp�F���
vRz�H�����Hk��#AG*�MVA���c.M^=�K5��u��o���v��_��j��Z�4B�U� ��7v�-�Fg�+�X��������}zH~��s���R����C
j��8c�������U��d��q��0����Q������i�@��w����p����aa���E����yl�N�:�AhH	>�\�=C�2������v��%4���_��=?���i�@��@��7YY�\��l���t�����E	�4��17:��6��T����{�9�T7����v�J���i�/���EL#Z"@0o�Z�6�R�
�RT����)�g��P.�gQ.�J��'[-9�hK����B~/��F3Hsc��=�Z�����/����?����cm6�m��� � ������f� � �6�m��� � ������f� � �6�m��� � ������f� � �6�m��� � ������f� � �6�m��� � ������f� ��_����/�r����V-i���{�n��w�#<�PZ��:@'���H�Nl5�D�4	�p���GtK���~�i���_��z8MKf- �@�.�p5�<���@ u������[n��c��d�g?�.[���<u���@ ����w�N=����w���>%c����S��}j�>�q�)����>��8�+��O~0����LGv�;	IEND�B`�
v5-0002-Introduce-a-bump-memory-allocator.patchapplication/octet-stream; name=v5-0002-Introduce-a-bump-memory-allocator.patchDownload
From a53a3f779ece2acad5dde5d34ec8f8a0232aaebb Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Thu, 4 Apr 2024 22:20:53 +0200
Subject: [PATCH v5 2/3] Introduce a bump memory allocator

---
 src/backend/nodes/gen_node_support.pl |   2 +-
 src/backend/utils/mmgr/Makefile       |   1 +
 src/backend/utils/mmgr/bump.c         | 818 ++++++++++++++++++++++++++
 src/backend/utils/mmgr/mcxt.c         |  14 +-
 src/backend/utils/mmgr/meson.build    |   1 +
 src/include/nodes/memnodes.h          |   3 +-
 src/include/utils/memutils.h          |   7 +
 src/include/utils/memutils_internal.h |  20 +-
 src/tools/pgindent/typedefs.list      |   2 +
 9 files changed, 864 insertions(+), 4 deletions(-)
 create mode 100644 src/backend/utils/mmgr/bump.c

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index d4244facbb..81df3bdf95 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -149,7 +149,7 @@ my @abstract_types = qw(Node);
 # they otherwise don't participate in node support.
 my @extra_tags = qw(
   IntList OidList XidList
-  AllocSetContext GenerationContext SlabContext
+  AllocSetContext GenerationContext SlabContext BumpContext
   TIDBitmap
   WindowObjectData
 );
diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index dae3432c98..01a1fb8527 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	alignedalloc.o \
 	aset.o \
+	bump.o \
 	dsa.o \
 	freepage.o \
 	generation.o \
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
new file mode 100644
index 0000000000..f98a203a0c
--- /dev/null
+++ b/src/backend/utils/mmgr/bump.c
@@ -0,0 +1,818 @@
+/*-------------------------------------------------------------------------
+ *
+ * bump.c
+ *	  Bump allocator definitions.
+ *
+ * Bump is a MemoryContext implementation designed for memory usages which
+ * require allocating a large number of chunks, none of which ever need to be
+ * pfree'd or realloc'd.  Chunks allocated by this context have no chunk header
+ * and operations which ordinarily require looking at the chunk header cannot
+ * be performed.  For example, pfree, realloc, GetMemoryChunkSpace and
+ * GetMemoryChunkContext are all not possible with bump allocated chunks.  The
+ * only way to release memory allocated by this context type is to reset or
+ * delete the context.
+ *
+ * Portions Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/bump.c
+ *
+ *
+ *	Bump is best suited to cases which require a large number of short-lived
+ *	chunks where performance matters.  Because bump allocated chunks don't
+ *	have a chunk header, it can fit more chunks on each block.  This means we
+ *	can do more with less memory and fewer cache lines.  The reason it's best
+ *	suited for short-lived usages of memory is that ideally, pointers to bump
+ *	allocated chunks won't be visible to a large amount of code.  The more
+ *	code that operates on memory allocated by this allocator, the more chances
+ *	that some code will try to perform a pfree or one of the other operations
+ *	which are made impossible due to the lack of chunk header.  In order to
+ *	to detect accidental usage of the various disallowed operations, we do add
+ *	a MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
+ *	various disallowed functions raise an ERROR.
+ *
+ *	Allocations are MAXALIGNed.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "port/pg_bitutils.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
+
+#define Bump_BLOCKHDRSZ	MAXALIGN(sizeof(BumpBlock))
+
+/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
+#ifdef MEMORY_CONTEXT_CHECKING
+#define Bump_CHUNKHDRSZ	sizeof(MemoryChunk)
+#else
+#define Bump_CHUNKHDRSZ	0
+#endif
+
+#define Bump_CHUNK_FRACTION	8
+
+/* The keeper block is allocated in the same allocation as the set */
+#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
+#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))
+
+typedef struct BumpBlock BumpBlock; /* forward reference */
+
+typedef struct BumpContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* Bump context parameters */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
+
+	dlist_head	blocks;			/* list of blocks with the block currently
+								 * being filled at the head */
+} BumpContext;
+
+/*
+ * BumpBlock
+ *		BumpBlock is the unit of memory that is obtained by bump.c from
+ *		malloc().  It contains zero or more allocations, which are the
+ *		units requested by palloc().
+ */
+struct BumpBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+#ifdef MEMORY_CONTEXT_CHECKING
+	BumpContext *context;		/* pointer back to the owning context */
+#endif
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+/*
+ * BumpIsValid
+ *		True iff set is valid bump context.
+ */
+#define BumpIsValid(set) \
+	(PointerIsValid(set) && IsA(set, BumpContext))
+
+/*
+ * BumpBlockIsValid
+ *		True iff block is valid block of a bump context
+ */
+#define BumpBlockIsValid(block) \
+	(PointerIsValid(block) && BumpIsValid((block)->context))
+
+/*
+ * We always store external chunks on a dedicated block.  This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+	(BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
+
+/* Inlined helper functions */
+static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
+								 Size blksize);
+static inline bool BumpBlockIsEmpty(BumpBlock *block);
+static inline void BumpBlockMarkEmpty(BumpBlock *block);
+static inline Size BumpBlockFreeBytes(BumpBlock *block);
+static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
+
+
+/*
+* BumpContextCreate
+*		Create a new Bump context.
+*
+* parent: parent context, or NULL if top-level context
+* name: name of context (must be statically allocated)
+* minContextSize: minimum context size
+* initBlockSize: initial allocation block size
+* maxBlockSize: maximum allocation block size
+*/
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+				  const char *name,
+				  Size minContextSize,
+				  Size initBlockSize,
+				  Size maxBlockSize)
+{
+	Size		firstBlockSize;
+	Size		allocSize;
+	BumpContext *set;
+	BumpBlock  *block;
+
+	/* ensure MemoryChunk's size is properly maxaligned */
+	StaticAssertDecl(Bump_CHUNKHDRSZ == MAXALIGN(Bump_CHUNKHDRSZ),
+					 "sizeof(MemoryChunk) is not maxaligned");
+
+	/*
+	 * First, validate allocation parameters.  Asserts seem sufficient because
+	 * nobody varies their parameters at runtime.  We somewhat arbitrarily
+	 * enforce a minimum 1K block size.  We restrict the maximum block size to
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+	 * regards to addressing the offset between the chunk and the block that
+	 * the chunk is stored on.  We would be unable to store the offset between
+	 * the chunk and block for any chunks that were beyond
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+	 * larger than this.
+	 */
+	Assert(initBlockSize == MAXALIGN(initBlockSize) &&
+		   initBlockSize >= 1024);
+	Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
+		   maxBlockSize >= initBlockSize &&
+		   AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
+	Assert(minContextSize == 0 ||
+		   (minContextSize == MAXALIGN(minContextSize) &&
+			minContextSize >= 1024 &&
+			minContextSize <= maxBlockSize));
+	Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+
+	/* Determine size of initial block */
+	allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+		Bump_CHUNKHDRSZ;
+	if (minContextSize != 0)
+		allocSize = Max(allocSize, minContextSize);
+	else
+		allocSize = Max(allocSize, initBlockSize);
+
+	/*
+	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
+	 * the context header and its block header follows that.
+	 */
+	set = (BumpContext *) malloc(allocSize);
+	if (set == NULL)
+	{
+		MemoryContextStats(TopMemoryContext);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("Failed while creating memory context \"%s\".",
+						   name)));
+	}
+
+	/*
+	 * Avoid writing code that can fail between here and MemoryContextCreate;
+	 * we'd leak the header if we ereport in this stretch.
+	 */
+	dlist_init(&set->blocks);
+
+	/* Fill in the initial block's block header */
+	block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
+	/* determine the block size and initialize it */
+	firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
+	BumpBlockInit(set, block, firstBlockSize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	/*
+	 * Fill in BumpContext-specific header fields.  The Asserts above should
+	 * ensure that these all fit inside a uint32.
+	 */
+	set->initBlockSize = (uint32) initBlockSize;
+	set->maxBlockSize = (uint32) maxBlockSize;
+	set->nextBlockSize = (uint32) initBlockSize;
+
+	/*
+	 * Compute the allocation chunk size limit for this context.
+	 *
+	 * Limit the maximum size a non-dedicated chunk can be so that we can fit
+	 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
+	 * block.  We must further limit this value so that it's no more than
+	 * MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks larger
+	 * than that value as we store the chunk size in the MemoryChunk 'value'
+	 * field in the call to MemoryChunkSetHdrMask().
+	 */
+	set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
+	while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
+		   (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
+		set->allocChunkLimit >>= 1;
+
+	/* Finally, do the type-independent part of context creation */
+	MemoryContextCreate((MemoryContext) set,
+						T_BumpContext,
+						MCTX_BUMP_ID,
+						parent,
+						name);
+
+	((MemoryContext) set)->mem_allocated = allocSize;
+
+	return (MemoryContext) set;
+}
+
+/*
+ * BumpReset
+ *		Frees all memory which is allocated in the given set.
+ *
+ * The code simply frees all the blocks in the context apart from the keeper
+ * block.
+ */
+void
+BumpReset(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_mutable_iter miter;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	BumpCheck(context);
+#endif
+
+	dlist_foreach_modify(miter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, miter.cur);
+
+		if (IsKeeperBlock(set, block))
+			BumpBlockMarkEmpty(block);
+		else
+			BumpBlockFree(set, block);
+	}
+
+	/* Reset block size allocation sequence, too */
+	set->nextBlockSize = set->initBlockSize;
+
+	/* Ensure there is only 1 item in the dlist */
+	Assert(!dlist_is_empty(&set->blocks));
+	Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
+}
+
+/*
+ * BumpDelete
+ *		Free all memory which is allocated in the given context.
+ */
+void
+BumpDelete(MemoryContext context)
+{
+	/* Reset to release all releasable BumpBlocks */
+	BumpReset(context);
+	/* And free the context header and keeper block */
+	free(context);
+}
+
+/*
+ * Helper for BumpAlloc() that allocates an entire block for the chunk.
+ *
+ * BumpAlloc()'s comment explains why this is separate.
+ */
+pg_noinline
+static void *
+BumpAllocLarge(MemoryContext context, Size size, int flags)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#endif
+	Size		chunk_size;
+	Size		required_size;
+	Size		blksize;
+
+	/* validate 'size' is within the limits for the given 'flags' */
+	MemoryContextCheckSize(context, size, flags);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+	blksize = required_size + Bump_BLOCKHDRSZ;
+
+	block = (BumpBlock *) malloc(blksize);
+	if (block == NULL)
+		return NULL;
+
+	context->mem_allocated += blksize;
+
+	/* the block is completely full */
+	block->freeptr = block->endptr = ((char *) block) + blksize;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* block with a single (used) chunk */
+	block->context = set;
+
+	chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
+
+	/* mark the MemoryChunk as externally managed */
+	MemoryChunkSetHdrMaskExternal(chunk, MCTX_BUMP_ID);
+
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+#endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* add the block to the list of allocated blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
+#endif
+}
+
+/*
+ * Small helper for allocating a new chunk from a chunk, to avoid duplicating
+ * the code between BumpAlloc() and BumpAllocFromNewBlock().
+ */
+static inline void *
+BumpAllocChunkFromBlock(MemoryContext context, BumpBlock *block, Size size,
+						Size chunk_size)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#else
+	void	   *ptr;
+#endif
+
+	/* validate we've been given a block with enough free space */
+	Assert(block != NULL);
+	Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	chunk = (MemoryChunk *) block->freeptr;
+#else
+	ptr = (void *) block->freeptr;
+#endif
+
+	/* point the freeptr beyond this chunk */
+	block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
+	Assert(block->freeptr <= block->endptr);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Prepare to initialize the chunk header. */
+	VALGRIND_MAKE_MEM_UNDEFINED(chunk, Bump_CHUNKHDRSZ);
+
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return ptr;
+#endif							/* MEMORY_CONTEXT_CHECKING */
+}
+
+/*
+ * Helper for BumpAlloc() that allocates a new block and returns a chunk
+ * allocated from it.
+ *
+ * BumpAlloc()'s comment explains why this is separate.
+ */
+pg_noinline
+static void *
+BumpAllocFromNewBlock(MemoryContext context, Size size, int flags,
+					  Size chunk_size)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+	Size		blksize;
+	Size		required_size;
+
+	/*
+	 * The first such block has size initBlockSize, and we double the space in
+	 * each succeeding block, but not more than maxBlockSize.
+	 */
+	blksize = set->nextBlockSize;
+	set->nextBlockSize <<= 1;
+	if (set->nextBlockSize > set->maxBlockSize)
+		set->nextBlockSize = set->maxBlockSize;
+
+	/* we'll need space for the chunk, chunk hdr and block hdr */
+	required_size = chunk_size + Bump_CHUNKHDRSZ + Bump_BLOCKHDRSZ;
+	/* round the size up to the next power of 2 */
+	if (blksize < required_size)
+		blksize = pg_nextpower2_size_t(required_size);
+
+	block = (BumpBlock *) malloc(blksize);
+
+	if (block == NULL)
+		return MemoryContextAllocationFailure(context, size, flags);
+
+	context->mem_allocated += blksize;
+
+	/* initialize the new block */
+	BumpBlockInit(set, block, blksize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	return BumpAllocChunkFromBlock(context, block, size, chunk_size);
+}
+
+/*
+ * BumpAlloc
+ *		Returns a pointer to allocated memory of given size or raises an ERROR
+ *		on allocation failure, or returns NULL when flags contains
+ *		MCXT_ALLOC_NO_OOM.
+ *
+ * No request may exceed:
+ *		MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
+ * All callers use a much-lower limit.
+ *
+ *
+ * Note: when using valgrind, it doesn't matter how the returned allocation
+ * is marked, as mcxt.c will set it to UNDEFINED.
+ * This function should only contain the most common code paths.  Everything
+ * else should be in pg_noinline helper functions, thus avoiding the overhead
+ * of creating a stack frame for the common cases.  Allocating memory is often
+ * a bottleneck in many workloads, so avoiding stack frame setup is
+ * worthwhile.  Helper functions should always directly return the newly
+ * allocated memory so that we can just return that address directly as a tail
+ * call.
+ */
+void *
+BumpAlloc(MemoryContext context, Size size, int flags)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+	Size		chunk_size;
+	Size		required_size;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+
+	/*
+	 * If requested size exceeds maximum for chunks we hand the the request
+	 * off to BumpAllocLarge().
+	 */
+	if (chunk_size > set->allocChunkLimit)
+		return BumpAllocLarge(context, size, flags);
+
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+
+	/*
+	 * Not an oversized chunk.  We try to first make use of the latest block,
+	 * but if there's not enough space in it we must allocate a new block.
+	 */
+	block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
+
+	if (BumpBlockFreeBytes(block) < required_size)
+		return BumpAllocFromNewBlock(context, size, flags, chunk_size);
+
+	/* The current block has space, so just allocate chunk there. */
+	return BumpAllocChunkFromBlock(context, block, size, chunk_size);
+
+}
+
+/*
+ * BumpBlockInit
+ *		Initializes 'block' assuming 'blksize'.  Does not update the context's
+ *		mem_allocated field.
+ */
+static inline void
+BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	block->context = context;
+#endif
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+	block->endptr = ((char *) block) + blksize;
+
+	/* Mark unallocated space NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockIsEmpty
+ *		Returns true iff 'block' contains no chunks
+ */
+static inline bool
+BumpBlockIsEmpty(BumpBlock *block)
+{
+	/* it's empty if the freeptr has not moved */
+	return (block->freeptr == ((char *) block + Bump_BLOCKHDRSZ));
+}
+
+/*
+ * BumpBlockMarkEmpty
+ *		Set a block as empty.  Does not free the block.
+ */
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+	char	   *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
+#endif
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(datastart, block->freeptr - datastart);
+#else
+	/* wipe_mem() would have done this */
+	VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
+#endif
+
+	/* Reset the block, but don't return it to malloc */
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+}
+
+/*
+ * BumpBlockFreeBytes
+ *		Returns the number of bytes free in 'block'
+ */
+static inline Size
+BumpBlockFreeBytes(BumpBlock *block)
+{
+	return (block->endptr - block->freeptr);
+}
+
+/*
+ * BumpBlockFree
+ *		Remove 'block' from 'set' and release the memory consumed by it.
+ */
+static inline void
+BumpBlockFree(BumpContext *set, BumpBlock *block)
+{
+	/* Make sure nobody tries to free the keeper block */
+	Assert(!IsKeeperBlock(set, block));
+
+	/* release the block from the list of blocks */
+	dlist_delete(&block->node);
+
+	((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(block, ((char *) block->endptr - (char *) block));
+#endif
+
+	free(block);
+}
+
+/*
+ * BumpFree
+ *		Unsupported.
+ */
+void
+BumpFree(void *pointer)
+{
+	elog(ERROR, "pfree is not supported by the bump memory allocator");
+}
+
+/*
+ * BumpRealloc
+ *		Unsupported.
+ */
+void *
+BumpRealloc(void *pointer, Size size, int flags)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * BumpGetChunkContext
+ *		Unsupported.
+ */
+MemoryContext
+BumpGetChunkContext(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+* BumpGetChunkSpace
+*		Given a currently-allocated chunk, determine the total space
+*		it occupies (including all memory-allocation overhead).
+*/
+Size
+BumpGetChunkSpace(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
+	return 0;					/* keep compiler quiet */
+}
+
+/*
+ * BumpIsEmpty
+ *		Is a BumpContext empty of any allocated space?
+ */
+bool
+BumpIsEmpty(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		if (!BumpBlockIsEmpty(block))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * BumpStats
+ *		Compute stats about memory consumption of a Bump context.
+ *
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ * print_to_stderr: print stats to stderr if true, elog otherwise.
+ */
+void
+BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+		  void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
+{
+	BumpContext *set = (BumpContext *) context;
+	Size		nblocks = 0;
+	Size		totalspace = 0;
+	Size		freespace = 0;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		nblocks++;
+		totalspace += (block->endptr - (char *) block);
+		freespace += (block->endptr - block->freeptr);
+	}
+
+	if (printfunc)
+	{
+		char		stats_string[200];
+
+		snprintf(stats_string, sizeof(stats_string),
+				 "%zu total in %zu blocks; %zu free; %zu used",
+				 totalspace, nblocks, freespace, totalspace - freespace);
+		printfunc(context, passthru, stats_string, print_to_stderr);
+	}
+
+	if (totals)
+	{
+		totals->nblocks += nblocks;
+		totals->totalspace += totalspace;
+		totals->freespace += freespace;
+	}
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * BumpCheck
+ *		Walk through chunks and check consistency of memory.
+ *
+ * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+ * find yourself in an infinite loop when trouble occurs, because this
+ * routine will be entered again when elog cleanup tries to release memory!
+ */
+void
+BumpCheck(MemoryContext context)
+{
+	BumpContext *bump = (BumpContext *) context;
+	const char *name = context->name;
+	dlist_iter	iter;
+	Size		total_allocated = 0;
+
+	/* walk all blocks in this context */
+	dlist_foreach(iter, &bump->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+		int			nchunks;
+		char	   *ptr;
+		bool		has_external_chunk = false;
+
+		if (IsKeeperBlock(bump, block))
+			total_allocated += block->endptr - (char *) bump;
+		else
+			total_allocated += block->endptr - (char *) block;
+
+		/* check block belongs to the correct context */
+		if (block->context != bump)
+			elog(WARNING, "problem in Bump %s: bogus context link in block %p",
+				 name, block);
+
+		/* now walk through the chunks and count them */
+		nchunks = 0;
+		ptr = ((char *) block) + Bump_BLOCKHDRSZ;
+
+		while (ptr < block->freeptr)
+		{
+			MemoryChunk *chunk = (MemoryChunk *) ptr;
+			BumpBlock  *chunkblock;
+			Size		chunksize;
+
+			/* allow access to the chunk header */
+			VALGRIND_MAKE_MEM_DEFINED(chunk, Bump_CHUNKHDRSZ);
+
+			if (MemoryChunkIsExternal(chunk))
+			{
+				chunkblock = ExternalChunkGetBlock(chunk);
+				chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+				has_external_chunk = true;
+			}
+			else
+			{
+				chunkblock = MemoryChunkGetBlock(chunk);
+				chunksize = MemoryChunkGetValue(chunk);
+			}
+
+			/* move to the next chunk */
+			ptr += (chunksize + Bump_CHUNKHDRSZ);
+
+			nchunks += 1;
+
+			/* chunks have both block and context pointers, so check both */
+			if (chunkblock != block)
+				elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
+					 name, block, chunk);
+		}
+
+		if (has_external_chunk && nchunks > 1)
+			elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
+				 name, block);
+
+	}
+
+	Assert(total_allocated == context->mem_allocated);
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index de83a5b047..b0a665f604 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -95,6 +95,19 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[MCTX_ALIGNED_REDIRECT_ID].check = NULL,	/* not required */
 #endif
 
+	/* bump.c */
+	[MCTX_BUMP_ID].alloc = BumpAlloc,
+	[MCTX_BUMP_ID].free_p = BumpFree,
+	[MCTX_BUMP_ID].realloc = BumpRealloc,
+	[MCTX_BUMP_ID].reset = BumpReset,
+	[MCTX_BUMP_ID].delete_context = BumpDelete,
+	[MCTX_BUMP_ID].get_chunk_context = BumpGetChunkContext,
+	[MCTX_BUMP_ID].get_chunk_space = BumpGetChunkSpace,
+	[MCTX_BUMP_ID].is_empty = BumpIsEmpty,
+	[MCTX_BUMP_ID].stats = BumpStats,
+#ifdef MEMORY_CONTEXT_CHECKING
+	[MCTX_BUMP_ID].check = BumpCheck,
+#endif
 
 	/*
 	 * Unused (as yet) IDs should have dummy entries here.  This allows us to
@@ -108,7 +121,6 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[id].get_chunk_context = BogusGetChunkContext, \
 	[id].get_chunk_space = BogusGetChunkSpace
 
-	BOGUS_MCTX(MCTX_7_UNUSED_ID),
 	BOGUS_MCTX(MCTX_8_UNUSED_ID),
 	BOGUS_MCTX(MCTX_11_UNUSED_ID),
 	BOGUS_MCTX(MCTX_12_UNUSED_ID),
diff --git a/src/backend/utils/mmgr/meson.build b/src/backend/utils/mmgr/meson.build
index 9dcf990cdc..dd43a6844c 100644
--- a/src/backend/utils/mmgr/meson.build
+++ b/src/backend/utils/mmgr/meson.build
@@ -3,6 +3,7 @@
 backend_sources += files(
   'alignedalloc.c',
   'aset.c',
+  'bump.c',
   'dsa.c',
   'freepage.c',
   'generation.c',
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index edc0257f36..c4c9fd3e3e 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -146,6 +146,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), BumpContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 6e5fa72b0e..4446e14223 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -108,6 +108,13 @@ extern void ProcessLogMemoryContextInterrupt(void);
  * Memory-context-type-specific functions
  */
 
+/* bump.c */
+extern MemoryContext BumpContextCreate(MemoryContext parent,
+									   const char *name,
+									   Size minContextSize,
+									   Size initBlockSize,
+									   Size maxBlockSize);
+
 /* aset.c */
 extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
 												   const char *name,
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index f6ec1962db..ecd0c48666 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -79,6 +79,24 @@ extern void *AlignedAllocRealloc(void *pointer, Size size, int flags);
 extern MemoryContext AlignedAllocGetChunkContext(void *pointer);
 extern Size AlignedAllocGetChunkSpace(void *pointer);
 
+
+/* These functions implement the MemoryContext API for the Bump context. */
+extern void *BumpAlloc(MemoryContext context, Size size, int flags);
+extern void BumpFree(void *pointer);
+extern void *BumpRealloc(void *pointer, Size size, int flags);
+extern void BumpReset(MemoryContext context);
+extern void BumpDelete(MemoryContext context);
+extern MemoryContext BumpGetChunkContext(void *pointer);
+extern Size BumpGetChunkSpace(void *pointer);
+extern bool BumpIsEmpty(MemoryContext context);
+extern void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+					  void *passthru, MemoryContextCounters *totals,
+					  bool print_to_stderr);
+#ifdef MEMORY_CONTEXT_CHECKING
+extern void BumpCheck(MemoryContext context);
+#endif
+
+
 /*
  * How many extra bytes do we need to request in order to ensure that we can
  * align a pointer to 'alignto'.  Since palloc'd pointers are already aligned
@@ -120,7 +138,7 @@ typedef enum MemoryContextMethodID
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_7_UNUSED_ID,
+	MCTX_BUMP_ID,
 	MCTX_8_UNUSED_ID,
 	MCTX_9_RESERVED_GLIBC_ID,	/*  9=01001; glibc malloc'd chunks */
 	MCTX_10_RESERVED_GLIBC_ID,	/* 10=01010; glibc malloc'd chunks > 128kB */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index f3b8641d76..98b18af91b 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -335,6 +335,8 @@ BulkInsertState
 BulkInsertStateData
 BulkWriteBuffer
 BulkWriteState
+BumpBlock
+BumpContext
 CACHESIGN
 CAC_state
 CCFastEqualFN
-- 
2.40.1

v5-0001-Add-bitspace-for-more-memory-context-types-in-Mem.patchapplication/octet-stream; name=v5-0001-Add-bitspace-for-more-memory-context-types-in-Mem.patchDownload
From e025bab12717f17bf551e5e9713a698e1b0f68d5 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Thu, 4 Apr 2024 21:34:46 +0200
Subject: [PATCH v5 1/3] Add bitspace for more memory context types in
 MemoryChunk's hdrmask

Assuming we don't want to use patterns from unused memory, common
glibc malloc patterns, and wipe_mem-ed memory, we now have 18 new IDs
available, from previously 0.

In passing, simplify/clean up initialization of unused memory contexts
in the mcxt_methods array, and update the naming scheme of unused and
reserved MemoryContextMethodIDs so that we don't have to touch all
lines to assign a new method ID, and that reserved IDs are clearer
about why they're reserved.

Inspiration: https://postgr.es/m/CAApHDvqGSpCU95TmM%3DBp%3D6xjL_nLys4zdZOpfNyWBk97Xrdj2w%40mail.gmail.com
---
 src/backend/utils/mmgr/mcxt.c            | 63 ++++++++++++++++--------
 src/include/utils/memutils_internal.h    | 44 +++++++++++++++--
 src/include/utils/memutils_memorychunk.h | 15 +++---
 3 files changed, 90 insertions(+), 32 deletions(-)

diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 5d426795d9..de83a5b047 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -102,26 +102,49 @@ static const MemoryContextMethods mcxt_methods[] = {
 	 * seems sufficient to provide routines for the methods that might get
 	 * invoked from inspection of a chunk (see MCXT_METHOD calls below).
 	 */
-
-	[MCTX_UNUSED1_ID].free_p = BogusFree,
-	[MCTX_UNUSED1_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED1_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED1_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED2_ID].free_p = BogusFree,
-	[MCTX_UNUSED2_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED2_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED2_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED3_ID].free_p = BogusFree,
-	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
+#define BOGUS_MCTX(id) \
+	[id].free_p = BogusFree, \
+	[id].realloc = BogusRealloc, \
+	[id].get_chunk_context = BogusGetChunkContext, \
+	[id].get_chunk_space = BogusGetChunkSpace
+
+	BOGUS_MCTX(MCTX_7_UNUSED_ID),
+	BOGUS_MCTX(MCTX_8_UNUSED_ID),
+	BOGUS_MCTX(MCTX_11_UNUSED_ID),
+	BOGUS_MCTX(MCTX_12_UNUSED_ID),
+	BOGUS_MCTX(MCTX_13_UNUSED_ID),
+	BOGUS_MCTX(MCTX_14_UNUSED_ID),
+	BOGUS_MCTX(MCTX_15_UNUSED_ID),
+	BOGUS_MCTX(MCTX_16_UNUSED_ID),
+	BOGUS_MCTX(MCTX_19_UNUSED_ID),
+	BOGUS_MCTX(MCTX_20_UNUSED_ID),
+	BOGUS_MCTX(MCTX_21_UNUSED_ID),
+	BOGUS_MCTX(MCTX_22_UNUSED_ID),
+	BOGUS_MCTX(MCTX_23_UNUSED_ID),
+	BOGUS_MCTX(MCTX_24_UNUSED_ID),
+	BOGUS_MCTX(MCTX_27_UNUSED_ID),
+	BOGUS_MCTX(MCTX_28_UNUSED_ID),
+	BOGUS_MCTX(MCTX_29_UNUSED_ID),
+	BOGUS_MCTX(MCTX_30_UNUSED_ID),
+	/*
+	 * Reserved IDs with bit patterns that we'd see if we were working on
+	 * invalid memory (uninitialized or mem_wiped)
+	 */
+	BOGUS_MCTX(MCTX_RESERVED_UNUSEDMEM_ID),
+	BOGUS_MCTX(MCTX_RESERVED_WIPEMEM_ID),
+	/*
+	 * Reserved IDs with bit patterns that we'd see if we were working with
+	 * a glibc malloc chunk header, and not a memutils_memorychunk.h chunk.
+	 */
+	BOGUS_MCTX(MCTX_1_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_2_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_9_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_10_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_17_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_18_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_25_RESERVED_GLIBC_ID),
+	BOGUS_MCTX(MCTX_26_RESERVED_GLIBC_ID)
+#undef BOGUS_MCTX
 };
 
 /*
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index ad1048fd82..f6ec1962db 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -101,24 +101,58 @@ extern Size AlignedAllocGetChunkSpace(void *pointer);
  * malloc not palloc.  (We can't tell that for most malloc implementations,
  * but it happens that glibc stores flag bits in the same place where we put
  * the MemoryContextMethodID, so the possible values are predictable for it.)
+ *
+ * MemoryContextMethodIDs that look like known likely bogus pointer patterns
+ * are reserved:
+ *		- zero-ed and unused memory.
+ *		- mem_wipe-d memory
+ *		- malloc's 0b...001 and 0b...010 chunk patterns for normal and >128kB
+ *		   chunks, respectively.
+ * This leaves us with (2^n_bits) * (3/4) - 2 available IDs, or 22 options
+ * with the current allocation of 5 bits (see MEMORY_CONTEXT_METHODID_BITS).
  */
 typedef enum MemoryContextMethodID
 {
-	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
-	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_RESERVED_UNUSEDMEM_ID,	/*  0; occurs in never-used memory */
+	MCTX_1_RESERVED_GLIBC_ID,	/*  1=00001; glibc malloc'd chunks */
+	MCTX_2_RESERVED_GLIBC_ID,	/*  2=00010; glibc malloc'd chunks > 128kB */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID,			/* 111 occurs in wipe_mem'd memory */
+	MCTX_7_UNUSED_ID,
+	MCTX_8_UNUSED_ID,
+	MCTX_9_RESERVED_GLIBC_ID,	/*  9=01001; glibc malloc'd chunks */
+	MCTX_10_RESERVED_GLIBC_ID,	/* 10=01010; glibc malloc'd chunks > 128kB */
+	MCTX_11_UNUSED_ID,
+	MCTX_12_UNUSED_ID,
+	MCTX_13_UNUSED_ID,
+	MCTX_14_UNUSED_ID,
+	MCTX_15_UNUSED_ID,
+	MCTX_16_UNUSED_ID,
+	MCTX_17_RESERVED_GLIBC_ID,	/* 17=10001; glibc malloc'd chunk */
+	MCTX_18_RESERVED_GLIBC_ID,	/* 18=10010; glibc malloc'd chunk > 128kB */
+	MCTX_19_UNUSED_ID,
+	MCTX_20_UNUSED_ID,
+	MCTX_21_UNUSED_ID,
+	MCTX_22_UNUSED_ID,
+	MCTX_23_UNUSED_ID,
+	MCTX_24_UNUSED_ID,
+	MCTX_25_RESERVED_GLIBC_ID,	/* 25=11001; glibc malloc'd chunks */
+	MCTX_26_RESERVED_GLIBC_ID,	/* 26=11010; glibc malloc'd chunks > 128kB */
+	MCTX_27_UNUSED_ID,
+	MCTX_28_UNUSED_ID,
+	MCTX_29_UNUSED_ID,
+	MCTX_30_UNUSED_ID,
+	MCTX_RESERVED_WIPEMEM_ID	/* occurs in wipe_mem'd memory (0x7F) */
+#define MaxMemoryContextMethodID (MCTX_UNUSED28_ID)
 } MemoryContextMethodID;
 
 /*
  * The number of bits that 8-byte memory chunk headers can use to encode the
  * MemoryContextMethodID.
  */
-#define MEMORY_CONTEXT_METHODID_BITS 3
+#define MEMORY_CONTEXT_METHODID_BITS 5
 #define MEMORY_CONTEXT_METHODID_MASK \
 	((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
 
diff --git a/src/include/utils/memutils_memorychunk.h b/src/include/utils/memutils_memorychunk.h
index 38296abe1b..86ed28afab 100644
--- a/src/include/utils/memutils_memorychunk.h
+++ b/src/include/utils/memutils_memorychunk.h
@@ -25,14 +25,14 @@
  * used to encode 4 separate pieces of information.  Starting with the least
  * significant bits of 'hdrmask', the bit space is reserved as follows:
  *
- * 1.	3-bits to indicate the MemoryContextMethodID as defined by
+ * 1.	5-bits to indicate the MemoryContextMethodID as defined by
  *		MEMORY_CONTEXT_METHODID_MASK
  * 2.	1-bit to denote an "external" chunk (see below)
  * 3.	30-bits reserved for the MemoryContext to use for anything it
  *		requires.  Most MemoryContext likely want to store the size of the
  *		chunk here.
- * 4.	30-bits for the number of bytes that must be subtracted from the chunk
- *		to obtain the address of the block that the chunk is stored on.
+ * 4.	28-bits for the multiple of 4 bytes that must be subtracted from the
+ *		chunk to obtain the address of the block that the chunk is stored on.
  *
  * In some cases, for example when memory allocations become large, it's
  * possible fields 3 and 4 above are not large enough to store the values
@@ -91,7 +91,7 @@
  * The maximum distance in bytes that a MemoryChunk can be offset from the
  * block that is storing the chunk.  Must be 1 less than a power of 2.
  */
-#define MEMORYCHUNK_MAX_BLOCKOFFSET		UINT64CONST(0x3FFFFFFF)
+#define MEMORYCHUNK_MAX_BLOCKOFFSET		UINT64CONST(0xFFFFFFF)
 
 /* define the least significant base-0 bit of each portion of the hdrmask */
 #define MEMORYCHUNK_EXTERNAL_BASEBIT	MEMORY_CONTEXT_METHODID_BITS
@@ -135,7 +135,7 @@ typedef struct MemoryChunk
  * optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
  */
 #define HdrMaskBlockOffset(hdrmask) \
-	(((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
+	((((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET) * 4)
 
 /* For external chunks only, check the magic number matches */
 #define HdrMaskCheckMagic(hdrmask) \
@@ -157,11 +157,12 @@ MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
 	Size		blockoffset = (char *) chunk - (char *) block;
 
 	Assert((char *) chunk >= (char *) block);
-	Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+	Assert((blockoffset % 4) == 0);
+	Assert((blockoffset / 4) <= MEMORYCHUNK_MAX_BLOCKOFFSET);
 	Assert(value <= MEMORYCHUNK_MAX_VALUE);
 	Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
 
-	chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
+	chunk->hdrmask = (((uint64) (blockoffset / 4)) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
 		(((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) |
 		methodid;
 }
-- 
2.40.1

v5-0003-Use-bump-memory-context-for-tuplesorts.patchapplication/octet-stream; name=v5-0003-Use-bump-memory-context-for-tuplesorts.patchDownload
From e5389070d3289e240e2391e343812e45315c11c0 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 22:23:42 +1300
Subject: [PATCH v5 3/3] Use bump memory context for tuplesorts

---
 src/backend/utils/sort/tuplesort.c         | 52 ++++++++++++----------
 src/backend/utils/sort/tuplesortvariants.c | 38 +++++++++++++---
 src/include/utils/tuplesort.h              | 21 ++++++---
 3 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index f50a9c1a8e..7c4d6dc106 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -191,6 +191,11 @@ struct Tuplesortstate
 								 * tuples to return? */
 	bool		boundUsed;		/* true if we made use of a bounded heap */
 	int			bound;			/* if bounded, the maximum number of tuples */
+	int64		tupleMem;		/* memory consumed by individual tuples.
+								 * storing this separately from what we track
+								 * in availMem allows us to subtract the
+								 * memory consumed by all tuples when dumping
+								 * tuples to tape */
 	int64		availMem;		/* remaining memory available, in bytes */
 	int64		allowedMem;		/* total memory allowed, in bytes */
 	int			maxTapes;		/* max number of input tapes to merge in each
@@ -764,18 +769,18 @@ tuplesort_begin_batch(Tuplesortstate *state)
 	 * in the parent context, not this context, because there is no need to
 	 * free memtuples early.  For bounded sorts, tuples may be pfreed in any
 	 * order, so we use a regular aset.c context so that it can make use of
-	 * free'd memory.  When the sort is not bounded, we make use of a
-	 * generation.c context as this keeps allocations more compact with less
-	 * wastage.  Allocations are also slightly more CPU efficient.
-	 */
-	if (state->base.sortopt & TUPLESORT_ALLOWBOUNDED)
+	 * free'd memory.  When the sort is not bounded, we make use of a bump.c
+	 * context as this keeps allocations more compact with less wastage.
+	 * Allocations are also slightly more CPU efficient.
+	 */
+	if (TupleSortUseBumpTupleCxt(state->base.sortopt))
+		state->base.tuplecontext = BumpContextCreate(state->base.sortcontext,
+													 "Caller tuples",
+													 ALLOCSET_DEFAULT_SIZES);
+	else
 		state->base.tuplecontext = AllocSetContextCreate(state->base.sortcontext,
 														 "Caller tuples",
 														 ALLOCSET_DEFAULT_SIZES);
-	else
-		state->base.tuplecontext = GenerationContextCreate(state->base.sortcontext,
-														   "Caller tuples",
-														   ALLOCSET_DEFAULT_SIZES);
 
 
 	state->status = TSS_INITIAL;
@@ -1181,15 +1186,16 @@ noalloc:
  * Shared code for tuple and datum cases.
  */
 void
-tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev)
+tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple,
+						  bool useAbbrev, Size tuplen)
 {
 	MemoryContext oldcontext = MemoryContextSwitchTo(state->base.sortcontext);
 
 	Assert(!LEADER(state));
 
-	/* Count the size of the out-of-line data */
-	if (tuple->tuple != NULL)
-		USEMEM(state, GetMemoryChunkSpace(tuple->tuple));
+	/* account for the memory used for this tuple */
+	USEMEM(state, tuplen);
+	state->tupleMem += tuplen;
 
 	if (!useAbbrev)
 	{
@@ -2397,13 +2403,6 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 		SortTuple  *stup = &state->memtuples[i];
 
 		WRITETUP(state, state->destTape, stup);
-
-		/*
-		 * Account for freeing the tuple, but no need to do the actual pfree
-		 * since the tuplecontext is being reset after the loop.
-		 */
-		if (stup->tuple != NULL)
-			FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	}
 
 	state->memtupcount = 0;
@@ -2411,12 +2410,19 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 	/*
 	 * Reset tuple memory.  We've freed all of the tuples that we previously
 	 * allocated.  It's important to avoid fragmentation when there is a stark
-	 * change in the sizes of incoming tuples.  Fragmentation due to
-	 * AllocSetFree's bucketing by size class might be particularly bad if
-	 * this step wasn't taken.
+	 * change in the sizes of incoming tuples.  In bounded sorts,
+	 * fragmentation due to AllocSetFree's bucketing by size class might be
+	 * particularly bad if this step wasn't taken.
 	 */
 	MemoryContextReset(state->base.tuplecontext);
 
+	/*
+	 * Now update the memory accounting to subtract the memory used by the
+	 * tuple.
+	 */
+	FREEMEM(state, state->tupleMem);
+	state->tupleMem = 0;
+
 	markrunend(state->destTape);
 
 #ifdef TRACE_SORT
diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c
index 11de12d745..05a853caa3 100644
--- a/src/backend/utils/sort/tuplesortvariants.c
+++ b/src/backend/utils/sort/tuplesortvariants.c
@@ -674,6 +674,7 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 	SortTuple	stup;
 	MinimalTuple tuple;
 	HeapTupleData htup;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tuple = ExecCopySlotMinimalTuple(slot);
@@ -686,9 +687,15 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 							   tupDesc,
 							   &stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -705,6 +712,7 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 	TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tup = heap_copytuple(tup);
@@ -722,10 +730,16 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 								   &stup.isnull1);
 	}
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->haveDatum1 &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -743,6 +757,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 	IndexTuple	tuple;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
+	Size		tuplen;
 
 	stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
 										  isnull, base->tuplecontext);
@@ -754,10 +769,16 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 								RelationGetDescr(arg->indexRel),
 								&stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 }
 
 /*
@@ -770,6 +791,7 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	BrinSortTuple *bstup;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
+	Size		tuplen;
 
 	/* allocate space for the whole BRIN sort tuple */
 	bstup = palloc(BRINSORTTUPLE_SIZE(size));
@@ -781,10 +803,16 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	stup.datum1 = tuple->bt_blkno;
 	stup.isnull1 = false;
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
+	else
+		tuplen = GetMemoryChunkSpace(bstup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -833,7 +861,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
 
 	tuplesort_puttuple_common(state, &stup,
 							  base->tuples &&
-							  base->sortKeys->abbrev_converter && !isNull);
+							  base->sortKeys->abbrev_converter && !isNull, 0);
 
 	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 1013c64dab..e7941a1f09 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -98,6 +98,15 @@ typedef enum
 /* specifies if the tuplesort is able to support bounded sorts */
 #define TUPLESORT_ALLOWBOUNDED			(1 << 1)
 
+/*
+ * For bounded sort, tuples get pfree'd when they fall outside of the bound.
+ * When bounded sorts are not required, we can use a bump context for tuple
+ * allocation as there's no risk that pfree will ever be called for a tuple.
+ * Define a macro to make it easier for code to figure out if we're using a
+ * bump allocator.
+ */
+#define TupleSortUseBumpTupleCxt(opt) (((opt) & TUPLESORT_ALLOWBOUNDED) == 0)
+
 typedef struct TuplesortInstrumentation
 {
 	TuplesortMethod sortMethod; /* sort algorithm used */
@@ -109,10 +118,11 @@ typedef struct TuplesortInstrumentation
  * The objects we actually sort are SortTuple structs.  These contain
  * a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
  * which is a separate palloc chunk --- we assume it is just one chunk and
- * can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
- * column in Datum/nullflag format, and a source/input tape number that
- * tracks which tape each heap element/slot belongs to during merging.
+ * can be freed by a simple pfree() (except during merge, where we use a
+ * simple slab allocator, and during a non-bounded sort where we use a bump
+ * allocator).  SortTuples also contain the tuple's first key column in
+ * Datum/nullflag format, and a source/input tape number that tracks which
+ * tape each heap element/slot belongs to during merging.
  *
  * Storing the first key column lets us save heap_getattr or index_getattr
  * calls during tuple comparisons.  We could extract and save all the key
@@ -367,7 +377,8 @@ extern Tuplesortstate *tuplesort_begin_common(int workMem,
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
 extern bool tuplesort_used_bound(Tuplesortstate *state);
 extern void tuplesort_puttuple_common(Tuplesortstate *state,
-									  SortTuple *tuple, bool useAbbrev);
+									  SortTuple *tuple, bool useAbbrev,
+									  Size tuplen);
 extern void tuplesort_performsort(Tuplesortstate *state);
 extern bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 									  SortTuple *stup);
-- 
2.40.1

#37Tom Lane
tgl@sss.pgh.pa.us
In reply to: Matthias van de Meent (#36)
Re: Add bump memory context type and use it for tuplesorts

Matthias van de Meent <boekewurm+postgres@gmail.com> writes:

On Thu, 4 Apr 2024 at 22:43, Tom Lane <tgl@sss.pgh.pa.us> wrote:

The only objection I can think of is that perhaps this would slow
things down a tad by requiring more complicated shifting/masking.
I wonder if we could redo the performance checks that were done
on the way to accepting the current design.

I didn't do very extensive testing, but the light performance tests
that I did with the palloc performance benchmark patch & script shared
above indicate didn't measure an observable negative effect.

OK. I did not read the patch very closely, but at least in principle
I have no further objections. David, are you planning to take point
on getting this in?

regards, tom lane

#38David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#37)
Re: Add bump memory context type and use it for tuplesorts

On Sat, 6 Apr 2024 at 03:24, Tom Lane <tgl@sss.pgh.pa.us> wrote:

OK. I did not read the patch very closely, but at least in principle
I have no further objections. David, are you planning to take point
on getting this in?

Yes. I'll be looking soon.

David

#39David Rowley
dgrowleyml@gmail.com
In reply to: Matthias van de Meent (#36)
7 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Sat, 6 Apr 2024 at 02:30, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

On Thu, 4 Apr 2024 at 22:43, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Matthias van de Meent <boekewurm+postgres@gmail.com> writes:

It extends memory context IDs to 5 bits (32 values), of which
- 8 have glibc's malloc pattern of 001/010;
- 1 is unused memory's 00000
- 1 is wipe_mem's 11111
- 4 are used by existing contexts (Aset/Generation/Slab/AlignedRedirect)
- 18 are newly available.

This seems like it would solve the problem for a good long time
to come; and if we ever need more IDs, we could steal one more bit
by requiring the offset to the block header to be a multiple of 8.
(Really, we could just about do that today at little or no cost ...
machines with MAXALIGN less than 8 are very thin on the ground.)

Hmm, it seems like a decent idea, but I didn't want to deal with the
repercussions of that this late in the cycle when these 2 bits were
still relatively easy to get hold of.

Thanks for writing the patch.

I think 5 bits is 1 too many. 4 seems fine. I also think you've
reserved too many slots in your patch as I disagree that we need to
reserve the glibc malloc pattern anywhere but in the 1 and 2 slots of
the mcxt_methods[] array. I looked again at the 8 bytes prior to a
glibc malloc'd chunk and I see the lowest 4 bits of the headers
consistently set to 0001 for all powers of 2 starting at 8 up to
65536. 131072 seems to vary and beyond that, they seem to be set to
0010.

With that, there's no increase in the number of reserved slots from
what we have reserved today. Still 4. So having 4 bits instead of 3
bits gives us a total of 12 slots rather than 4 slots. Having 3x
slots seems enough. We might need an extra bit for something else
sometime. I think keeping it up our sleeve is a good idea.

Another reason not to make it 5 bits is that I believe that would make
the mcxt_methods[] array 2304 bytes rather than 576 bytes. 4 bits
makes it 1152 bytes, if I'm counting correctly.

I revised the patch to simplify hdrmask logic. This started with me
having trouble finding the best set of words to document that the
offset is "half the bytes between the chunk and block". So, instead
of doing that, I've just made it so these two fields effectively
overlap. The lowest bit of the block offset is the same bit as the
high bit of what MemoryChunkGetValue returns. I've just added an
Assert to MemoryChunkSetHdrMask to ensure that the low bit is never
set in the offset. Effectively, using this method, there's no new *C*
code to split the values out. However, the compiler will emit one
additional bitwise-AND to implement the following, which I'll express
using fragments from the 0001 patch:

#define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF)

+#define MEMORYCHUNK_BLOCKOFFSET_MASK UINT64CONST(0x3FFFFFFE)

#define HdrMaskBlockOffset(hdrmask) \
- (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
+ (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) &
MEMORYCHUNK_BLOCKOFFSET_MASK)

Previously most compilers would have optimised the bitwise-AND away as
it was effectively similar to doing something like (0xFFFFFFFF >> 16)
& 0xFFFF. The compiler should know that no bits can be masked out by
the bitwise-AND due to the left shift zeroing them all. If you swap
0xFFFF for 0xFFFE then that's no longer true.

I also updated src/backend/utils/mmgr/README to explain this and
adjust the mentions of 3-bits and 61-bits to 4-bits and 60-bits. I
also explained the overlapping part.

I spent quite a bit of time benchmarking this. There is a small
performance regression from widening to 4 bits, but it's not huge.
Please see the 3 attached graphs. All times in the graph are the
average of the time taken for each test over 9 runs.

bump_palloc_reset.png: Shows the results from:

select stype,chksz,avg(pg_allocate_memory_test_reset(chksz,1024*1024,10::bigint*1024*1024*1024,stype))
from (values(8),(16),(32),(64),(128)) t1(chksz)
cross join (values('bump')) t2(stype)
cross join generate_series(1,3) r(run)
group by stype,chksz
order by stype,chksz;

There's no performance regression here. Bump does not have headers so
no extra bits are used anywhere.

aset_palloc_pfree.png: Shows the results from:

select stype,chksz,avg(pg_allocate_memory_test(chksz,1024*1024,10::bigint*1024*1024*1024,stype))
from (values(8),(16),(32),(64),(128)) t1(chksz)
cross join (values('aset')) t2(stype)
cross join generate_series(1,3) r(run)
group by stype,chksz
order by stype,chksz;

This exercises palloc and pfree. Effectively it's allocating 10GB of
memory but starting to pfree before each new palloc after we get to
1MB of concurrent allocations. Because this test calls pfree, we need
to look at the chunk header and into the mcxt_methods[] array. It's
important to test this part.

The graph shows a small performance regression of about 1-2%.

generation_palloc_pfree.png: Same as aset_palloc_pfree.png but for the
generation context. The regression here is slightly more than aset.
Seems to be about 2-3.5%. I don't think this is too surprising as
there's more room for instruction-level parallelism in AllocSetFree()
when calling MemoryChunkGetBlock() than there is in GenerationFree().
In GenerationFree() we get the block and then immediately do
"block->nfree += 1;", whereas in AllocSetFree() we also call
MemoryChunkGetValue().

I've attached an updated set of patches, plus graphs, plus entire
benchmark results as a .txt file.

Note the v6-0003 patch is just v4-0002 renamed so the CFbot applies in
the correct order.

I'm planning on pushing these, pending a final look at 0002 and 0003
on Sunday morning NZ time (UTC+12), likely in about 10 hours time.

David

Attachments:

v6-0001-Enlarge-bit-space-for-MemoryContextMethodID.patchtext/plain; charset=US-ASCII; name=v6-0001-Enlarge-bit-space-for-MemoryContextMethodID.patchDownload
From 59b269fcde648c02d470c49f74d4edf16cc227b0 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Sat, 6 Apr 2024 21:58:07 +1300
Subject: [PATCH v6 1/2] Enlarge bit-space for MemoryContextMethodID

Reserve 4 bits for MemoryContextMethodID rather than 3.  3 bits did
technically allow a maximum of 8 memory context types, however, we've
opted to reserve some bit patterns which left us with only 4 slots, all
of which were used.

Here we add another bit which frees up 8 slots for future memory context
types.

In passing, adjust the enum names in MemoryContextMethodID to make it
more clear which ones can be used and which ones are reserved.

Author: Matthias van de Meent, David Rowley
Discussion: https://postgr.es/m/CAApHDvqGSpCU95TmM=Bp=6xjL_nLys4zdZOpfNyWBk97Xrdj2w@mail.gmail.com
---
 src/backend/utils/mmgr/README            | 21 ++++++++----
 src/backend/utils/mmgr/mcxt.c            | 41 +++++++++++++-----------
 src/include/utils/memutils_internal.h    | 18 ++++++++---
 src/include/utils/memutils_memorychunk.h | 30 +++++++++++++----
 4 files changed, 72 insertions(+), 38 deletions(-)

diff --git a/src/backend/utils/mmgr/README b/src/backend/utils/mmgr/README
index b20b9d4852..f484f7d6f5 100644
--- a/src/backend/utils/mmgr/README
+++ b/src/backend/utils/mmgr/README
@@ -395,14 +395,14 @@ relevant MemoryContext as a parameter, operations like free and
 realloc are trickier.  To make those work, we require all memory
 context types to produce allocated chunks that are immediately,
 without any padding, preceded by a uint64 value of which the least
-significant 3 bits are set to the owning context's MemoryContextMethodID.
+significant 4 bits are set to the owning context's MemoryContextMethodID.
 This allows the code to determine the correct MemoryContextMethods to
-use by looking up the mcxt_methods[] array using the 3 bits as an index
+use by looking up the mcxt_methods[] array using the 4 bits as an index
 into that array.
 
 If a type of allocator needs additional information about its chunks,
 like e.g. the size of the allocation, that information can in turn
-either be encoded into the remaining 61 bits of the preceding uint64 value
+either be encoded into the remaining 60 bits of the preceding uint64 value
 or if more space is required, additional values may be stored directly prior
 to the uint64 value.  It is up to the context implementation to manage this.
 
@@ -420,13 +420,20 @@ pfree(void *pointer)
 
 All of the current memory contexts make use of the MemoryChunk header type
 which is defined in memutils_memorychunk.h.  This suits all of the existing
-context types well as it makes use of the remaining 61-bits of the uint64
+context types well as it makes use of the remaining 60-bits of the uint64
 header to efficiently encode the size of the chunk of memory (or freelist
 index, in the case of aset.c) and the number of bytes which must be subtracted
 from the chunk in order to obtain a reference to the block that the chunk
-belongs to.  30 bits are used for each of these.  If more than 30 bits are
-required then the memory context must manage that itself.  This can be done by
-calling the MemoryChunkSetHdrMaskExternal() function on the given chunk.
+belongs to.  30 bits are used for each of these, but only a total of 59 bits
+as the lowest bit for the chunk to block offset is the same bit as the highest
+bit of the chunk size.  This overlapping is possible as the relative offset
+between the block and the chunk is expected to be a MAXALIGNed value which
+guarantees the lowest bit is always 0.  If more than 30 bits are required for
+each of these fields then the memory context must manage that itself.  This
+can be done by calling the MemoryChunkSetHdrMaskExternal() function on the
+given chunk.  Whether a chunk is an external chunk can be determined by the 1
+remaining bit from the 64-bit MemoryChunk.
+
 Currently, each memory context type stores large allocations on dedicated
 blocks (which always contain only a single chunk).  For these, finding the
 block is simple as we know that the chunk must be the first on the given
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 5d426795d9..52ae120af9 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -37,6 +37,11 @@ static Size BogusGetChunkSpace(void *pointer);
 /*****************************************************************************
  *	  GLOBAL MEMORY															 *
  *****************************************************************************/
+#define BOGUS_MCTX(id) \
+	[id].free_p = BogusFree, \
+	[id].realloc = BogusRealloc, \
+	[id].get_chunk_context = BogusGetChunkContext, \
+	[id].get_chunk_space = BogusGetChunkSpace
 
 static const MemoryContextMethods mcxt_methods[] = {
 	/* aset.c */
@@ -103,27 +108,25 @@ static const MemoryContextMethods mcxt_methods[] = {
 	 * invoked from inspection of a chunk (see MCXT_METHOD calls below).
 	 */
 
-	[MCTX_UNUSED1_ID].free_p = BogusFree,
-	[MCTX_UNUSED1_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED1_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED1_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED2_ID].free_p = BogusFree,
-	[MCTX_UNUSED2_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED2_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED2_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED3_ID].free_p = BogusFree,
-	[MCTX_UNUSED3_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
-
-	[MCTX_UNUSED4_ID].free_p = BogusFree,
-	[MCTX_UNUSED4_ID].realloc = BogusRealloc,
-	[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
-	[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
+	BOGUS_MCTX(MCTX_7_UNUSED_ID),
+	BOGUS_MCTX(MCTX_8_UNUSED_ID),
+	BOGUS_MCTX(MCTX_9_UNUSED_ID),
+	BOGUS_MCTX(MCTX_10_UNUSED_ID),
+	BOGUS_MCTX(MCTX_11_UNUSED_ID),
+	BOGUS_MCTX(MCTX_12_UNUSED_ID),
+	BOGUS_MCTX(MCTX_13_UNUSED_ID),
+	BOGUS_MCTX(MCTX_14_UNUSED_ID),
+
+	/*
+	 * Reserved IDs with bit patterns that we'd see if we were working on
+	 * invalid memory, either uninitialized or wiped.
+	 */
+	BOGUS_MCTX(MCTX_0_RESERVED_UNUSEDMEM_ID),
+	BOGUS_MCTX(MCTX_15_RESERVED_WIPEMEM_ID),
 };
 
+#undef BOGUS_MCTX
+
 /*
  * CurrentMemoryContext
  *		Default memory context for allocations.
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index ad1048fd82..c3f010b595 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -104,21 +104,29 @@ extern Size AlignedAllocGetChunkSpace(void *pointer);
  */
 typedef enum MemoryContextMethodID
 {
-	MCTX_UNUSED1_ID,			/* 000 occurs in never-used memory */
-	MCTX_UNUSED2_ID,			/* glibc malloc'd chunks usually match 001 */
-	MCTX_UNUSED3_ID,			/* glibc malloc'd chunks > 128kB match 010 */
+	MCTX_0_RESERVED_UNUSEDMEM_ID,	/*  0; occurs in never-used memory */
+	MCTX_1_RESERVED_GLIBC_ID,	/*  1=0001; glibc malloc'd chunks */
+	MCTX_2_RESERVED_GLIBC_ID,	/*  2=0010; glibc malloc'd chunks > 128kB */
 	MCTX_ASET_ID,
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_UNUSED4_ID,			/* 111 occurs in wipe_mem'd memory */
+	MCTX_7_UNUSED_ID,
+	MCTX_8_UNUSED_ID,
+	MCTX_9_UNUSED_ID,
+	MCTX_10_UNUSED_ID,
+	MCTX_11_UNUSED_ID,
+	MCTX_12_UNUSED_ID,
+	MCTX_13_UNUSED_ID,
+	MCTX_14_UNUSED_ID,
+	MCTX_15_RESERVED_WIPEMEM_ID	/* 1111 occurs in wipe_mem'd memory (0x7F) */
 } MemoryContextMethodID;
 
 /*
  * The number of bits that 8-byte memory chunk headers can use to encode the
  * MemoryContextMethodID.
  */
-#define MEMORY_CONTEXT_METHODID_BITS 3
+#define MEMORY_CONTEXT_METHODID_BITS 4
 #define MEMORY_CONTEXT_METHODID_MASK \
 	((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
 
diff --git a/src/include/utils/memutils_memorychunk.h b/src/include/utils/memutils_memorychunk.h
index 38296abe1b..948a5ac954 100644
--- a/src/include/utils/memutils_memorychunk.h
+++ b/src/include/utils/memutils_memorychunk.h
@@ -12,7 +12,7 @@
  * Although MemoryChunks are used by each of our MemoryContexts, future
  * implementations may choose to implement their own method for storing chunk
  * headers.  The only requirement is that the header ends with an 8-byte value
- * which the least significant 3-bits of are set to the MemoryContextMethodID
+ * which the least significant 4-bits of are set to the MemoryContextMethodID
  * of the given context.
  *
  * By default, a MemoryChunk is 8 bytes in size, however, when
@@ -25,7 +25,7 @@
  * used to encode 4 separate pieces of information.  Starting with the least
  * significant bits of 'hdrmask', the bit space is reserved as follows:
  *
- * 1.	3-bits to indicate the MemoryContextMethodID as defined by
+ * 1.	4-bits to indicate the MemoryContextMethodID as defined by
  *		MEMORY_CONTEXT_METHODID_MASK
  * 2.	1-bit to denote an "external" chunk (see below)
  * 3.	30-bits reserved for the MemoryContext to use for anything it
@@ -34,6 +34,14 @@
  * 4.	30-bits for the number of bytes that must be subtracted from the chunk
  *		to obtain the address of the block that the chunk is stored on.
  *
+ * If you're paying close attention, you'll notice this adds up to 65 bits
+ * rather than 64 bits.  This is because the highest order bit of #3 is the
+ * same bit as the lowest order bit of #4.  We can do this as we insist that
+ * the chunk and block pointers are both MAXALIGNed, therefore the relative
+ * offset between those will always be a MAXALIGNed value which means the
+ * lowest order bit is always 0.  When fetching the the chunk to block offset
+ * we mask out the lowest-order bit to ensure it's still zero.
+ *
  * In some cases, for example when memory allocations become large, it's
  * possible fields 3 and 4 above are not large enough to store the values
  * required for the chunk.  In this case, the MemoryContext can choose to mark
@@ -93,10 +101,16 @@
  */
 #define MEMORYCHUNK_MAX_BLOCKOFFSET		UINT64CONST(0x3FFFFFFF)
 
+/*
+ * As above, but mask out the lowest-order (always zero) bit as this is shared
+ * with the MemoryChunkGetValue field.
+ */
+#define MEMORYCHUNK_BLOCKOFFSET_MASK 	UINT64CONST(0x3FFFFFFE)
+
 /* define the least significant base-0 bit of each portion of the hdrmask */
 #define MEMORYCHUNK_EXTERNAL_BASEBIT	MEMORY_CONTEXT_METHODID_BITS
 #define MEMORYCHUNK_VALUE_BASEBIT		(MEMORYCHUNK_EXTERNAL_BASEBIT + 1)
-#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT	(MEMORYCHUNK_VALUE_BASEBIT + 30)
+#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT	(MEMORYCHUNK_VALUE_BASEBIT + 29)
 
 /*
  * A magic number for storing in the free bits of an external chunk.  This
@@ -131,11 +145,11 @@ typedef struct MemoryChunk
 	(((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE)
 
 /*
- * We should have used up all the bits here, so the compiler is likely to
- * optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
+ * We should have used up all the bits here, we just need to ensure we mask
+ * out the low-order bit that's shared with the MemoryChunkGetValue field.
  */
 #define HdrMaskBlockOffset(hdrmask) \
-	(((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
+	(((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_BLOCKOFFSET_MASK)
 
 /* For external chunks only, check the magic number matches */
 #define HdrMaskCheckMagic(hdrmask) \
@@ -149,6 +163,7 @@ typedef struct MemoryChunk
  * The number of bytes between 'block' and 'chunk' must be <=
  * MEMORYCHUNK_MAX_BLOCKOFFSET.
  * 'value' must be <= MEMORYCHUNK_MAX_VALUE.
+ * Both 'chunk' and 'block' must be MAXALIGNed pointers.
  */
 static inline void
 MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
@@ -157,7 +172,7 @@ MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
 	Size		blockoffset = (char *) chunk - (char *) block;
 
 	Assert((char *) chunk >= (char *) block);
-	Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+	Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset);
 	Assert(value <= MEMORYCHUNK_MAX_VALUE);
 	Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
 
@@ -225,6 +240,7 @@ MemoryChunkGetBlock(MemoryChunk *chunk)
 }
 
 /* cleanup all internal definitions */
+#undef MEMORYCHUNK_BLOCKOFFSET_MASK
 #undef MEMORYCHUNK_EXTERNAL_BASEBIT
 #undef MEMORYCHUNK_VALUE_BASEBIT
 #undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
-- 
2.40.1.windows.1

bump_palloc_reset.pngimage/png; name=bump_palloc_reset.pngDownload
�PNG


IHDR����sRGB���gAMA���a	pHYs�����e��IDATx^��|u���o
I��-X�C�*�(  �X��
��a������b=9���`/XP�
X@=P�!�*%������d�l�M��%�y�^������i;���$1[T��?�-���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P����"�c[��7���?n?���{��^{���C�c����S��w�q���f;����Q�F��o���6o�<��m��v��gZFF�{��-77���k.��wLAA���?�>��#���_m��
nx��um�w��;��W������.cW�Z����������O[�~�.�����Ak���u�����kgu��q��<������i�Z�r��c��������F���_~i3g���K�Z^^�^�~}�n����SN�z���a0���o����r���g�m�[����-���KJJr��S�N����6{��7�g!e}�������c��k�h������v����o?k���[n[7J�������^�u���_�![?]\�����&��6��_}��}������[�r�����F����+�D�>��CnM[�����g�������&M�����u��C:Q��z�j�;w�=���v�
7�k��V���P��f�R>���v�M7�{����#>h����&L��~P���������3��}��D������o�C:�L�6��z�*�+:�)��=b��y�*�����?��O����A�F��U�\�=�>���Zq����r�m������gDb�m���������r�@U����G�%K�xC��i4�^S������������m�?��m/������T�[�u����V���>h����74�}�������{�6�R�*���P(X{��wm�������y���������3�W~��Z�#(��Rn�O������/�p�{d�m��=���=�*i������
UK�{��7�v�����J��%�T��e���q<����KL��M��]7���R���{��.]�����j�����q����+�!���������1U��:����nG}�������c���^��L�������
ZTj-H��t�z��6`�;��C�����w���4i�>�|JNN��v�|��7��n*x�=�8���U.�_�f�\h����Z'M�6u������+n���������c*`U��2�p�	��W/�������N�
���=T������.����������A���Z�u��������e�{
���uN�v?�����m�P6x~S�J���i*S�@��D@�S������\U���nvE!]�>}��3�p7�
���;���z��Y����?����VD%[^z��R�u��6����:2d�'���\��=�����+���/��s>�GmG�?|U$--�����������u�Y���P�U���>(u3
TD�h����������o��o�G��D��n��Y�
|uTyUUb?��j�Wm;?�y�*�q*��t���t�Qh{�W�����������c��A@S�U����b
�Tz���L����*H7�
�4����O>��k����h������������.6b�W����`-�BBu�,!��i
+{^��y��n}���{�V�C�Q'%���
�U�PmVGH':~���R�u��e���U�V�mcI�4�x��9M���
4P�$ U�3g�=��.���J�������*S����\i��X|S����k�NU�t���y�����N�tO�����W����&b���>�J��(l-��[��Au����=
6��n����_�CDe�]�����
�+�TE7XjX�5J�['�:��v��x��RUI�V�9��S�F/Z�)�K�����;����u�����(�Z�`�{N`���nx�]��u�!���TH��Mx�J��Zb0��[�jTJL�l�����o���
S���;��d�:������j�����9���F�������?B�|S�������������������~��=W{<C�u-*��R�����~�W�@�����N%	���������W9�#TQ�
�����O�������v7_A�i��U55���vu����.2TUH�����T����xuS�����������][D����T�A*U�qZ�7�������cL���_�����K���q��(�q`�MR�e���N�_B�������F�����fZ�jwE�����U���F�g���<������������;�����������j=z�p�C��v�������1z����[nc��5Z��#�����T-&x,�%�c�"��*Q�}E7�����}F�}���s7��fR/��*$
UT0������k����u����6������h�q>��P���8����c����z�>��sU��h�����u�Q�y����h~�=��kITzDm����n~u���Q�9Y���u>s�L���O��w�q��^�2v�X�_v���-s��
e+����SO=����}1�[�#��Y������8w�����}Y���8P�_-��i��s��{�A��+�V����s����]��:�^������>�n��V��U���<^�=����<��z�k|s��+-��	��k����}�G���kt<�����)����C��/�[�s��|���>�c^�7u��_*s,�oS�����u^����F�.�$�����^�s�����)���ZoZ:�����������:����=���������&J��^��
let����n4�����^��Y7�`�������FY77���l/�
4�����Vp�W7E}���yQ���>��W��Z~�f�CM��#���.�g����D7�.���������j���t�8�����i���U2D�]�t#��R�~P���i|e�A����^s!��E�l��u�����q_�MSy@y�L��Q
nm�
e�g��_-S�&us����������������s
�����/�"X7q�T�E�Z���K7%�9�t�y���n���O7�c�,�K��q�~��W�����������=���n6������|���
�����4/�W��i�y������':�4��o�q!�>�n��q�*}f�U��Dj�V�'e}� �ZW����r4%Ht����!������}Q�C����EUsU�Na��������}�3eG����c�<�^�o���
'4h��WU��Y���Q�f���zS���z�
��j�O��z+����<x��\������Q?\U�����>���^���i��1��
�5_����w�2)�����Z��~��u^	���(X��^R�������
�T�\�2H�I�_��J��8�uK0$�wR�����I���~U���n���1�}S����}A��u���U��M5��YZ�N�>�����h������w����Y������"
=�#Z>U��W����^?\���������Ut���]�q >Q�����O��0)�.����."��-A������z���|8]������&]�����Q+�.�t�������I�
���T@�����a}�r[�:�i4��Hta��C���vY�/�QvUy�f]E��������T�|��Ry�W������
���b�o���Wf���L8����f�`���^���<���3��,���4N��w P�3+@�D���G{����^�Cu���ys��U�^~��.(��}Z7����2�d�����y����n�7'L�I�NR�2�JGW���;���_��(��g�}Jm[�Gj�P?>Ds����O�����R@}G��M�z��2���cO�4�;��W^��������R���������[���:�b_�u�~	�|:G�x�*t����.s�k+�W��7����k<?�S8�9��2��T��{��PB:`�D�W`+��`�$���u���_?W�NT�@]������$�ZU������+-����d�~Q����e\�
F��������8����S���.�T�c�����|g�u���3H.~U5�+�i�Z�j��A�����W^y�T0�_�U}�_�U�_�+���bJ�JT%HU*��u��H�����A���u��Q7�>�Oa�.b5?}&�ZWZ����:P�����rem��m�y)@��Q�&M���a�J��E���f�������J������u��#���Jkh}�k����q��@hM����y}�Q��D�U�V������TW*��Z����D�i�D�J���f]7O:~��VY������j��gR8<e���������:V��8:/��(���H�������KKD��D�G�������c��*O��?W����h��j��5�����������lt���z��O<�D��&T�����_�0��W���3�������9}�k_�KJJ��D�=��P?Bh����T�'x\�\^J�O�G����t��:����Ye����T�$����W����?�h�q�����*�<�ixY���uN4������l��Z}W���h])t�t�������H�N��E��
Y���X��n���9��c��N�Y�]gi?���6We��A����u����}��c���*��k��W���*[�U�S����Z~gR�q �Q����WK�W��j_������h��)(L�E�.t�����"�������/�*�i�S�L(0T��������Vm��X����H7��2��@�T�/%�������G�����|�^�.dt��u��
-�B3�K%D\�S5MSU��������FE�Cl��S0��C��DcZW�V�{���J��������s�~`�C7
Zv�-�_G��^~�i����n�us�o-�.\���y���n��U���6�}FtS��5_��9���\�8-���h{���n�����+t�rD�fz?M��[M�}��V�^U9u�-Z�G}t����f*X]es�P�T�/��e����s��g�pmk�F:�yD���Y�S�>�Zi�V��k��?�*��Z~���M'�K����Y�O�}nmS�F�]U/�u��b�l�?'�|��_��HtNQu-��
��z��F�.����M�W�����*�����[��������se��=������V���?6*����n���M����\g��N�����7tlh����_i�����5�F?���M}�����
1-�Ji���B��km{9�vz/�v����M���>-�yWA����J��0�_������C��?_�0����_����Y-���tV�:�"��@M$������������e�i���A��_�6:_F�~	�v)�*d��C��$��":w�ZO����/�|�����[���p�e�N���>�=�����J�?�<�}���jY�^�6�|"�g�?�s����5D���'PH�u �bw���t�������q*����qx����������A�i�.���ta��w
�"	� �/�����_���pz�"�PL7����,
j���Q���.��\��Y�>��Y�o�u���&�(�.0u1�_*0�M��Le�28�8|	��q]��tSm5������Z�eY7���h���J'���f�V�M���JY�FDT��/u!�'��$3�*�*��e.�����Y>mW?�n�������\�s�1��U8��0)��w:W(��t��)dP��O���&ms�r�����),�g
>�p=Vv��"Q��tM������
t���];���O�����=�e)��O��5���?�u�)T��
�?�e}������s�hy�#�����m��(��*�/K$�F�bp_��c)V�B$
�MMl�t�m�W}o��j�I��={�,���������b��6(4N�P�]]h��i�u���y��,��\�U9�/���/�Q�Q�z���
��S�\��E���J����G�A�~����	�olT���9-�~���n�Tj��Rc��/�T��D���[p��B%������J������/)o�)�	��1��$���!��t�z*/��������U}]xG��@��}����[��?-�n|}����G�m�������}�����}S����+����:�Hl�X���"��P������������F����8������\[�~x\���H������tMP���O���h��~\����G@��#G������s%�UZ+H%�$�hErj�a���/�k
G��:�T���[��Z�m6���h��� �����w<��,�X����:�E�^Y?���J�������/���_*��������I47��F7�>]��/�A�S
#�7Z��nB�����E������}���U�P�_�U�������Gu�Ru�_�]t�E�������_}]DGC7���@�u�R}e�MJ0����6��}7�)
z��f
�T�����V�~�L�4WuUY��z�K.��m���,�L���/V��JbKc�$G4��8�'�/����|���V?
n��xUn7�|YS�3bK������\���Y��U ��g�^*M��nsK��{D�����_�G�l���
���������T��6X�������*��� �>�����j�>��U�WTJP���G�*����<��k���i����6P��������G?�����U��5�P�<�S	:]O�K�E�G�*>��������ZH47��Y
�
��U[5Mn�?����^��

u!�S�V����Z���*�!H���L����9��y0T+�:����V�����v������%a���K.V��T�2�k����J����x�}J�Q�H���X����RG~{����Ke�}U�TA��O?�Y�\PM���Mxu�-Y�W��JB+�)�O�5i�mw�������|�LZ]�������W�K��`eJj?����vPP�RS>�`UI�;������ZN��^����zRG
��+���>�6���)
�����{���K�5%��B�h����~,	V�T+E�y�y�z�Q�U���3��#�8��7�t�kV�oZB�
����@|!��r���t�����h��X�L���?�_)�pUo���6�V�O�>-cy���/�V�7��A����V��*�d�����
�Z���U�tU���R*j?Oa�C=d��r��Fv��7�`)��[,�r���R����n^t��6�a�z�S5�������S�|
��dM��[�B����*�#<��w\��v���Nm�E*��?�~'��~HS���s����t�e�3�U
T��������B	M��[*e�s���{��J�h�:��-����U�}r�ev������w��{��PJ^l��8��Bm���` ���~�{���`���k�X�������Tp��-e;�	A��t3�F�YE�K����9�7�@���H���
R��x���2L�6��������?o�f�r7�*�o�MU���m��,ZO�TB���9�@������������w���5-<��L���i����]�I���0z��XG*��?�S;������q���7����F�~�����a�\�����k���s��y����|,U�G��RP3f��=���.��:�m�Fc)��P�UT�Um���j35�#oM��S�����D���,��j����*UiG%���Sx����>Z�f�h�L��-A=d�TBU�.���rou����o���s
��-E�Y����v�\�N�$������;*)����bq��&���s�y�KA�t���V�@Z���~z���$�����9%t��j��_���
�����v�bM����h�+����^{�q���O!T��[�B	U�UuWU�U��*�������-/���W)�H���JD���T�P�j��<�iPIm����kwK���P[�W�U!�_�U����Rk��"�����Xud v���\eJiK�(l*���J���w��3�������o����7���?�fUe�W	�l)��������X���?��2��
���6yj�.�,D�C��^m`)�������n�����
S�b%��_u'���_�T�:�U�I%�F��n8�
(�����h����%h���wM4��	o�P�V����}:��������U���i�i�%���9�Gy�k�LU^J)4a��~,m<���[CI4��S5W��u�K��9��c�\s��z���|}�y�����*�\@u��}��)�����S����6�
���j�b����E�+�~��B�tC���~������GAA��:���^8i{D{��e�E����=-�j�������S����c�V�N%��*&mu���1��[oW�Q��3�<��m
Wi�-Q�G�n|�UU���'��J����H�\4�����l�L��h�1������\0������7\7P��U7�*���z*����K�Ds��VM��[��K���t��&`Q���>������,E�������-���O0$����v�*�pC�7
8��_|q�PWU����:�����~>s�����`C�����.�S�:��z�����%�������*]������wM��@�"��r�H��lEtS���|�r�/�u�Z���.J���>�)��Wd���7���EwU) �/��n��I4a����{�F�u�����^�p�{\m7��O����
�D���V����0e�����RV}��q����B[�'M�T#X�#���*�l��,~��X�����4X-_%��9Vuc�?�:N�g��~�`-��m4�|�
[u�i5�?oi
���A�L4=Zj=��^�yD�s��i�J�����g������S
������$���<:��{�=��z�T���r����{�i�<��K|����������2����!��U��?��m|]M����(�����I����\!����*:��%�����K��<�:`+����^���z}�oVu,��V���P0��D7��nk���(�������_|���@�DU�Om��U%���B���.��@U�,���GEu����o�����O�&��I
������}WU���1W��t��vt�vP�����B��zt��X4�z[�I��S���t*}�b)V����`U�h�U*e��!���;������h�������5�?oi��A��������W��������<��
c��[���>�W?����z�i_�{��p}����������O�UIa�T�~5{����
��!��t�V�-,G�cR�lp;k��4?����+��R{�F�r�9D[ws��>�uBE!����eK�d���������I~0���-��x	����o�x*FPl�t���{��VA�	R�=
����o1���~)"�N�9���������Ui�x��6&L(3(�����
�WU�
��<�}��c������Mm[�VP��S�d~0�h�|��~�����q�M�/�P\�H��n��l[J�&'H%���k[�����U*���
�_�4��Jx[@
�t��!�.b������}��hi��_�����n�`�}�e�+�����OV)���X����q����U��MA��}�������Q������.-��O?]�gK��� |��+�$��i\��U�(��y<xp�PG������Q����O>q�'�x.o�Wm��}I�eY%cI����U��p�}������_v!�B%�
����:OK�i���(o������/�������{�<��?����1��W������&^���x���!�;j����������b�/$�`�W](����}vKV{���s��W��!-��{����-��"�c[]���%]��.T:C7��_�D��I_��9x��gK�R 1d����� 
S�"S7
�t�^�j�.0uQ�jf�F�t�� ��Xv_~��{\�4A���3gNqiUETOie	_'���M���>O���.���TJM�M�
���~�����c�-����.u�������Z��n�5-������n��.-��G]jTIU��R���]����/h[����7�,���z�����e��D������>���}6=������P�/������g����Z6}>m�?���Z�>�\	p�yi��K�j�QG���*���K���i=��4�B\���h���cO���O�S������2�o��*��6�v���~�5Q������S0���y���d�1���/����X�
l�:��<x���8��w����
�S��*��m-��7�h���J�G�X�Q��g�|�G
dU�I�t�-�?W�����y�=�t�+�u��`
E���h��ZZ��\��T�N�6]�E�H7�:7������}/��4\�G�Q�����e#��E�?�����~�cU�q<��*0T���\�����7�padp�i���B��hUT���eWi��t<������K}�h�<�}@���>{�N�����u����Ok��8����zO�}�j������w��=�i?�q�Oj���u�eP���j;��K/��>����=
��yO��~p�������}*{li]h����B,��Z�Z7�����K�0���G�/mG}�*s=��P�����%��r|Hp��{�>���6�z���5��uG*��u��;T�P�����:��S�[����.����@tn0`@��V�'��$��_qIk�?�xq�S]��
E4%�t�t�gD,M��M�n�������_�f��R�;���7��.T�@��&H*�����C�2�%|��&H/����W�lE�9nj~�_�7�.��>��PVD��v�n<��T,Xj�"�XW����;n�m��6�h���5��_�)���^Tj�Th��|DK���3�������������M����s7�����v�v�{�����V���nh���E+����0�����_�����]*8�0�W���P�aJ�c���O��U�C��h/UtSs��'����k��F&ZZ>B
t#$Z^5nK���-�|Z��.��2���GR���,Z��7�l�e���z#=��C#��<6���C9��?*�����W���+o;�|��ODu�R�:�F*�S��IQ���+�u���y8�^:�"���?T��Q�Ta_��>�C�~/�<\����~�b_���%��z|�����:��� �(�se��;��T�6y�������Q�s+��u'T����
�.���_Ou�T����\�M�O{��~�9�F*!r�I'��9u���H���T[H'�)�6S����D����s�=���-m{]���$��)�P�#e�$���s����x��� �>v���6X��]d����T�n��^���8k�n����N��|��Nu�����Eeo�D����������i��g�vU����
�b%V��>�J9q�Q��1z�9�lR:G��_�~�<����Qxp�Yg�j3M�\� bK���H�1:_E�����mV�I������F�A:�u\_{�����
��6��O�����sMM������w[4�M�h�������t��<��G�I�<R�QV�<�{R�)��_t>�k�:E���J�U�M;�������"�,��1b�;���A�:�l��~�b_H����u�r[��}g�;��u��+��	/>�`Mm����~�PE��C]���T7����T�5�.6T���p7�*Q���(�.�t3�`C7��(V���HU����.���,
1�Z����eS�F�X��>7q��S��e�v����E�J�li=������>�J'�gD]���<|~~��U�V�^������W���������i�(���Q*U����i�h{��������n�t\���}TEJ�)z/���T�1H��i���TJ�1u!�������j�Z���'�9�Mdei�
�U2K����%�t1�m�y�Z��K�FZVU����5���9|�U��W�����W�]��T�J��/	�����N�jXe��ZW:�t\��Z>����CA��->~H��D��>Cxh����X	?�j9���������z���:����?D�������}����'�N�0���=m{�o�*��*�
nu�G�N��A���lZo:��j}�j�UU��s��O���O��^����;*��u����(���2}�j��{��}Z!��wy4^�!��������9P�7mW�O�#���s������K����������M�v}���B����?�_ZW�V�7<4��{�W��������h��_EM=h���Y���e}����`��h�q={�t?�����c@|��+��P��#l	���@�#��A��8@P��: �q �����B�: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q ����$�_VX^�����m�o�[�������Y�H���%��y�P�:`N���f�y���>;����dX�o�V�'�_�\�=�^�n����?�{���37	����c��g?��S��x����w�����r��	A	n����_^��WE�����e�]f��u�v���?�x{���,++��*:��{�{�+���
���9s�k�����{��W��/���l}����=�~���,Z�����cW^y�m���Z1m�H��<�W���C��?=Z�d�=���Z� �:�UPH����������g��m���z������#G��g�m?���7u�z�����.���loj�_�ew�y�p���~�������7�|�
�<�:�UPr�=�Xff����6i�${���l���v�%��{��gO?��+�+��������6b�oHH~~5���������q���7����^���m����O�}��<����=��{��w\���:�<�@KJJr����g��r�p�����o������SUdU{>��#l��v��l��@�S�Rs
����������f����j�*[�~�74z�2�Ry{�����cU��?���������v�w���
�����+W����������{���$`,K��vK�.�����:u���=��[�S5��-��o���}��}{��GVPP�M��:�|��gT�o�]v)5^�y���.P����mE�g��Hlu �edd�E]d/���������
mT%U���5k�
���)S���O���'[���U�V�����1�c&L�$�JMMu���d����C9���������}�����
�853f�p�����OXWZg�<��-_��.���Jo�p���k_N!�B��v��U�V{�>�`�UZ6l�>U�������~{KNNv���p�
n�_|���������_S
���V+77��M�^`��w;���E��������:�f��i/��������:�:���������M�6��C�����_w�u��3���sj�LA��������s��v����u��������?�ZDG��J����/��v�}woL�M�6��}�^���}B��m�m���9��3]����[[��=��'�t�5j�B��_~�N;�4�5k��������a����kc��@$�:����q�]y����m[;���m��6f�;���7�[��?�Uum���{�RP�v�+���/����*�E(D�r���^{����S��PQ�~��go(���w����_;�����������$�zV�*�g������/w�U�N%��BUj��[g����+q��cu*����*q	@P�:����LUI�����@�������X��7��W�TP���
����+�TVJJ�k7M��j�/^\���SO=��x�
.":k��u����a�\G"��W�^.�
�}K���
�U�����'�|�\_��>�����Y����lu�&��w���������>��������,VF�v��G������n����d\e��S����{��G]O���������%���Y�*��*�em���u�]]�Nm
����?��[U�$���]m�TI=u&�������*� ��l�T�T=n�D�J�)�[�f�7�b�����~�����9�W�U�����:)�8�cV��z�h�����7��p�)X-��)�����\�"*�����6�.���\����A��������?*�U���zv��0o�wt�~~�����
6��B������z�Jx�F1���:wP)7����)��?�+q��}��q�h��U�#Q����T�U�����z~}��'\�:uX����U�-A�{K�,���;�N9��2
U!T��v��-�?�,]���+��ysk���7���)DR`�^?��j���[�`1Q����$�������b�=�z�����s�*�����v�ygk���7�������:0Q�8u�TW:�����S������,--��O�n�}��7��B��R������5h��S1�$�� H�S	����1������-���?�>A
l��^x'(����M�H�B��uk����>��C�=�FC�<D
���3�=����WXY���6z�h�a�J���~��v�y���V@���1����\����R��QS%�T�����r�^�^z�%{��G�;wP�<�s� ���:P�
�s>��S��	&���cm������2���[�������l9��������+W��������:,Q����?�222���hX�~�{�����>���6f����_�p�{(t�1c�k��*A �}��VAUG�a�-r�]�~���OtU/��"7���_o����{��/��^x�;��C������J����#�m���qyT�K���(B��B��O?���w�����y�/�m����m�z���
Q�������Tm�i)�4h���z�W�mW��k���+�y��'�}U=�j?�<'O�l]�vuA��k��!C�k��
���VAm�)@SU��N:�u��PD%��(;��#�.��S@��c�Y���]�D�W=q��]������B�K/����S����"4OU�����3��2���:������N�\��t���u��X��?�`'�p�����v���F�+������K\G!_��+E�b�
��5�����X������{�=��a����Ue��SR�_o$�)_��mod{��_�N�v�Q��3�����z�b�����#��u@ ��A	������&�{�x�~@�Q����j�����<�Q�e�T�eA^���]���n�����ukm��wY��o�!�#����y���������q���@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P���j�`������w�y�a�oh��/_n��v��������d����]z����O?ySl*;;�^~�e����5l���6mjC�����[AA�7Ui���N��uz�������U���W���_o�����T�/��b��
��#G��U��w��n��w�m�
��?��=R�6z�h;�����?t!�n��f/���}����X^^�7uH~~�=���n����z�^���t�M�~�zoj���j�0��Gq�WU������n'N�n�����2e��{�1�����;�p%��^}�U7|����W_���3���>���g[����;��Y�fyS�|��'.P��O?���z���s�|��co���75j
A]5Pi�|��=�Xk���74z
�T���c���.��������������n^x����}7\T���^���L�����M�6n������~�d�J�i�:�������W\a����.;����5�Z�j����Ps�6����j7�x�kWn��V�^=oL��������o_k���74$55�
�>��rrr�c��S�8U�������K����+5�t�R7L��y��=m���w��v�uW���������CP�TB��{�q����h����7�w�}�G
��e����^{����m���n�^�pO%�T�.�����.�*���M��{
���f7,H!c����t�|��75����
]uU�Mw��W[�N��1���o��e��v�Y������5k����v[�X��
�������5r�]����;������}�5h�����D��P��f�U�J��w�}v������"�e�PG
�T/R����[���_�R|~i<U�MKKs��
U4��f������~kO<�Dq�U=?��\�o��w
Q��+W���C]i������o��)i��o��u"1n�87.��i��o��r��h��k���*-�i���g�y�S�����D]%�����?�za9rd����A��Jz������Ow%���k������Su���?�����K/��{���)���S�t�=���Ba�y��g=���{������%��-��)S���w�����xS��K��5�M��A��J�9s�-^��Yu��qm��������7��h�A�s��.�+����-Zd���7�4t���N%���������HY��
��_u�[�j�����o�����H�N$��+jA]%) S��H=z�pAX�6m�w���-9q����p%�d����p?���;v���/z�z�U��w,�z�jx���A��W���}�]�PP��j:���9u��N$&O����G��w�����g������A�+��{w�i
���+��3���:���V�WUl�N�1�����]�����m���n���s�OUm�����������F���>uq@���;�8�A����o[�~����c���1c����]P�S�!C���t������j�s��qm�eff������q�_�Q����G���j�z��W_�B?���b��APW��Q���S'jC��N'.��"W}VT�����O�k������v�����*k��������:����8q�k���vU]�v�j���w���u���.��27^�i�^��k>z��A���PS��D�v�l��qv���Z�&M\�V���Kl��	v���A���w%��[�����;�.\h���^{��^�WZ�s
�xM���:�^���4_����H]��Q���A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A��8@P��: �q���u@ ��A�
�x�������������-+�v���f���j'�_�v���(A�(���������_zj��wZa����T�.QB:�g�ge#��UwM4�~��=�P��z�Z�=J,����=*�������S�;�C+�����.I�
,m������Y������F�:[�B�?/>��gL��!��[�>�>�>3���bT��0;�{V����3	A�-F�]M���x�(���S����E�`����+��@�"��J���(7����d�lQu�T�l��/(7�K*�����V]�g$@$�:`�\6VX�aZR�gW��Y���/T���XRa�1��gV�wo�NN<�����5o�d)�fEG����6��|{��[�v���J�A�����y�=/������)���)vZ�4��E���	�g����
��Y�6�����a�Z����l��\��)Y�w�����-���{���G�D��|�l�%(Q���~,	���%���d�Y=�l�m�\��`+�h���I��s����k�����A�S���R]HW�$;sN�Q��;6�:��luR�6-Cv�Y��$�{���������F����t;������=� ���u�c���>�o��E#�E�(K��m6J��AP�r�����$������s�{����v�Yv��Yv��Y�����[4�N����}6
�"
��
�V���H�4M�i�]Rl��u��?�,����hG-��;��]�m��BkP7�����wh�T;`�K.:�|�$�F<��-�>�>���v�1������
�����o������Y�q����]����|bM_x�\�����7�D��M|���FX�I�y7��oh�e����h�_eM�|��>������7��W�]�zV�������X���
�G���GP����B[��$��g�W���ev���U\U2��ws]p��d�k�(�����ul��Sl��B[[���M���)�M�$7~�������GC�[��I�W�����g�M�?����rm����r����c��.��Ts%�|���oy^����}Y�ohap5��=V�������VXP��YYV��k�M�YF�����{,��Nn���?n�A=-�w�����d�(�kp��.�k<�q�{�����u����3Hp���6�~��>��e^|�������gzS�:��Zhv^��J�):S(�|Ma���D��l
��K���Wy����wUY#IIN�
��.p��>����N�S��%���P�~[]`��/�~���xeh^-%Y�&�S�o�mc�{��Z�Ws�0�w����O7�8�����cm����rp_����.�K�i�;�x7M���Z�
j��4�������[F��,���E��c+�{c�Vo���y�%Vg�=-)���Zi����@E������J����<�.z���� �K�m������>��`��o~������A	��=og�e8��
�������i�d�)fk6�+�'
�����.d�d��B�V���hB�e�����\���77L����J���������
�/s�U�n��c,����V���k{7<�$4�H���nu��a���Z����P)����-o�[����rp?����7�l�����r�m����y�p[s��E���@�����TO����r(dS�q�J��'���o|^Fq�2�U^UJ��Y���M?�� �S�u7�a�Kum����<������)��
e,�z~��/�<�f�2C�j>���+�*u��nZ�r
����_��
���Q\��H��i.�+Q:�[=�t����g6Y��d=���z����z����7��#�jX����;N��iW��7.�g�K��Z$���JU-� `Vy��u���!��Mgu��i6���n9n=!����b��m��9��9EST��P`��_4�:i����n����OsU]���R7,H������=��u��?Z�*������g��� ��qBm��:���&��z�n�l��Z����j|�Wy�~�%��g��z��m�5��d�O��;T��")-��s��,2������/K�nW�n���xc��0<7{�Wy-�s�mxq���o���+����j�Nm��o��Y6}~�����^�v���W���WyU�V�|�k��Z�#F��G�����B��}�]pxz������Y�w�H�q�5�:���>����N$\�uw�b9�?r��c������:�m�IE�*��65�	�:��-�M����
�]�[&dM����}wN)�9�X�,���Z	�}�ko��sUV����{F��N�GH��D��l�����6���]R�������j��HE9Yx���O=������`#�jPVv�=���z�.�eU(����d;6
{M����UyM-:���V��3���yr�5kzA��R���/�g]���a��iy��[�hR�Z���]4L���Y-KJ�k���V�M��c�BYb��y��:�W����m������$��w��?�|o*OXX����kC�1+jAP���*�%^��i�{����������aeQ����r�������P�W�%Qj{���ov��������e�eU���Q/k����7z
�D�^�����C�A9�.��]c����x��������7�H����U^=����@�#�jPjJ��y��V�/T���}��E�C�U�P|�1������)��]���/e�*�T����s��xx�}�$�����eh�0����i	;�R�������*Zf�w�Pk�����m����mZ�%+]�'�m�W��
��[������T?�Rvl��%���\�WKM������������Kn�<4]��nX��&Z�.��a!��F�5�~�WAUx��_
\I����eJ:k���K������p_�\Pv�������2��}7��8���|[���U�=���R����l�N��:��E����%��}�c��6Z����|`���}�3I�m�I�����|�m���U�n�����V�Sgk�����#.��
+SA��rTu��9�m�m��!�h��X��]2,�/�XDP���T7-�Uy����o/pm�){���\5��F���{��:�fs�����S��H��Sm�V)v��|���s�l�7������%��O�ko-��������if�����9�����P��N�2��u�������z�%�f}�g���u�Ea���55���������\�Vu��s;7,�_��p����w��,�6������2+X��M���d7l������\7��t�-���A����=J���+
��g6���s]�V�Wi:�:�s
���l���OUN��SI�e^�o�������I�n�j�N�bzs%�>�.�nze�����=4-����cK�-��=�h�W�+t��K�~S5YQH����{�j���������t��\vMI��E�g^|�����J��~�Eq5X����l�?fu
)z����z�&U�TX�{�����{T�t4G](�?��P��R�SB��:�q�����Q�iJ���������$+��	����*yWt���b����
W]U��k
.���[���G[u����Jm�5��F�N�������[�1�?P�K�qm��Xnkn��r���S���#�j�p���?#�n�
�4q�^R�����z�!�sA��W�o^y�UmMJNq���RS�s
�t���B�_�{��X���EC��P���6 J�����u��zm����*���&���u����z�*#��,4�J�G�����u�H(QlQ��*�_
�I�Ue9��T�G��:��r�QX���]f@�@P�X\�I����-�;����p��@P ~�IX0�'E
����6W���@Ku�����r�1�3@�AP��: �q���u@ ��A��8@P��: �q��n3���Ov�������nIII���{�m��f��/����N��|4?�W����%;;�^~�e����5l���6mjC�����[AA�7Ui���N��uz�������U���3�_�~v��w���~�eee���#�O�>���������_l��a���V����{�����A�"�O����������?��:w�l��������v��G�<`yyy��!������O���N��uz��s�M7������PS����_�n��~��7z��?�&O�l�����{�~�����{l����+��a�����m���n�,�)S����w��II�W_}�
0`�}��W.<����l�����cG���;m��Y��!�|��]��n���~�@�n���n>c���7�x��5���
f����������-99����]uR���4i�}���nxE����1�c]t���[�
��N?�t���](���������^���L�����M�6n������~�d�J�i�:�������W\a����.;����5�Z�j���6d@� ��$UU���GW=UaZPjj���W���]k+W����O�N��w����5n����i�|���������J�i:t���u����u��J�-]��
��z��gO������]w���w��������URJJ��y�����QG�
-�Rl.t�Y��m��e��q�}��w�q��MZ�li{����b�P��{*	�Ru�4l�]vqUa���hz��S ��6��aA
[�n�����o���	u�D�6��3��;�<W-VUV��o��-�J�-[����n;�_��7��f�����n�J��X��
S�x��Q#W�5���������k��GA_�
\I�H�	%�jA]5�����N�:��kW���C=d#F�(3R����E�K��e��u��.Z~i<U�MKKs��
�x�Qj3��otm��Y�����k_|�]u�Ue������C���r�?�|�Rx��Q8�N$����E>������SNqM\{���T�E3MY>��3�*r����G�et��U�[_��Qb����G��&�ygeQ�n3�d����/�<y�kNa�`��r�=����	�"���U4h����N��3�X�6m�1�Ru���?���{���l�=�����K��}���{�u������T���s���,�/Q���p�	6e�;���m���n�:����5j����k�z�Qb�vu���{w�%��Sg{�(A��Po�
�~���������TUv��E���zCK�<T�U�J�}9�{mU����
6������j����������]'���H��W���J����\I6Uo���/����*�
�*j�.##�����������6o�<�����D�QO�
���%�V�^m�~��x���A������}��wCA��S`��v�=1�P�R�*I=�6i��UU�:uj��l_|����5��i-[�����{��.S;w
���+��3���:�����w��������������������n���=�����TP�bh��?jA]%�]�c�=��;w�}��[o��J���W;pW\q�{>d������Q)���;�^}�U����m����/S���c���1cl���.��i���J���V�����9s����*j����n���k���G�v��A�^��W��O��f�P}�L�
����y�	��U��}��.�Z�x��n�����AUdSSS�W�t�������[��7�\��.���y�7�v����%K����k/{��]u� zz��=;w�l�����\Y��z��B����������;w�{������.6�I���%(QWIII��>��.��7L�����*�dQ07n�8���[]�ZUi�o��	��t�0M%��[���*l[�p�
<�^{�5>|�&������N��uz������<J�[%�B(Q@	J�q���u@ ��Ab����������
6����cm��uz�a�<���F��*�������_��^{�5{�����������G�v����Giu�����S�Q��G�e����G!�{w�%��Sg{�(Q�A]vv����+v����W_}e
4�������j]�t�V�ZY�-��K�,�����_|a����M�6�
�����9��=�XKOOw�����.�����)x���k����r���n|���_���,k���7�|���I'�d��~������@�APBP@�ji�n�������V�Zes��q�^���uH'
6��O>�f�����v�Z7O�����D��u�����v�[����:��;w����n����
jJ��P���R�NA����_*�����UMrr������tH���SHw�M7�%�\b+W���(KL�:u��g���+,--�
�,1	�|�
����=P��u�l�������L�b�|��UC@�V-���S��j�n�����������h����q���T�eddTk��@<���z}�DL�:�tC����'{C*��o_?~�5m���^u!u��IP�n�:7n����_���5j��N9�������A]A%b�(A]A%b��+���<�S�,�x��
f��s��^��rrrl�������{S�+�A����]0��cG;�����'��%K���.//�&L�`�z�M�4����D��N���=�����G���]��>�uM�4��~�����*[�p�7H<1�^y�{����n���'���_nm���������[o�1c���y�����@��IP�a��3g�+Iw�YgY��u�1�%''���_�~�D��u��1@b�IP���e?���k��i��������om���e���&�D��$���+�����Hd1	�TJn�]vq�_��ky���;�����z��b�edd���n?����u�]�|�roLi�/�����,X`�z�r�Q�������N=�T{��Gm�����#G��_m��������������&N�h����}�z�ORaE��m��k��m��fc��q�#QHw��7[�&M�!@��k�z�Qb�vu����{w�%��Sg{�(��N4�?���f���J���;����}{W=�U�V����M
$���:J�<��F^^��_��4h`��1���u!u��I*�r�J����k�n��
���4��.�N8�V�^�
�/����c�������Z������=��c��M+~>o�<W��]�v�Vg]�x�}���v����y��[��^T}
��+%����/���N<�D[�`�7$z�X��G��>��nu!u����N�Y�f��Bm��}���[��7�l�T�Ri;��KMM���A]A%������$k���5m������N��;?/��q���tHx1�LB��������.���tohd*��t�R�3g�egg{C����NU_O:�$���+m������4��n�k����_��b�U��e���_~����Z����|���[];u�k���M�<�~�a�W�^��Hm����'Z��]�@"���.%%�Uu����7$z
�l���?,--�
$�j����q|���b����o_;��s-++�
+�o��5��/X�v��9�'&m����A��!��J�(_L����L;���\u�`uV���a

�!$&A]�O?�d�\r������)���a���������������N��������gO���{l��������[�d��}��v�UW��HT1����9r�{�Rs*Y�P���u�]v��)V���}o�xb��4��/�l?���=��#v��gZ�-Ju,���l��u�{����4ib�&Mr���D��n��56o�<��������}{;���m����~�zo(�Xb�������k�i���z}�$55�����^Co�HT1	���
��+WVX�u����l�27�^$��u�����C�<y���5��)�g��S����: �$�S����j��.��"7n�m����v�~�a���]g,���H�
U�-4�����?�|[�j�7�\(��eK[�`�{�*�=���t�I������]�Q��q����{�B����=J,����P"&%�D��	'�`S�L����@N��!��Gio��!^�J��S�Wu.���?�*���7�M:$,J��P��1+Q�n��.��������N�t@@�����W����a��Y��}]�u���m��%�-; �������k����z�)oHij���3��n��u2$
���P��1+Q����^�B�N�:�M7�d����M�>��|�M����\87f����km���.�IP�Bz�����^{�r~������c={������j����s�9�Z�s�=��H<1	���[gs���C=�.��W�5��9r�u����{�=[�v�7H,1	�rrrl��e��n�U�����nk�;wv��u@"�IP���f-Z����,�����F���N��u@"�IP���}���	&�|���+����9s�r�!eV�j��ur������^j��r��{���z�joL�zy7n��{��6h� ;���1@�I*,��[�V�\iC����'{C*O%��Sl��M�!@��k�z�Qb�vu}�Q���]�G������#J��D��UKP�Rp�&Mrm�U�O��4%��8@P��: �q���u@ ��Ab��_��~��+,,t������W^�c�9��<�H{���4@"�YP�@n�����uk=z�m����uO<��w�q6a��8q�
>����
�:$��u}���9�rss�N�:���oK�.�^x�4h`��w�M�>�N:�${��l����+����N���I��cUsU(��������5�
dg�q�����������M�6���QL��u����E��w����[7KJJr�g��ik���.]��Ru��iS����}���T@��IP������W�RSS�0=_�`���v����IP�pN�\VV������O?����g�i���0Qu�e��Y�-,--�
$��uj��C�����9s\�o���+Qw�X��-�t�������S�Z��m]	< �$�KII�����z�r%����*k���
0��W;v]t��w�y��I7���(&A����3�<�zt�N�:����kx�{�PN%���~{���{��@�J*T��S;t��������3���6l��$�^����iW���,����4�:�{@��������$��v���E�%�+((���W�?=���\�2�?��@"���N!�I'�����N��5k���Z �H�W����$�Fg!t&@	J�q���u@ ��A��8��.??�{�"1	���t�Mv�%����+�����n��5��g���+,--�
�,1m�.==�RRR�g���n�m��>}���)S��O>���Bo�H�
c��������������x�������7n�MUZFF�u�����j�^��{�����{�B����=J,����P"&A�B��C������!�����?��6m�
j/���:J�$�[�n��7����/oH�5jd��r�effzC����.���1	����.���1��@tb��c��<`��
�s�9�V�^m9996q�D�����)�.*���u�������nIII�}�!C�������������m��f{�����������O�������_~����o
6�j4\�5�����z�G�@��iP��I�\�������'�|��,Y�����<�0a�z��6i�$|m-���?��t�Av��w�a�~����n���/�a�f�^{��_}��_~���#G��U�VY����p���A�����A
�F�m��}�����s��e8���]8�����oO?����4�^��k>���2�
����N���=�����G���]��>�uM�4��~�����*[�p�7&�}��W.�j�������6�|���G}d�g��n����Q�\�
6�����J�p�
����)Sl��y��c��?�`w�q�>�^}�U7|��n�f��Y�
G���N�5k�7u�'�|b�_������P��;w����1c��7���@M�YP��+��[
�b]~����m[o���]o��V)���[K��i���0���.�#�8���C�Q�U��o��O�{���n\y����1���Y�n]7<==�N?�t���](������� ��^p������6m����eP�<M�Wg��z�x�b���+l�}�q�e��wv�b�V��4�?jNL�:��3g�+Iw�YgO�p�W�~�\��u��yc����]��}{WrN�X8�f
��-[���������>�8l���74$55��$�|P<?��S�8U�������K�|*5�t�R7L��y��=m���w��v�uW�����������.++�u�������<���w�V�������a7�|�}��7.��D%���������s�#n��eK�k��\[���^�pO��J����]v��U�U�w��UzO��6�l����W�Z�n����@��Y�WQ�Ug�xMW[��L�:�-Zd]�v��)�SH��v���2�f�����n�J��X��
S�~��Q����T�q�vp��^c�>
�T-W%�"Q�'���Y1	�8�4��������d��~j������:f����\�8��������L��*<k��E��qe��	�u��K��zmZZ�{����N�C?�pW*�������R�:5����]��z�r������/���aw�5�X�N��1@��
c������]��<��k�n��A�#� ;��3l�����W�Z�����Go�%��
����#\��{�����ZV����+W���C]�����uR.�4j'O���7���$|�����)���z����k��J�f��|��g�#T����{������>l}�?�G�����j�}���{Ty1k�N��m���J�)����������5������?\�)P�ZC:�O=���Tr����a��E�������:�P{u��<���~��hz�����z���]�h��#f%�|���a3f��������So�*����Z�����@�G����y�r�!��<����K/u%�{�=;����1%���[;��\�*���tU���O��������������.���\��|��[����S��.��z�|�F��%�iW������=J,����P"&%�


�K�)@R��P��[n�7�|�Us=���l�w�jC:u�p�����N���	�g����<j�o�]wu��V_$?�����7����U���v#��[�z��<�@���W�y����?�t�����[���}����	���$�SH�6��9�{��g\�j1.�W�T������|�l�?��f[*��pL��i��j�J#�AT�[��;�l����M�:5b����j3n�}�uA��=��T�.��?����|5���u����-5�2S�I�Pp�+I7�|>m��x�
{��������1c��;�����r;�����W_uUR�J%�;�����]P�k���
2���S�*Y'
D������fff������q�_�Q����P�z�^�F�@��Yu���d�{������_�i��yc�E�{����s��Rb[�+V����j�&M����o���m���������z��[��.�rz��;����O�v���S��������S�l#OUb�.sss]i9=����;�T�
I���B9UuU��:u����\����v���������D�z}U�:N��U�3�&������/t�Tu�����;�����������x�x�b�^\uS0�n��Vk���[gr�%�����!�(LS���^z�U�U��p�BW���^s=���������N��uz�����������Ju��J�=��c.�
/}�f���D%bV�.���_~��^�u����]G�^{���D�u6l��~��f��������U���!]�t�~��Y��=m�}�qU>�����@�E��J�P"&A���+]o��'O�����@����k�=�}���WR ���P"�U_[�ha#G���>���x�
1b�+=GH��IP�����r���m:�d��n�Y��-m��!���O�j��z}�;�P5�����&��\;u����;Zrr��ol1T}
��+%b��Sp������3l��I.�[�v�k�n�����iSoJ��"�!��D�_KII�F��?u0Q�No��j$�[�~�}���v���Z�l�����N:�&L�`|�+I���O���@"�I����[�h�����������N�[�M�66p�@;��cm������KW�U_C��
@��u+W���C��$�[�n.�;��#]�����n8���B�(�����u��wwUZ��w������+���;��X��999��_EI���y���B��J�P"��I(�7o��~����Y�M��b����Hd1������v��G�SO=U�4����/�h}�������	-fU_���K;����?��k����8�k���7�l������O�-��b-[�t��{���j7���P���ni�=�I���.��rj�;��f���j'�_�v�6������	(�{�����_~�1c���#J�t�����N��(U�����6���y�6��u��>�>�>3����n��5�m���{[���-))�S��k��[�p��[��@�J�e�%�9����OL����<�]�-,##���k�e����aRu�D���y�#�$&A]jj�5h�����\hW��t�^� �6Ww-���H<1	�233�C�6u�T�3g�742��t���s�QL����8p��������m�������s
�xM7`��: �$����;����o����y����eK;����o���=�p��t��u�^	@���V,=���b����K/��^y������Z���O�n�����_�5\�5��*-r��UTzf��vdzb ����8f


l����p�B����:u��n��fM�6����e�@��5j��(�L����(dy������|�l��/���,�5�@�������La\����{����gO�����U
�(| Z5R�@i���D*��g�t�����v����i�d����������9��:vT�:��a��$�}+�^_h�������������~;��i=�l���^'�m�.�o~+�f����C���{�sU}7
��Q-�����_?KJJ���^���[�gA��%�����O��	����G��A�S�Q��*w���t;�g�m�Mh
�����if�����x\���Sz����c��nwH�:�f�^��kV7=���)���h���{�vW�<*��<����^��V�^�� ��[= nm��������RmD�4�w���H�5Y����ii�2�Mz�>u��.)�T4�/~����g�Qwf��d��������&��T�{����]�j?�,����hG�f�������r]�5��d��
M�;�c��k����������7l�^�vL)Z������n8��Q-A�:��4iR�
Ea���z������x�����
/o�c����k+�d��f���'[��$[Y4����K�?:=�~���R��%w����r�]Sl�zI�vc����t�����GC�[��I�W�����p�n���>�^�
���{=�^�{�.��Ts%�|���oy�J�u���2�������g��/nt��6W��Pr�>�����eU���z� �$�KIN�
��.p[�f�����Pu��:f��%���e��	[]`��/�^�W����y�l�d���>�o�mc�{H���Dvv��[���K������LKOO����I���*�fC�sO��lS�����i�l���o�?S��$.�������.�1�s\�S�s��p��J�)������.��Uh7�������o�NH�'���yv��lo����n����}�����C���yp�5�_��}��e=������
��^/[�������V���,u���P 1TK�:�t��~��r�-�|�roh�)����;�<5o@bjX�$��la?K��Y�-YU�J�
;$��Y'����~���Q���������6������;�]���������N��kzj�s�����z~��7��n��+�������������SjmH'���������$�j	��4ib�\r�������~��]w�eK�.um�EK����Ov�M7�n��f�������n�l���OUN���c����XU�N�>o<.�vi����{lzN��j�k����88�&_Y����2��n)����s��3r�����j�.?�d*IW��ii��J�U�H$��%%%�a�f'N�O<�n��F�n���O�>v����'�|�J�����%D�5��?��o��8��i����;��s�9��L�b�z�r��:�Gg�o��B:}����e�����Q�$�m��������������|kX���������M�]M���x���P�w��7�[o�����k\p���=���S��E����7����t�Av�u���E����/�/���n��67/�S�����N���c���\��>�eG��e����zpUu����cg�L����$�{��\�~�^s������5Y��a�d���t����?4����]��j�@"���������6f�[�x�������6m�xS�h����q����o��?��J��m��@���#�23���ev���=:Q	�;'f��K�--��G����XKG}�=�q��TeU\�g�����S�������zT�����������'�t���w�+V��U�,77����{��'l����aC�U�V�Sl�mB_�
���[�k����5�L���E�u���W%�N����B!��5�����F��~���&+�l�����E�H1
�����Y��M]���������`~d���u^v���~��[g������

(��R�{l�eU���Q/S��P��`O���U	$�
��r���o+�uC����FjO����.��r��-t��h�d]w)�m�dKKMr�_W��F������������_��o�,��KV�`O��&4�xW�)7C�����j�s{��}�e�1�������~�w��v�>�F�K+����E��n����EK���������W��M�$;���R����+��tJ���A��*�O��F>���u
�Q��N>��u�$�����j�sC��v�.���&��V63�l�q���G5��Pk����Sm�V)����������]��^K�W�Y�Uk}iD=X4m��P�u���7qn���&�����[������E��xy}�t@�5�Lr�R<6#�~Y]R�v��<��e^���=��{����+.a7��<{eN�{,
�����l�V	z�
3��|�h2�~D ��j-U9]�W�k3n����O���9���(���Y���Si:u�r]��=7������xE�F��z+��������gV��5�u������|����6;P�����{bf�{/�{�}����9�$U��(��+�j����G����"�c5�������2���������z�K����G�F�/���R
�UV��U|���R��k+�u����~X_K?��������_h]q�7�j2�d}����;[R��V�r����z����M>G���[���n�;�jI�fV��*�������feyS�H�W���&���-y�&f��V����~����^���'��!/H$��l=6��%/h��l�^XXs!]��+�G!
��6���:������i���)z���Y����f'������?GZ�=�����. ,��e������{�����Y����t+���
��,�I3�{�I����](��
o��k:M�����O�����7�0��2Hu��G49T)\�l*�y/���)������5������>GX�#-m�n���ik������:,Y8�t
����t��J��~�����
[�� [5l��~5��GQ�V�������,_fk�/���Cl�q�[���}�BK����?���>S���/���=m��^�[{�
V�r�%5hhu���������#���yP�~�z������CW�yyy��+��1�cGy�=���n�E���Tjb�EY�ZU�Y��2^Z��i=���V��/��h����Jv),�uo�;�b������W��K���fxc7�����4k�J�e���e�����k���r|e���n��n�J���kW�U����������mY�=����r;��3�
���t��b�)�;v��n��F�m7nta�O<a�w�M�0�&N�h���+�����U�JWUI)��a%����j����u���EK����?(��o��s�7�l)�Z���q��������-��O�.r-�ISK�����.y����o���I�u'�a�Kw_�����E�Y����A����>��F�i���V�N����|[�t������A����l���v�I'�<`�'O�^	�xU�P)�:{vq�:�-�����aIR{x���jj��K�0'����-�u[7,9��k��
\����^��4o���BU�-��AL�:�r�&Mr�U�U�\ff��;k�,4h��q���gOW��G�6m�4W�����`��������j�V������;���V��1���W���
B����J�E�0'��!��m�C=�F���W@P����n��u�h�"����u�����^�f��ik���.]��Ru��iS����}���T�L��m�'�K�J�������zv�7��l��jR�z������fi]t��J��
kr�f�����)��ZRZ�YH���\���b���I���@"�q�����JuH1	��>��zE����n��/X��t]�vu�@UUO+y��8I�c���jkF]�zb]1��e=�����0�������d���
V�����Vo���~�!n����.��Gq�R���}i��Z�C'kp�U�a]���[��o��@o������K��j��u
��eee��N~��g���Om�}���v*�Wu�e��Y�-,--�
b���Z���\n�����������G�X��3]����v��z���,b�;o�x
o��U�m��d�;h����D������(��H��^����z��{������,�T��YJ����JL�|�]�\U�9s��%�|�MW�����-[�����o�mS�N��m��x �6-����O�3�#�Y������T
��B�?���3��~��y���rf}h��z�
�������&��_����#l����p�ZKJKs���/��h~X�E��t�V��%�I��0��b���������^�z��rW]u��i�������.���;�<k����^�@�QV)��p����
-)%9T�-`�����sN��}�������z�_��OK���%ed���/���:�`�2[{�����^��O7[~������-��O-y��]�W��/��E��C+3��[���Y]Uq}��g\����S'���{��C�Q+�SI��������7=�Y������-�}G�8��8��d��V���Z�;Wg�=�$��N%����wWKn��
7n0u<�Ki���	#fA�zzU(����.����g�
*��n��v���������c�)�AY��
���+��e)��[���{CK����UO-�Zoy?~XN'��^a�;�s�[��i����G���wUa�~Xd����)�PQ); A�H����v��Y��
�g���}����E���������(S8f��������'xc�^s���v���q�7�+i�.�ok������y�2z�s%�r>�m^}�YJ��\/�
G��:�(�k�m���fe�
���*�$�	�������A�Jj������(�������%e6����X��3����z'�*��������x�(Q��a���)�|������-u�=�fXh�3����_�M����?����c��^b�4tm����@��I��.�K$�j��]�r����������^�������B�_��Ya��/[�
-��rko�?�c���&�����p��C�zjU���4M��c{�_Z���l��#�_�T\HxM�����=
s�-�b�z�i[}������E����TX�{\e
��j�'O��T^��}m�����iSoP{���{�X�]]�{�^"Q����G��KH����O���c��7�I���S�6i�$S����G�x���ooc���U�V��n�:{�������>���KH@�� (��Ou���GoES�/����(f
�L�8�n�����{�������{cB���o��r�����J�)�Q�����R�*���������E�OL�������	\u��:��AW�����z��m���Y��
�U�D���^��*�v�N\�FL���7����n������Tohd����]kyyy�PP���x��.--�Z�ha�����������.]js��u��u@"�IP���i�;w��S��#�<b6l����Rtw�q���5�9�k���7H,1	��&���C]o��P�[�nv��7��)Sl���=����=���v�1�����W�'�0�]�.^�����
{���!�>|���4i�
j�^��{�����{�B����=J,����!/!/@��u�6m����>k���������8�;������=������������	/�%�DF	�J!/!/@��u��u���������~j��-�y��yc6��uk=z�5n���^�
��������~1���������_����o��6~�xk���7��B
������YP�N"��o���
f�������xc7���a]�t���toP{<�< /!/@���n���v����*��=�������B�B��hp��p��_L:����qm�u���v�uWo(���$�KMM�
���III�Pe�IP��aC;��l����t�Ro(���$�S)��C����nk��z�-_�� ��t&���m_|��}��7v��7���i{���u���Z�n�MUZ�F���SN���LoP{�8~��#/!/@���n����D�����!�����?��6m�
j/���D��%����bZ�n������eddX�.],==��^!��K�P��$�P>���D��%����b��D$999�J����Bb�����y����O�f��m�7d������t@"�YP����W_���>��z�)[�v�7&D�_|�E����=����uHh1����+���k\ w��w��U�\������M7�d���w��D��NA����n�����3�F�a�7������k��S�;J� Q�$�[�f�k��w����KJJ�������.\h������%&A]^^�����E�����F���n��e�����.55�4h`YYY.�+��k:M���(&A]ff�u����N�js����F����]�v�u@"�IP���b���4>|�M�8q��uz�����^$��u��sg;������~�#�<�Z�li�v������������[�n�+����N��]z����+�X�=l��U6}�t{��w��z����h��,fA�$''[���m���W�>���t�_�5\�5��b���������n��7����[��=m��������-Zd��@��iP��'��Ad�F���7zC������g���w���
fK�,���)fA��l��6g�W�.77�c�]�N�\uO=��]x����xc��������^~�e�?�=������[�
��f���v�E��)S\i��^{�^|�Eo,�xb��Y������:�0`@��E����9��c������g���k�1@b�IP����B�-ZXFF�74����[�6m\/��"$��u��������++��eee���: �$�St:t���'��Y��������S�Z�v����D��.%%�hM�4���������������!����q������---��=�X�: �$��}������j�*;���\h���T���s��z����on:M$��u
�N:�${������Nsm������m��n:M$��u��m�����c���+6�{��l���&�@��iP�v��6mZ���Bj$�S��������=��C�n�:����E�Yvv�7��b�m������k���u����>�l�����	������{�����R�����Q��Q��C��R�.�K�/)I]BIrE�GR9C�f������{����1�;�v5��z>cf?�s��3���=���3}�����d���::��5k�%���������*�2�!H7|�p������.]�hu�����o��[n�E�n�jN!""""""""�|�,P���^s�5��o���3�}���T����� �>��|��w2m�4s
Q�S&�:4k������c��|P���gN	���$�]w�t��Q~��W���2��?n�[�k���e���fi����S�~����p�?�x6l�l�����`hF��'�����/U�T��:�����O��k�����0?���X�$"""""""�o�I�.==]V�^-��v�4j��,
��O<Q6o���e�#�0H��q������e�4Hx�m�����s�I���e��E�w �F�-}����QZ�l)�w�|�����W/?~��[ �1q�D���0?���X���#u�"""""""":��t0	����a��?��2D_���<��|���������gk���	d���2f������(���e���2�|Y�p�,]�T���>��,Y����o���2b���l�2
b�+V�zl�9s�97.e�C�Vd�!���f���o� ��r�2�L����6W�S������S�A64;����e�����CBB�0@������Z{���)S�Hjj�<��#��qc-G`����Cf�����o��I���^i�����)�5J6l��`�DDDDDDDDt��I�.99Y�@`	�,#�{�4d�!��c�����kWY�f��w�}HC��$��t��m��[7d#����rX�`�����Cv���p�	Z��SO�v��i����g���S���=,M�6�:�z�~"""""""":|���k��=5Cl�������~*����f�!��_����W_��]t��d��Yk�V���:��W999�v�Z}.�u���-Zh����� ��� ��B��I�&����G�r��U��@�y��:������������)�@]��������`��Y������������s��3���j����U�`Ty�������C�l�;vH���%%%�,
V�V-9q�v�����G��V��@4�m�����F��v�C]d��ce2�����������*��$�o��~(�}������cG
��%u�!xw����KT>�0D�}�����$##C��\��l<4�EF ��2
�D@�h/����.z��i��w��l��4�QY�>�����������ua�0��?��C.��2m�:i��������W]u�6k�����Y�f����y���u����_��:�M�������<�,_��|EE�w�����et������f�U���3���(�/~�/DDDDD�C����W�W�u���2y�d����t��M��O�Q��>�8�e��BT4_����8:�M�����&�V��X�Pk���l�x<�:��<%"""""""*O�,�n���2d�����k��W^����>(c���L:4{������'����J�Q�Q_�
&�������^�:�,sJ�p���z�����#�:��qw�y������`�����D���BSd��*�L=:t�Ge��*����L������r�=g���������BDDDDT��YF��34�t�E�
7��#�b��9s�H�-d��%�m�6
��^�Zf��)�A+��\bb�4m�T_����������_�UN<�D
p��H��5\V���{5�����
j�?���e����o�>-��&���/����+gS3"""""""��J���%���?k��K/�$g�y���v����4�t���J�&M��G���[7Y�bET�8+"d�!8�Qp`�Qa������9Z��s�1:z.��|�V��>���i��x�g����kn�a�Y�l���'"""""""���Lu��n��u��e�� �DC���p�3�r�	���]e�,�K/�T�M��MR����{��we��q��gO
�Y�W�.�����&~@f +���~�������^��x�2�7���G�|V#�>|����ODDDDDDDD�O�&�b�����C��Te������M7��}�Y�W���C����{L�7o.��w�6<x�f������AX���~���
��;4um���6'F�
B���w���1�c9,��`���{�s��R&�:�5j�#�Z�a�/����2�,��o�f�GuT��
*01��SO=����I+�u�]2}�t9�����@���sS�N�������O����|��gr�-�4z+�F9�c>����<���a�DDDDDDDDtx����o���f�a����;�����[�=�������o���L�0A���:-'��8��G��h�����U|e����"c����������K���J���~m�� �1QeUf���u��;��#S�L��2��e�r@�X4wE�h�Y�DDDDDDDDD�Q�5}%������M�(�/~�/DDDDDuD��x�h�����Pq�����d�Z�d�U�����6M�rE�8iZ���6��!"""�`��^��[���A:��}�x�x�DDDD�uDDDD2�r�����+�3Qy�@Q������un�Q��@QS���F�f�DDDD�uDDDDDDDDD1����8��G��h�����Pq��p��C�&�'��?�7+�,�Xl�i���$_�_�M���DDDT1�������(��gK�j��e�[���}����]a�t`d��{�{�{&""����:"""�J�f>[�!��D����[����tFn����������/�����*�(#d��2���d)wh�Z��-[l�"""����:""""�12[�e���n�����k$hKDDD�uDDDDH�0a���[8e��$���pa��1��dy�g��N+<f�&���x���%^�M������=)2n@��v��?S!q�f��rG�y��$���d9�(s��
�{aK���?QfK���hHQ��?�U 9�Yh����!���:vq������s��%���INy��Di\+�%�oUX�.C���K���qR3�&�H�K$>N��vy�W��h�<(��{.J�����t��5�b�/_@������r�	���CRl���������*�����*�M���+��Ks�$8E����(G.z6K���%3~v��-rtm��j��!��@��s���Q�����-�<�%����g���C�}�����G�t?�)u��t��7�������	r�����������Or��3�2��lY���:"""�X�#"""� @[�� P���Sj��4�l�2�����������.�s�G��Z��l9��A4L�|��V���u����r��E.���[�.]Ov��l���s����Y�t]�[H`D������~�,���s�;��O�:Jc�DDDTi1PGDDDTA��"��D������!�'����\�������j�6i���,���0�����[��A:����e�>�8}�iZ���[�����%��������:50�j�G^��gN� �Y�������C^g�o����*"�����*4M�.�TM�?��4d���L;�^_9�{�c.5���c>��!W��=����������!�R���Qe��F�-&T�7BDDD%�@Q����*�v�&��>�n}:��}��<��;l��F���y��}�o��qf�*��V���j��o=��$I�(����,�.K���@��"}����S���KC�Q9�@Q�����4�=�c���'4��i���j��R��}�A�*v��1^j��t�Z�|�n��!�\�(m}��}��
�J<��T�����=g����DjM�#i�?&��u�� ���|y���t�=k�}�,��S�����[r�9�_��8-i���>����o���z�R���!��s0PGDDTn1PGDDDDEB�������;��C����;���� �k���e�d������}^yrz��0{��L�h�K�vhb��]Az���n�63
��,}���IRo�G�G7������<��U�������k�,ZU~JR��E�����,��<n���%I�\!U��%_v�Ta�u���'FN�or�9����pf'I�r����0Z/�[�Qx!�Y�u�N����$�L�#9��1u�A��*�y��;�������V��}��<Y��[w���v9�XV��������m��3��p�$t�.���l�(���!�zt��}�I��O�p��ytI���\�/���/	��o{�����e���^��g'����]0�7�!�����+���!`�������w�^��2Qv_�Cv]t�d�c��I}�{��f	U$�U�?h�&0
�g�.��(G��rKz�!�N��8��8`�[��d��[�J$�b�&���Z��+�.���Y�I��k�����0��i��A|���U[����M��e��Yp�o��_�kd0�E��,������@��7[R�xw������Z���	/�g�V�vs6;A�-A��7����K�����FJ�$�Ys12��HO7K�����`�������p�mA}�e��Mi����U�I��&yC��Va�����<5#Wz?�%]Fe�yOe�U�d�/=���u���|B��(`71"-XM`��3}��k��}���?_��z<���I��y������g�����,�C����O��f�!X����������`dfhF] ����{`�������%��E��[O�lYS?����)��o���;���JQE�@Q��]�a���l�sr�H�0��VO��2y.����p�	�+XeD��v�`9yF~f]�(����!c�Y��}��,%�&v�M<���}�t�o������j5}FSS�B��p����\�����l�����$��nf����5j���L���O���.�
�$���fit�����Mr�kN!""����+6""""*��w\#�(`�V�G�f�=htT@�����\vW�!�n*j���<��g�8m
�n�Wf�,�O�X6���^
�a��sO<x����%!N$+��~������Gg�X��H�5x��#�B�.N
����q����C?s�s���o�w�/�:�4H����Z�����f-����Ru��R�������x�3E6_���V�W�?��WQ��@Q��u�,]��]��$���n�����u�]n�4�k�Wd�F�d�Yipc�xy��\�[�@h^�H�Zv�5^��L�X�e2}��`]�"]�l�>������S.9�`t����3�����
��kP��Y�r!C�P���<�6H�����5:�k��{5���^��}�e����-]h.����QSl��M�h�����^��G�u���������������&�s��XADDDuDDDDH����[�2m�K��T����&�(���g�8�s�����wp�K�tn��[:�yC������AI��=)��
I��M��%����W���1��&������h�[���m~yo�\�����a�W��.�\B��Qm�z8���^
��<�����^��8�=����^��B����}��$g��:J,F��w�`��C�.����xAos)��!M^3�{�����{�!HDDD�uDDDDHrBp�j������A-����J�6d�*�<1=7(�n��^�������5d�����n�n�|��%C�/[G�-M��{bz�,�=g��Mk��-�@�Qa-�sj��R��1�_$��D��(���n��[�N��������5����r��:?�hf��9�+�����1#���s��J��}C���R�$��iZ5y��g�f�#wk��?�Up_�������3��yT������,��p����7o��2����G��
�t�:.�)_��7��;h=AB&=�E���e����Y����e��}�[�����b����.
��?���s�k@���/I�h~�
A��g����+�/	�G�#u������e���
 ����Y_[������g�z���o�2%����|`�������V~���`^iB�.%��C���������c5K5�\���H�Nn����{���n���|���_:�q'h�g�61\.��n/k��>�]�M^����>W��0���{�������e5'!q���2�?����G"""*W�#"""��$?bW�l��,��}�lKN	�p���a6'u��A��|������
h��q���?��w�^1rsu;|�
���Nk�}��A(���`��e� !��Q��@�����l6q4ll������n5kI�i���~�A��lv���!�_��S�Y���cu~0�q�1�|(H�'FV�x|���E?�g�&�N|���h|��[��U���>������=��w�n�/��YZ�gP_q�\�e�����	�����`������b[q�l6q6*��-�Q<�v�-1I�z���3��r�R�"�&�i&�k�r�L���0T��8A��v�5���J\�%�����uxN��>�;�4
���Z#�_��4��?W��lq�P�n�G���hy�	'I��4���H��E����0�E�)��-.�,!""����:""""�p�u������fO��n�Z���3Rk�<���,I�q��\kVI�{o�K`��n�tao�k~��(��W$o�2
&t�.5?�Fj�^�����rP2`���$w��� ��m�����~ �<��yy�=����s��0$������l���K}""���go""""�p�=({��������V�[lI�:2��~@r�~-�|((����O��yvl7K�0��G��������Glv�f�!@���Kr��!��S<�6��>�1�|�U]���W�������(�i�X�~1�MR�����ODDD����1_�a�yT���r�;�`>������r��|=�?�*������<�={��-���@DDTY1������(_!�C�y�p�4� Q��@UlAA�C��J���lT[�L�3�GDDD1��:""""��lF��s$%��EX,����L�3Q��@UAA��"b%��E�X�v����*
����������buDDDDDDDDD1��:""""""""��@Q`����������(0PGDDDDDDDD�#"""""""*!���$��GeW����K[�����������J��5���aH�/?��!����;�����Q���1rs%o�r`�=�������4���^�V��?Dv]xV��Ee��:"""""""�p��J��:Prf)��#��u��VE<o��g���O��v�s�e�2V��;D���JB�o}u��<k������~��v�>W�?<Lr���sns��r�����$o��;�mQ�c��������������z�
���+	g�+5����~��U�xN�ii�����Z��\�p��������rX���9����Z�������<Y����_��%}�H9���Z�\l�iw�����<[7K��7�p{t��f���V��{��t���=����������	�F�Q�,���n[J���&��:HB�b�\�3��"3��D5����^I�b���?S���vI���k�J��l��q�������H��>��S����W�S"s���xw���^r�+u���tAo�k�^��Z��_N�
uDDDDDDDD��^����l�Lu���&�Nm#�t���M��H7'�gde�g���s���,-`KH��SZ�k����lI��>��~q4>� �W���AF[r�����g��xB�Ee��:"""""""�b� �h�H�[���j�-)I���������k���/�#5��"�c��������C�lr���{�����,�C�^�����y�1�L�uDDDDDDDD�de���&�!|�%�h���
e��������3 ���u���$�e����Z�&	��%�-K�����:���'�����%��S%�������������b�aH��	TK8����p�9��L��I��b�[�D��Tvvi+�/�*9�>����K����)��b���������(F�~��4�&��n���$<���Yz��j@�^��?0gx���Fqo�`�I�uDDDDDDDD1(��Y�>f����%�������rh<����G����$u��R���R���Rk����S��^������K���@Q�A�,��g�u�mwK\�S�ui��b��K�p����R�#O�l�pVgI�s���?�$�v���a�@Q1�k��gk4�P�=����[J�8j�5K�� ������������s��S��Z�R_��z��B��n'��������t80PGDDDDDDDTL����#���:���#Fv���TIH0K����I�G?&��tI�f�$v9?l0������u����=������������:"""""""�br�\�����>����-[�<�q��=��S����%��JJ��%������Aw���?����]��_����L@[r�YJ����1_�a�yT���r�;<�|����+��s���(�/~�/T�7~�7������b�,]�����y{�� ���S�h'M�1_��4�9r`����o����s�-�Q����<���X�;�����H���~���.G�#�R�>���G\��'��\')W_W�w\1����x�~�k�B�>��6a
��;����Mk�������.�7�!I�\�����oh"""""����^��[���A:��}�x�x�Dt�l	��z����W_�/�=�] ���Hv���Yqt%��#q-[�K�����y\��~ ����Y��9�9��
�=L��W����G�G�����J&���|��b�=�������.�u�{b�%����t�0PGDDDDD�2�r�����+�3�G������$��D��s�x�����$���r���L5��JU�k�Z��s6mf��V?w^�xw������4K=$�}B����K|�v".��Y��<�$�2�iI����q�t���+��M���$�������B��z��zC���lV����`?vpS8""��1PG��
�o�(�/~�/T�7~�7
����H������G�fe���-5M���������DD����������C�n���%w��
�##]�#�+�3Q�a���������C&���k�U����=��������R�zBs��&o�b�Q�`�������� �4�Z����1�?�V�����,Q�a��������H����)ti�Q���@l����uDDDDDD�)3JW���@|��:"����:"""""����(WA���N�08I��/E�O�9���;����	R;��������2�~����0�,5� d��k�u�nM����e0�"��'�nI�o���~ E�M����Kr|��=^�Ea���iD�`�%���t������:"""""��2r��?�5��x�-A��c��N);��<�HZ�M:���'/K��YI���!]|�q��"?�TR��}���?Q�u�����q�o�y��O��H�k}�_����d������6��m���4!(X�}_����^p��l�5����d7��9h�DD���:"""""��6�.V���.]�;%�)���|�G9r��Y���,���K\n��k��Wk��	!H��]�TK98�d�����0pV��%�d_�!-v�����1����<s��9�D����!v������cb�����5��d��<��#h���
�(�1D��,�MDT
�#""""�JA���um�8�V�M���/s�O�=Z���7�u���=b���8���VH�+$ 4�c��C����3����������2rz���m��LXF(����IM��&-t������e��>O�����Z������#�(���V�g(���\W�9��#"*uDDDDDT� H�yOA��I]�6/��i��
� ���u��3��%��Ec�6*$����_��\���_W�����+zS����M��R�`�M}��������������+�v��'�������kH��|�aR���)���@j}�@j�Y*�g/�Z��H�����vs.��3�l�[I�����B]w�����`�m�K�qoK�/���������uZ8��dI���9�K���b�r�I���K�u������D�|����Q�����g���&�����e���f{}������&D��	l�������mN	��#�b��~�N�|���y��ds|�����C�d	N�������\C\f������0��IRo�G�G7������<��U���������������>��3;IB��E���'��^�<����p��������#��d�o�V�<��$������Tm�+�t�b�QK�K�,�r^�8�!�������`�w�N�Ql`�������*�7 ��'5�F�����-C�6i`f�E�j����K\�67E��6b����"Q���`�I�&����xC��f����HC@��o��A2����/|K��]l		���Q��Cv��$��v����h��ytI���\�/|��`�%��J����%�����I�q����.:[��{R�k4<�WP�-��k���D�G�+�yW�N���%��`�f������}�%D�o2_���������A`	����u�*��P�)6I�����D.h�}�a��������N�:�k����0�x|�0�lZo����� �Z�5�-{��[�X������8q���o���uP�������A��5##]��t�4��^��N���V�>@�W3$w�����N=�@��y�)������$��gt���s�d��xv��Hg�����v��+��buDDDDDD�,����?DHwP��M�6m����Y�s���}����e����8�!���+1�G��������'N�{vK��K�R?��k����U���Ct�l���7y���?W���cs8�����=����YZ���W��� ��4-��������^�!hq��J$��=[�j���~���k����&���(�M�]�����2�����^1�������C3�VG��ZR^���%A,���������r������l����7����9�`�/<�o�:���V�0Kh�x&�����F-s��L��T��7F�%t<Wl�|�����\����RS�Q�`�������*�f0�k�����k�l�!?�?x�g�^3����N�oq;���1r��R_�|3�r�9��j.������Yz�GpP�������'��	��Ak	����<6FN���[�~����XmN
#�&_9HRn�]�U��{�z��1��jG���#���@U:�^C�TKF�?H�f*�j���1dk���!��h�o����m�i��AX�g�L�Zi��o�����$��G9���^��������7L�H��n��WM�Lu��8"Y�"�2��"H��0��;Tm9� ������;��{����R�(���Qj�Y*UG� 	�of�d���d���9��j���.S���%����Q��f�e<?J<�6�s�>�F��WDD���:"""""�tD����fRFFm&[
�.,����
�$��r�6u
���^sq��2�Gd_�!���c;�^8	f���u����j��9�'���{��������5��7�)�����ud�y����9#��F�U�J|�N:Bm$��e�����y�����9�	�u�~+Q��V��j���������vi�$�M,2�������H�uS���>�q��x6+��������j��7��
|=[Vn����<�y�W���h�4�|��&�
��5�l���}X��w�a�	�uU���������f�xg@D����5�x�7d}�����<�{n��%s���7�t�C�	y6��[����I��>X��lI����JI�|�9��{�_�+"���@U:�S��vh�:��J7$1N�w�t8��B���s��i]���"+6z4�rc�xy��\��tFa���#9.�#j�eH�x�m6�=��]v��&���,�����<ZV�7��3�u^����/���u���r\}�f�!���/,e�6y-����I�W3D�nqy�$v���F|M�i�l�,���?� ����gA��(V0PGDDDDD�R��eUu�r�d�����������2��d��*N��"k�z��\�"��;���Ni��!=Z�N;�Or��5nm��~�&�����b�$9��]�����-��^���w������ojV�./\�$3}�L�%9?�n�Z����`����@]J������������I{�#�����Wa/�T������l�3�zT3^��j��;���?��_H��m��0�M��I,�������a[�����hx���l�*���s!"��1PGDDDDD�RrBp���K\2~N�l���W$)��.�g2w�[����M�&����Av�}���gf�������}�ub?�?���f��[���<�ks����� ������ah���/��A0l��k���`��������`����B���D�.7?��+��O\���w����/s����c���yR�����^
�A�9]���/����u��By=�����i�?��(F��5&�Ge��*�jhg������S�X_�X_�8Xo�Xo(eV_p�,%�N~}���.�/F��b������H��o���ii9�?����������!���Ql%���*�=�Yuh��>�	s�H�����N��w�I3R��.4����M��\��p�^���S�9k����^���~����9���:5�o��~� ���%������/Qq1���������� �vl6q6jl�!���G���)��$I��O�8[��K�~�8��Yh����t)7�j/N�e�������'12�5{/e�
A����l�y�ksZ��-��l���-�A�������)�n�r�����=G�F���Z���"�ht�f�
f������L�������
E�r�3�. �.��9���"@��\J����+���9"v���<�o����w����&i��'��T�l� {�C���bau�v�����g\�>�l�}H�g�������l���gH���4 hR���9�s�����Z��yT�C��'�����_������b�,]<�JE��;��4q����i=�7���#������(V��AJ1�fbg���=��x�y�P3<n�%%�->^���:��CA�t�u�g�6��y��c���� ]$�)$��=)�/>�Ml
W�5�.o�90�������}w�,9���f���8���M����� 8�,�&��!H7�������X��\������Lt81���?�L?f:P4X_�X_�8Xo�Xo(�/~%�/��,YP��w_�������s5�U�286"���Cs��Nt����eD�������(����l�i����[�l��,����:""""""*�����Tq�%����@s������J��������e�L�`*���y�+l���������L�uDDDDDD�	�#F��>���*'J�O��
��0�och
�t�b%����L�uDDDDDD�1��0�#)�N!�(B4E��a��T,�f|���p���I��})��z��1��dy�g��N+����o��������2������Q������q��I��=�0sX���"Q����*N���[��n���V6��T��#"""""��ED����W4[9<{R�d�X:�'<��x�-A��c�]$;��<�HZ�M:���'/K����9`�1W&��m��f�M<^���7��4A����w�w��G.I��1���C�K$)�&��v����G+��Vp����2eh���=^�M�-����54�����:"""""""*�����"0���S�"����#=�%}^��?���9��]z��E��[�#������,��[�5������~�Ni����������Cn����c2e���;�����-��X����[����
��+��iHn4�c&�QDh��v{A��m��J�i��en�i�?��,�7�u���=b���8�$�(���rRC������#/~�����tC��>Ov�7�i9�QA�}S�TK�Iz�!�/p��|��[�����[�&-,������-�&����r%=;����#""""""���^��{
�YM�����F[�!x$X����� iA��MKk��$9��
�j�
ZVo���������u8�6�v���;h���o:�[.�Y��.���,yjF���)x��buDDDDDDD��+AYgU����m�sppm����� Y8+6y��/�d�������mP��2�,����c��-+7pk\���������2��b
uDDDDDDD����
]Vf[�����3��1$�a�5"����y+tss��]�:7wJ�j6
�����O�6�]7�O��q�S�����?���r��:"""""""��14#-M�fE���F|EF��]^�{.\�m�Y�2���;<E��,Q:���\C��!O���g�U���buDDDDDDDtXi���$�!����zv��1���]��l�nynC��������l�3�9u�Z?f�Q��@���z$H���:�����2��w�`�K.~>K���`t���]��2��#�r[����g�buDDDDDDD��&:0��r]�x�t�S_����|5:����,��k\�.]O��K�,�8��������("�������`��
�?�9l��1dk�Qa������6N�����q����l���&�q������L���@E����H-�p���?''�43�k��.�������f�.9-Nz�rJ�S���y��<�w.�'�&����e��$�$<��8F%*/�#""""""���6�F5����j�q5S���Ip�Xd�[���ewe���u!�m=Z9e@�8II��/<��'��t��2t[u���8��]��6m��'3�z�b
uDDDDDDD�n��),]��]��$���n����e�]n�4�k������`��������r�o�@X�����2�?/<H��C�j)6xV|���"��p�y';5�o�^�,[�1��>����;�����V�Z���lr�����a�d�����S������'�|"���T�REj��)�������N����\��1��rX����������5�^��z�W�-wIV�!
����K����e����U�6]]��#���2����)�tJ�����'�����v��'4p��7&i��p4y�/V�e��x<"������I�_��"�.L���6��n����dKQ����Jh��-2h� y��d��=��K-����w���h�"�;Z%Y�j�G��>}���?�(-[����;N>��c�����?^����������u:���X�c=#G����Lsn"""""""�G�Vu�����'vx��I��I�S$=�������������ze�~C���q 8[�nF&�d�!3/�#! �7��\y��\]��w��}���>,^�������u�����f���)J���r�����/�,�>���NJJ������/w�y��^�u�]���Td%]������n���:K^y�i����K��e����!Cd���2i�$������h@�����z���2�[���������Ce�����o���_��T6:�������S�W~;��5_U.��,5_Q4X_�X_�8Xo�Xo(�/~�/���/V#8pX�X_�X_f�����+����_��-� !!A�A�i���?���E)�����+S�L���Ty��G4Hh2{�i�if2�0�����{��M��tp�1���Q��a��:�ODDDDDDT6J�3�]	�tLQ�r���@V��m��[�nR�zu����tj9,X�@����uaJ�>d�-^�X3�N8�-t���J�v�4��u�g���S'i����j���f�a�X?Q�8��8#��[��/���0PWL999�v�Z}.@u���-Z����%==�,
����2�!Yu�P��IY�p����{�U�VM�%''��G���Y��,%""""""�1���[���sT�0PWLh>�c��_�����S���UK��C��]����J����G��V���]C��l�
��5j,��@_ZZ�f����0������������"a���0�*�]u��	��V\%]_FF�>c�hY�xh^����������(60PGDDDDDDDD�#""""""""�6��|MQ@��[o�UGF�:u��r�)���w�����J���<y��II������M7�$�����x������qy��Gt��.�Lf��-]�v�x@�9s��&M����ZF���G�6m�WDDDDDDDD���e��W����b��������/��o�Y5��A �\aJ�>k�V��.����-[�n��
6�gl��;w� �X�H`�W"""""""":|�QW�?���u�]�����;��S�~��"n(���/_.=z��.]��+����@Yxyyy��H���e��Mr��W����;��#������9s����3�u���""""""""*k��+�:H���e��Y�w�^������7O_�y��Q��Z��s�1��}{
�����Zh��U�Cs�z��i��7�����6l����X/�ODDDDDDDD�u%p��'���^*��M�_|Q233��I�}�]7n����Sk�(����_�~���!#G��o����~�I��KMM��&$$�4<cd��=Z��*�����5��e�j�KDDDDDDDD��M_K}��v�m��7�h���c����7��!-Z��W_}UN?�tsn?k�4oEs���$sJ����}�M_[�l).�K���7�1����4��g���-�rh���9���b�
��x����K���x��|M�P�fM������QC��.\��=0@�p���r�J���O��)�E��R���,���������]t��;V���{���v�]���_VV�6uEvh~��������"��#""""""""������������(0PGDDDDDDDD�#""""""""�������������buDDDDDDDDD1��:�J 77W>�������l6�R��\|��2k�,q���\E���?�e����{w��{�YZ66n�(/���dgg�%De��o������?�%���6l���Z��:�(�����~D����8�����0��_����1�c�S�N����Jff�9W0,�������5kJ�~�������k�Y�I�&���?��YR6�Os���3f�%D%���<����?��s�h��un
t8�1;w��_|Q���k�E���>X�}��:��<�:��p}V��g�y��<T�X�(uDN`8I���G�.]*;v���s���q������k����]�j�YBT�-Z$��{�����%�}���zq��s���]�v���Dy����[�n:=��.Qi@����>�s�=W�z�-�Q�������[n�E.����������e�<����r�q�����z���c���};�����. *��?�P���LNN���3b��/����G��
$_}�U��D����k2n�8�$<�{��%C���+W�9�����t��z
�g�N��P�.�����%�K��:�
n���2~�x���e��2�|} h��N���%��X�~D%��!��_q���P�T�w�}�z��)�z�j�J�r&L�={��/���m�yb����9R����G�����������~�{������k��%p��?�<��,\�P�?����Q�F�����%b��\g
>\�9�Y�`�Auf��%�+,P�s�SO=�Ac�CYg��pL#`��[a����<�lR�����[���[}�Y�F���z=��;6����p=��2\��:��^{���d�������5�%
�@Q��~Q�_��<��#z�hA��w����$�_y�*d;�Bm��A�su��1�/dG��
�4��f��O�			r��W������-[���"@]A3P
���h)))r�
7h���y�d���Z�Y� {��/X���N����)�D���������y��:���o�]�Y�n�����Z
�@S?@����(�;w�,O?��^w�<	��0?��
���v����9	����:�"@��p}��4�s����g�����_�y�p�C�@Q���';v��a��U���!x�u��b�_�����~[��o��x�W~��t����P(��_���O���24�������s�����>���k�ft
����A�;S�+�@���{��']t�^��B0�L����@v�u�e����q� Dq���9sf~�8Fq��J�E]���W���;�������U+���s�a��G�~�A�
���g�����p���k�.����2,�:��Y3��]7n����p�*�����=�����V���}����_���[h���g�;p������~������/����%�g��e�:�eQW�Bp8�z��-��~�YZ�z�������P-8�>���8q���z�g�SJ&�s��~�M7E�&���Ds���_j����v<�:�O<�c(~�Dkd�����i���~�����SO�z
M����t�?���8&� ���
����).l��k������/����2x��(�u���TDTAeee7�x#���_~i����Ot��w�i������|'V�E�����KtY���8��s��R��������2�0���8��c
�
��;a��P�i�Q�w�1^z�%�c��FZZ��;9�K�wd�n�t~������kt[��n�0O�y�������]$���uL�0��]L�sSe��i������
��TW,X`�U`��Ez�D[W��k�.�[�nZ7.��2]w�f��x���v���W�6�(����������D��QO���O�����~a:��X�@�c�=f����sSe����N�c���1K�f���E����1v��:t�c���]�����p�\����k�i���?�;u���8o�~��e�s���8�a�U��%����>^c����_�ueX>�X&O�����p��x~��'=����ch�����p��HJr���u���S'=.q��:O��_�X�l�1}����/<��_�~��u�t~�c(���K�x��i��m�WD{u����{���a}�_u�U��}��R?\]|����q4�:�����:����5i����Li�%��lV�����}�cz�y�u��c������T���M.���5k��@0-��?��j}���������z�6l���/\�
8PO��3g�����n�[����$�������
l+0 ��w�}�N�.n��>����s�Obx���g�~�z-����@]�����S��;w�2�s�0��u��qQ���o��#F���}��J�.����t|X����3F���c���Z�ub�(�8qb~���+Wj=�E��%K�R����y���N�c"�&�(�o���z����ux�&+�78������Z�J�������3�����m��!?@�w����/_�\o�0
����>~���e�Y�?��5J�3t���uQ�`��~��g�}:~���f�X��
X0�����m��i����\���N�<���x�y�k;���c(�����c"��"c��r�8��c�X�
��v[�uQa�:��k�����%�@�J�.���#����|��~�X����R����D�Yg��M,����3����8}�N��Q#�������M�~������/u�I@����)�H���,���%M����}N�9��;i�6-x��P�w��V����T;�=��c���O��o�3�y�UWI|||��j���w���8f�y1��C��8���qIG�]4j�)V�����24O@�<������a@����n����`;��z�\y��Z�|�Zn5�GS�6m���}8����/�E���G���9�W�GpL�nD���/��~-
���Co��������@8f���u�b�V�w�s������f��-�`�Q_}7,r��'����.`=z�����G�"�]����}YZ���o����&LGq����:�w��Mt�a:U��b���:"%���:�G�6m��jh~��`9�m���p=S�^=s�����z�:������B��.^�X�g���u��*k���^��G}
�/8��?$�=4-�Q��~W�{��������%��{��X�8Fq����m4y��Xi�%���`��2!��W<c�r|_`}S�N���.U�kDT!�D���qC�.;v��'��cT�={��sG��`=�p�MN�Y ���'�u��@!.���l�2=!��p����}��A�w:��^�/N����0������'�����+��������F�����(~��k���d�C�W�9�r!�RZu��?���?��s�I�&fi\���n��~�0��o�����'��7�����
U\�@@�k���onp��~�Eap��Sp�wz�!��8p���iS����
�8O�c�'�c�����W����ED��/������O;�WB�} `����D]�C�|x����W_}������|�7�����k�P�����:����b/�ti1�$7�|s�]����5~B���
�������BQx���� 8��>��3�O��P��?h�bc8?�\��Y3=��K��@�p�	x��}�z[u�����]�����?l'�}Zp�k��u��Y�*��*0��p{��7��SkXs\0�[�N���y������0�I�V���W�<pQ�H���'��psdA0
�4�� \�7X�!��'�/U����cm����
Y�����4�d����C�t7��_ q#eu��5�PD��8����������qouF\u	�_Q����G��u��^�y��##��K/�]l����(�.��8�!T~�;u��	z�Op@v6���k~$�!B���A����1���p7�A���A@6�I�G��x�)���8�`TZ,�����7z�6##C��������0p���]v�f�!�
��#}���'���be�!���d�X��8�Cw��:���,K��.\�=����������Q��v�����*+8^p��{�p��?�Yi��8p��p���e��p��s��1���Y���^���_X����K��`|W���.�;c��
��g]��#��0J%��D�_�Od8� X��8��D������\�������j�I��������a~+��,�D��i���
J���U�y�K6n�0Zn�8������	����	����R`����F]�BM���#<�}'���+p�����Gy��hb��r���X��yM9C!��
�j? !���%	F�����n�q3����9\}���w�^}�y5\}A�7���������-�~@�
���uT���]�A�F���A�P��B�����A+d� �4���%U�s�8��|�����m�+�c+Y�c�, c���2D�������]�@~�A�]#X�E�9�^qO������s�)�����c�cp���r����T�1PGT�Y�H} ��n`�E���Bp����0�(�ne5�7N(C��(89��1@s]l�����,�b����.�@�I�@n�p��>��"�u8#��e4�RQY58��o�Z�����p�'���
�O���~(���������e}��}Q���ys��E_���-nN�A��(����+�<T����o�8A�?����pC�u�EAF-�����
S�z��
�7b�l@ uY�����~h�K��u���f�{|�����12l�y�z����P���7�g�U�s�V����
��!�����2����1T�p���m����\��8��}(\!���H]+��6|���
2��U�q����:_u��B�1�����@���?>�.�o��B�*���&�/P����������O?]oV�<��Q��M�N��p�e�2m�(����3:���k�3c��Q��u�j�nv#��j�AR�,!4���{��W0����
�7��C�K��Sq�}���)�	��D3��`
�_8��|M6#
���c��~P��ln����)�j�
J`P��p��Y�@��V�Zf��������M��)���M���	7u��j��R�������Cp7WTqX�?h������4������86�=�o�����u=II�1J[M��	��|�?����9�J�-�&M�{������F��F+
Z��\.���Y�y�P�Q���i`�#�Q�p>;�������R��@Q�_fi��F�Cn��<E��kb� ����b��@��	�*!�����0_�7J�,�X��5�
#.1!�V_I������������)9Q(�t����k��u	��w��m����f��������|*Pi�%�����+��t�������_�e\��.�������V&	U���
���k��c�0�e���
h�_Pt-��o_�hFv���1��?���Y
h�7Odc:��_�W�?(E����
��s+����_|���uj��q�2AB����U<��A�f��W���`1��q��Gi�e������>�##3Z%9��'8��� �3��.C0�c�4��E+�|��%�q������{���^��DC�w���Oa���9�����C�Kx?8��GY����(�w�u�]���K��?��*�;v��|�#F�0���oN1�5�0�we�������5k��F��]�K�.5|W:m��m�5�\��F�m�nd�<���i����;�C�5rrr�����.t�=X�����n�M�q�W6l0�������������C�4���������r��|'[���o��aY��P�����o�ca��fi0�
�G86}E��FC�}]�u���A�,�����P�e7nl|������qy����4���P�Z�P?QO���]�����u�w�����O?���x��e�|������G����
v_���>}�������8qb��Xw>��C]�W�^�������{�����������?��/�.���}��i3���o�n�t�>��sJ0k;�����?��S�?�3f������������9s�?7�MV��V�\���u�*�?���z<�����~����q����tM���#u�H�v8�z��1��
�E���&�u����O>������R�c(:�5
�!K���]'���3K�f����E���}<�o����hXu����o��)�X'�7���%@��������:���1��u/��T10PGT��$����k��a�s�9��k�a���uBE`������q��>�p�s�=����Fi���F���u�SO=�0`@����}L�I�c�����e��53���{��2m����x������� U>��)*P���	&�q��pl{��K��_\��[[�l1�8���%\��~����@]����F����us��;���z��X>��*�o8�q����3�A�X	<��zD�Ae����.�!��?�(�����!�o�>�O�>:/�_�~�9%�?�e�_�y����pn���3>�c}.87%���`����
u$p���U�����?��������J��Ja%Pw(���>�L��c���A�K��u�w������u�1T��u8.������G`�w����g��rL������=�������>�=w���u�.a~�����%x��YD`]*��#�p�����g��_�8Au���?~|��zQ�*N$��/7��7u��"�AV2��8�c���f������uc~,g���`�F0�$��SO�
����n�O�X>�}��/����!K+V�0�����~��h�
�&
��[�|����Z_4u�4�.R������@�����@�6m����D|������.\h\w�u��>�Oq,���l�2�^�����
���&��p��1h� ���n�,�������;�4rss��`���:���������sM���}��WF�~��?7<�s�f���B�A�!�����&M*�w������:u��uu:���b]�}��7��c�p��\��Z?���p�
<�����u�������|�X��F]���[�N��u~B����~�m��X��7�����	�K\~��:`�}4Q0�%��y���up�i����wD��C�x9���_������X��$8�6�]#Un���X�Q0�%��A�������h�h��"��+!�K�.,�%*	���L!��r��y��e2e�#�Q�X�������#~c�U�/����������5#5�s�9��S'-#���.��b����TNN�2D�Ro���f]{��R�Js"��Q��]�VZ�n-���2x�`9��s�g��b���9�(�3�<���I�&2�|�7Gq�9�����D���:"*S�A�I*--M:v�(�����u�Y�T"��Q���U+�����_�{�9�[��9��B��R�F
��~���_�~l��*&ADDDDDDDD�QGDDDDDDDD�#""""""""��������������
�IDAT��buDDDDDDDDD1��:""""""""��@Q`����������(0PGDDDDDDDD�#""""""""�������������buDDDDDDDDD1��:""""""""��@Q`����������(0PGDDDDDDDD�#""""""""�������������buDDDDDDDDD1��:""""""""��@Q`����������(0PGDDDDDDDD�#""""""""�������������buDDDDDDDDD1��:""""""""��@Q`����������(0PGDDDDDDDD�#""""""""�������������buDDDDDDDDD1��:"""�
j��Ib�����7KD���i���t��]v��m�����\���d���f���?�x��+���+<Q��@����{N���*���1K����*&�����(���n�U�:�a����%��x/x&""����:""""""""��@Q9���f��-���:J�4�R�������[ff�9g� �����a��������3���q�9�����oy��'�U�V��m��������:y�^Y�h��~������S�N���/��g����#���g�y��k��VXu�,�����gd}N�|��~������{�����?�.]�D\�0XO�gi}.x����Q������>|N�v��)O?�tT�DDD[�#"""*xB��k��������G���6m*_��6����{K�C��W^yE��k�}�%''���n�Z&O�|P�g���:�����G�Z5j����zK:v�(~�a�2���1c��3���^zI�W����o�^���{2d�\w�u������qqq��Y3}�������w$���\r�%��,]�T��i�-����#�\sM�6B��5Kd����s�1���s��r�>�h��:�?��������[7y��w

���vi���.��g��qc������ �y��'<�@����]���8RSb"""��""""�q&L@T�8p��c���0�^���?'�p����f,Z���b����.3r�H��0��Yc�h�������k�.��0f����7k������
����x�2e���qc},X�@�a��
F�t9�_NN��c������c�V�Z��s���y�����K�,�W�6�9�����'��~��n�w�������-���[�>�m���\s�N�������,sJ�6�1b���������~����e���0X�OX�b��v������r����\�y�������������]�v�i��&""����:"""��,�?��SN=�T�����v��������~����[���e��u���a��|�������5���l�|P6m�$S�L��*C����	7`�IHH�r��9����[k��%[�l���+V���n�A��p�	r�e��k�������d��i��gO1bD�gU�^=y���t���u�����_|��wJJ����r��g��W^)��m��k�jya0:-�=��c���O�u��.d�����?������u�Vy���4[��;��������dV���,�������f^Q�b�����(�!h��3���?�,m��5K8IJJ2�*���7������&�X��A�K4Y]�l�l��]����uZZ�\p��t:������w��Mc��?�����f�����(�a�?��G�S�:u�)4h =z��@�Z�N;�4m��/�'D�l��_�d������ya��z��'�s��������Q����������_~�E���/���@�;(�����(�0PGDDDT� X�w�^�P��	\}��.@�}���,��U�#�8B6l� �v����l
���4d�2��0x2�n�����B2���N:I��i���>c������}���<��U�J�~�������_�g�����3/t����9�?~��r�-r�m���������)������U�4(��>"""�o1PGDDDT ��L*4���x�),�Q��9��QDK�j��YS3�+	d�a��k��V���=�x���a7q�Dq�\��e����x�uiC����~+�
���l7����:�;�8y��W����#�W�^:
��4��@�xFV�7�|s�c��9�|����buDDDD1���� 
�LbT�g�}V�/��������_o�]6���;��f5-.�;����S�	/O����^�Z3�F�i�Y��>4 ����h�B�������|�:�������s�����5��>����kN)�����;d�a�[3�=������������b���-�����>��S��Bo��w�}�����P2��tA�?��C�����hZ�L84�D�}�!P������=[�D;v��M7���^{M���K
>a�
��>�5�
���
�k4�A*k@��W��Y!��A.\(������M������w��F�/p��@V�d��f�DDD�uDDDD1��"�s�����{
��"����B3�y��i��Pn��$G���!����%�n��,�o�M>L��c�fz�O;��q���2w�\����:1.�
��m��g�,>�n`���>��=�\����j��A5BG���x@?w��w��g�SV�J}���?�X�g4���������b�������O/^��3�32�������h���W\��+���_��y�g4UE3����5�\�?�,>hB9n�8y��w��RXDL�4Iz���&��A�G����$4
�v�{�=�$�u����"�����3f�c�=&;w�4��4��>�h�_Y@f2�&L� }�Q�{E 
� ;Y��`�W4�������o����e�!H��l���/������`/�����4�38>��8VHC@���.��4���gLC�X�.
x]�5j�f�am���m�_4����O?-g�q����QG�� 05x�`
&b,�e���<��C0��n��@��,0,�10**�y�X�,<�h��O+�2�z�!�������,D,s�Yg� ��o��:�p,mx��CSe�A�f��|fH���W^yPf�Y�|^?������+��<@���G���������v����h�wLKHH�e���(�0PGDDD��N��q�����sgmN�h�"TA�Y�fi �dLE�g�0��K1t���:"*�������/?(�� �CF�5�E�s�jC ��GP�w����!����n�I�5k��BP���}�Q
Z!���~+�?;|��mh���V�X����
7 Ci��_}��6E�M����?�����G^$V^a��Z���������NX��{G�#�{8�;�����N��uDDDDDDDDD�9��D�4���[�IEND�B`�
aset_palloc_pfree.pngimage/png; name=aset_palloc_pfree.pngDownload
generation_palloc_pfree.pngimage/png; name=generation_palloc_pfree.pngDownload
�PNG


IHDR��O��sRGB���gAMA���a	pHYs�����e��IDATx^��x����7�� l�X�*bGEvE�o��E��r�^E������]Q�Y+X�
�� ��'��NfpX6�&��d��<�>��������9��{�I**f�u����e�R�j)C��!�Z��@-E`��"0PK���R�j)C��!�Z��@-E`��J**�=B5i�$�0a�{��qc�����Q�F�������f��eS�N�������������V[me����������N�6����7����>����S�.���n������lc��1�w�a;������'�K�0j�(�9s�{m[�\��������+��Gy�~���y��o{���m�����M����?KKK�^
����w������V�^������cw���m�}�u��h�������gO?�����O��];�����J�
]���O>���R�n]k���v�a��lj�E��e���u������}���n�h_k���m�����}{w���<��"))���-Z�p�n���r�Ww?���=��3n}�z^��n���:�,�s�=�)��G�[o���W���y�����#;����������Uw{
�B������y�o
�2{�l���;���9s�W�D M�	}��a���J�y������n����TT��\e�6��)/�E���U���w��J�B��������>����A!���w���d�M~o������o�������+�x�
��;w��_�-]��]��^Yg�s�y����2t���/��;���=������5k68�i_Spv����SO������7������2����6z�h2d��>��+m���6�~��Oe�ZOa�g�������M�f=���;�X]�P�Au@`�l���6|�p9r��������X������O��$U��t���?����"Tyx���m��u���Ey�����s�
)0TUz���K��t��6g��Go
b�����?w7����S�=t�P{������}�,Y���'��T���?���	Q�G��g*B����{��_�p=R�p��)�\)@6�!}�2����B���{/���K��P���s���O<�2�j�7�.a���TwP�V������~eT��jvp�u������]��n���;>�L,���DU5���]��N�MM�t�6��@��3M�4ib[n��{^~��Ww��k����v�i6h� ���]���\��FYj��9�1*;�V�
�tQs���{n}�]E)�����Q���v�������,z\y���9�O��gb
���]Y�}K�����Bj�6p�@���q������������:O�J�e5�=��3J}��=���-O����\��IZ���'��U<�U���M���d0�v���r�u�����f�
����D��*��V����� 0�*�������tm����u�C�� ����Np'������*]��5�SO=�N?�t��g������Jj��o�[o���bB����V)��*B�����[o]�}}(;���tA�c�>�������L0U��
(��A�j��w
0�7T?9��#\�^
�����.�=��C�{��_~q����L�W^ye��3������7���B��GA �cz(�`�n�)(��eK��b��&%%�v�e���C��]�9��<�s��z��Iiu	<��4}�t�/M��n����]��d�~���F0p+��h��t���N:�$Wt�U��� ��	�������Av�:0�	��HEReH���T������IwbU�U����-�&R@����bi���) ��[x����f=������H��������d;�����s
"(h�g)���^��pE��4�u��v�UW��Qy�5-��A������VE�������ej^U���MCe�)S+A!QPh�]w���\s����oV���a3�
������l2��j�"

� �pRe�4��h��>[Rb.Uv�����N���J?j� 
�h$$S�7�}��M����+5���1���G�\���8���T�z��+e5?R�'|:��}��5�*OFF�}��4-S��a�����\��u���Qg���Z��
z�n�-���@aE��Q�O�@~E�me(�M#�����g@mF`�"j���~���gU`�|�<������NT/Yf����yff�w�q�uT���9��x���J(+Y�U�f���>y���b��z?�F�yb	��)��_*�[Z?
8��0���G�G��w*(�ScS�����
86W�����FI����4e5v�a��gO���R���V�;�}k&������(���?������/�Pwb�T���7����*}Pw4�������N.j�����l�
���3���nX��%8o�=�������(��,<�@w"��K������?7<������w�y����'����~z�C�P:���������Z���<�>t�����k:�;eQ_
q.���]��5~�x��NZNE��u��#L�u��IY��_���
4p��Z&m�h�S#�(m�,:\p���	~F��������nW�`����i���V[m�Rz+���|J�U9Rh�Q�z�:�6�]!U46�E���)�Zt�S;�`j����=���2���tM�[��^�I�k�������ud�V_�������;������GYY>�k�]���~�:���������c�E?][eQ�X���o����)ta���e�~y������14X��)���O�T�+k�~��
��I�[����;�:�w�����X��/�\_y�:��dS��Zmc���l�>m���1����v�R��1\�)�+�>�d����v�3��
�Q����E�Q�.�Q����hp}���'t,�V��[U�P���*O��~Y����We�I�Fa�gt���k�i[h���6�W}$���4���FeQ������Li�����rTZ�/��W����U�WF�o����f�#$�ZY�����1�5��)��PYC���c�������������\sh�n������[yl��A�U����2G[��q��?�sN,�!�����F��
�TD��Q���'��~�����W�m�����J���/�8��$��!�%�s�9�}��V�#����H/���}�������o��qm�����a:����>���:�e��B��a���H�z�Ek��v�g�A���WS�.�=���^z��x�
�v�
�S�FC���5DU���E'V���F���
����.l�_���
&����6x4Z�����U�UAV�e���*�:�����v�5O�U��S�5M;��Ye���~�$�Yi�U��+)ZNU�u������~�{uB�N����h�:T�z��@[d���E)s�,Z�
�����gt"PE ���S�Q�y���]`Q����7U�+��Ti����g�q�N=�{t0�������sEf����r�2/:����T����@���c��-�N}k�h���J�L�F�ii����,����}�"��� ���w�����q,�J�Y'	�N�&���[��*E*�z�~dYT�����QpJ3��x�rh�h���;�:����CU��-����T^|*':������2Vw�5������<:���O��,Z�:�������$��S5/�Su1yN�O������*v�.��_�0__6����+�>?��C�4E�]���(XFu>����W_����~g��j�G+���]��9-�\i94���K���E@Z�
*+�j6���4���Q�Sp�sss��Q�m�y��>���X}O����w��T�L�Y���kT�+�������&���9����F����
R�+����u�}B��v�9M����|���r+*;:��z����I������e�zSE�{���w����v�	'����������w(����j��Q�\��8�yP����jY����5/m7�������*��:�h�t
����~�1#���c��:���-��/�<T��T�\�<��Dt�R��qV��h�a�o��q�EO��v�w�as��j��7%S�F��:hO����6�3��*�������������
�
y,Tq��S���eW�M��4zM)����>����CT��E`yt0R�7ZeG'Ze/��Z����/���'VZ��eu o�=����y���_oZ���7]��]�
���[U�\'N\��������;pk��F�C'{��X����
����K����:��F�7xWM�U�TA��,�=:I������S�s� �FG�WE����D�;`
���ZUH{����#��p�UI��XG-��|��2�h[�7(���O�S��"i��r]@�WQ��[�2f9U9�������
,��1�Zn����9��e	�>o����k��|�G�W�o�����\6UM���t��i����t��)5
Z�y	��@�������V]0o��oU������tY��0) �z���^�����=��}*��L�Q?(����:��������4,�UR^ "V
�x�9S6�u�T�q4�����?��s����d�ETp�J�w��^�>j��`h8�h����w8Eh���Nx:�z��.R��^���9��R�����=���������~:�.�4lu��y���.]h�d�(����+�.�O�Q�F�R����r*�������2�x*��m�AM�"O��Ru2P�R:�w�D�S��!��,Zf��|��K���h��V��5��R>�)���FkJ��.�t���/����U�����_����c]o��*z�Lu�Q�y�������������{n�N.�hytA��C�~�x�nPi��n� ���E�We_MZ����F�Pp��u�uZZ
s���#�b,�L�X'ye�����7�\R���o4��ln�������}X��j���E��V���U	�8_M"%Zy�`���K_�s��%�}��;]
4+����s�R�#�u�-o=��vNUW�)�����?���������c��s�~��-e�D�o�VP�'����k>��REM��?_]�h}F��l��s�g��QY
��h�y��j�U���{����,���k�yK,�0�����i����k?�6W6�Q��k?�����Mt�v����,3�m�0�#��.
>���K��}��|Z��T�RYP]O�Eu?_iu>	�#)��������"�4��F���Hs���|���oP.�MBE���O?�=�
����r(ZN����:���FY���V�}-��u��BE���F��������������y������A���s��`j^��h��g?0��M�-�w�{U�W�>x����a�8�FO4m�x];�����U�s_M��!#�wu����k\��������+��9��Spcig9����������������k��'����
HY��
��Q��N�L]B��O<�N8���D����e���u����\%A����
���^Sz�h~:�}�F�Pu������:P������:�Vntp�\}^7�v�3���mY��.(�A!�iY���~���*�:i��L��T9��`K������������G��u�ry��������j���xS9R����_oC�q��	Q�U#�h{�3(T��:������W]q�� ��K�*���Tw]�/��QY��������TFT�����.�����'l�����gT>�=����d�������%����
:���!o������D������TP��"�-�������+@�]�}:���RU�U�RI�#��4,�*S�"��}
�(8��v�G��k5_}hU�&]���
\��)���y�_�~�Q�����2*Z�0����
���E����������RYPI��m�s������G*[�m�m�����������������������I��h���u����;t�3f�F�eE��zST����*��8R_@�m��e��)Re���
7�`C�u��1S������A�0�<�kt�����:��+�����L�m����[nqY��������.��������t��C�wh����8����>]C����PU�Q����k�x�j���:�[`H']��H��b>�.rUI�F/�6�����;��s�o�H��v��Z�U���TJ��W�����^H�����
��3�7���T��kZ_~%U'+�U���i�V%�,:�SUA-�d�;���O������R�A�p|Z�h�V�D;���_��H���U.��TZc
6��B�#Z�[<h����H:p���*(��Fi�D�mup������T'��_xi���|CTF��5tWL�@>��~�H�.+8�
�S�~�+���1`S�R����O(3^�M��%���:��������H����R���
�+;
6�U��Jv0KM�W�� -��������*�:�F�u�;��u��}��P���%h�����}R�_��7XF��4�soE��U��z,�.t��S]w�����I:���M�4xcE��>/�v^@�.�$��I�E��E��R�(�VTM�7�D*OA�30T���S�X�_����6'�lF��RUS���-��+�7��h����I�����>��`�>a���h<���~�Y����� n�!>U}�)K��>EUUP���D�g����V�S���Ebd�)���e1��Ob
fDn��`v�*�t�Jlp�T�^�;�A:�����S�W]��9-����;����EnY�dAw�|
�����o���Z^����e�D�`fHp��I'��;j>E��Vy�w�T�����6�F��+�Z����.t�������N��h�Ox�������y��t@w���v���or�����eu�SGA�X���%Jy��@�*re��TFtl�+���-����
�*����2W��@k��w�D�������(
[���V����*/���*;L�+~�4~
��X���rU�2��Zm5i+��������RPU�G�A�`I:��u1�����������*��US�M� ��w[n���
2E�z�v�m�YR�����4��{o�����T���Ge�I�
����]�A����?.c&x��,�C7$t,�>��A,��`�����[UG�u���CM;�Uq�N���T���m���G���6�v�`��v�X6���a�2�����~�������~�&�.Th����J�O;N0�I;xy�"����8p��x��Dmc��Cde=����vrUxc�JE0X�u_^`(�m��`00�\�_'�a����j"l���i����0h_	���h���)��V5��`��*'�6�A�v�r���'6m������v���y�3����QevT�R^"�2��+����:�T�c�������3�V�����Th>���2��w��P^ETdT@U��
�F��u����U�*�*����������x�S�/�e�����]T�./��Sy
�T���>/:��s�U�M��t���Sy��������i������,+��f�����2�����>gT7�
t<���:���L��*���j,�����m�����S�����R�����l����J7I*J�� U,��8�k�x�j�����[`H��3Xi.�
SY U:�#;��X}�D�;����`�Q���P�+\��t1m��=�[�O��+����k�,:h�BZ���C^����;����t�1Q
n��6����w���4ua��<�W'���LtB�����/)�:���T^�t���L�ry]�.���t'(�P����hh�9r�;	�~����rc����u<U�X���-��KP�s�w��;��fss#����Zq�vv����#��� S�����}~���3�X�X+���\�.�����Np=V$3B���
-w�,VU}$^�����t���,sj
\���"6g����lYV}-l:��&�n(��P����B}�*H��P���^zi�f��J�?@�r��k}�](�Kp�<���]��A�@�����:X?Q����
:����c�����xs���h����]�i���"n��`�F�����"������y�T�Tzp���bS�@�����pL�<���q�=�����/���]w��.vu�*S��L��>��X��D��XoUM��������`���\@hd��T���`d��w<�{<(���E��D��o�����<�]0R$��:y)�U��R�������E�����th{�{����A�e���N�G�+*+�2Q^��:s#���nG_���.��|��k�n\hy�;��(�w��W�2�m�G�P��Z����M�m�&��>I�;���R>#����|�[�)L:�Vt�G�yD���,�X����#$Z��z�� ���1M}_*���D��D�cd�n���4\�
���H�&	����Y���~�T��(W�����E�]�U���G�����x�Ad4"��lG�=�Ju=��%����v��.�
�I�o�X��
v�T���&��E�O�j�t�M7���z�Wg�jj�JiuQ]/>+J���$�}D'8:���x�TJ�R�f�2q��.e�cV.���BTZ�R��5���t������H�>���_>}��������\��`�W�xS&�����X���f
����g}�*(��f����HE���������z,�}F�D��C���%�)�1������5m������)������RU�w�Tg
vG���n�m*�65O<x��y�����/�W�G�O5�3�8��"��C���}�J`�"��������v����2�k���JNAJ�T6A������@<(r��ZJ�,m}��d����������X�*���RJ���"�;t��f!��gu���������������>�3YV�y]�)�����u�-���:Q�]�hY=�C#���Q^0+h��Q�+�Cu,/�JA����D=����B��b���u��F�y��xdKT��xG���P3�`�A��>���Y>+*��Me����H@?��h�@�0����ya��-0�A�_�uTS���L����T�6�
�����S��;"�mce��K����n�f�>��c}��qYl5Q<���
�;j��/^�
n�����(�TV�)����	<�*�\�HFaS������A�\�<M��
r����c�9�P#/t��6�e-�"Md�
�L'�x��H)���"ms�*���`])��qFwz�Tg_�]+�S������?�6��N����O[��]�.,Q���1'��b����������\U��M�*z1S����s�����U�.<*3L�h��Y����WEE������������G���\����x�u�	��0i������r�C���/2x��;8
��{M;Wj-��R"�����d
��~%
�������uP�X���������++JC�+�@7��:��4e-m����U]��\�x����	���"n��`���������T��(�'�-k����.6gz�N��tM�!��u�Q.�@Ck��w���{	�l*]0FvvV�������e����x�n��V�GA��S���B4������-��P�|���=�������"���W�t��OyV���cE���fdeQ��oW�_|��;�:�n��(/����)�*-�����+!�����`���O��R�-�t r{��t<��F���:s�E�O<����*��]^eV��
}w��K}��W�������<��,�j�T��#�P}/$�l\���>-(��Ro
��[��Z�5���>+*�4�n��C��
~��S�6uHTZs2e��P�p��A�x��e� �O��:��3]vP�-����Q���q���]�i���"n�!
���V%��t���6�e\��i������6������C�����upP���dWt�u���/�cg}�"m��A#����`'�e��
�<�b<����������Zc����e��r1V
����U16���ZUw.+]�)"*#Zn����}�]w-5�Ew��~\��*;���������f-�I/��Cw����#�A������n�k��1�����5� �*Y������W���:���:.�Za�v6s�|�}��	���s���~P�,� ��U�W�/8L�:{-m_�F�'��������,��]��}Z����S��#�P� ������?��\ZP�<5��&]��u�X���>���y	��B�>���x��sW_��������D�cc�9��:u���������D����,G��(����e����$x�+���j��8ZQ��?��s_u���*��"���W�K��G}T��W�Y�g���(�F����������;����^���Q��$'[��K����n����M�dh����Y,�kU��:�f\>���r������?x�U��rUP��R���F�*������}D�/8F���H���w���:xWOw�6W�lyt��]I����w�/�eJ�D��J$U�uS�H'�`�U��[��[���U���w���*�_c�$by���*r
�id"��k[�������A����\U�?���r��~�����xm��������|�7�MJ����;�c\p�1�-�������!���_k�����TY(�J�������s�����1��:�)U��#��H����`�(���:���AU��M^*B�US�Ma
���?��W����c�>[��,��,/X6uQ���c��
��I��uX��t,����~WY�������s�~3+R7w32��u�f�T�_�E�i��c��a�Cu8�VT��5��W]�-0���~?�����K�Sq#ic��l����-x7M'}V�(����~U����NEO����Pv��u����Q[�`$3�
�N�J�������.�6�vj��)���P����N��[�$U�*�9��[�>��^x��J����~�;**��T��Zd��*1e���9r�6�ucS�z�E����ZV���� u����{��J��M������"WV�E��`EB���}8�D� ��h������*#:&�7����`@������r���O���C�`�7H�[��`0[���Zt�U�J�����
Vi�p�^5�	6�T�J����`e�"4��'���t�=z����9D�\�����Q0�Ble,�V�� �P�:G(��s���/P�S}$Ri�]}so�(HV�E��M�1X���[0��4��FM���Bd�Lef��qQ������c��Q��8��S6������;u������=��Ms#DM�0���0)��z��>�"���.��LA��Z�:�l�fd��T�x���6�)�<�TT0cF����e���C<��S�
r��~S���x�g�A����:K�1��D�7UL��V�:���QPF�Z���)'�1}*��*,�����>/��[@�5o�L��)S��`A�n�
����TH4��>#�]x�G�������
rd��*������[ilZ&U��NTXu���J
s�J�@��9���2��,�{��{�2���������"�:��DT����z�rD��U�����F�S;�"����_}����K���q��7=T	������y�|i9����T����zk�;X:9x��+��D��>�l������P%H��_*� �,�_|��=��3�}�w��c��xn��6R�N�_���<:(���!���������.~��Z��_.�*/Z���4��%�Un��o�����o�i=�}�{i�|��K�T�Q�G��>�,JuHY���}����e�~�������%r]l�����w�i+���V���nX��^�"2�cny�:�����u<��<�
�.�4_�O�������I~�R�?��c�f��������c�~�~���~�^���������T9�{����:O���r��U�`���K��r�������*��m�����*����v�X�A$-��u*+~F��&*�u�u����.��.8������V}�������]u��m�����mZ���.�zh�n���s����Wh�Pfh_;����{#i�����^����~�����/�k����uE�FM�t1,*o���������iG���Ae����~��:��������.��]�������-�;�C�c��6�?��|U_�
���Fp?�k:��8��X�C�@���"���w��������������w�qAj��t������y%�n�������c�������v��m�`�/��V�#���T��m��2����}��}V��h�T*s*�������Y�.��UnU�T9���_�RO��h�����a^;�����.���$�x�FV�N�JU��N���H�6K��T� ��+��4l`�������0�������{^5eSPBt`�����8�.�}������J�����N�:mt���I��P\p�M��Qp��G]�����kR)��tTZm$��9�������t�Q����z���A,�R,�Yo: ����]�+�l�J4U,t7�"��xZ�Zo���S��},��N$Z�zh��P��Zt������kxH]��C�`EA'u]��L���/�+B���SO������+������s�O�X^"�qU"����V���zVY��1��m]�����~�.�"/���
���*u���T���J�F��+`���E�(�@��	�Q��SU����']���F�9lS����Ns����D��Ha�G*J�Gy$�~�`���$hP`@A�X�G�.�N?��R��Q��D�_�B�o��n�s����u}-����h���������"���\��Mi�4�o*]����)Zw#F�X����-���J�1�����>�yT��2"�ai�"���)�O�x�_�T�����W2V:�({J�"�8R�� ��p���x�\�1�:��j����M����g��A�@��5U,����F)b�_~��H��vJmP������������5��p�3�;w�[A���v��<ZgZ�W^y������@PZ����?j������c��*g�v�U����:1� V������Rm
e�����eX�#�A�H�~[^[b-�Yg�U�E~E�Q�]���Zeh�����~g,�m/�u��b��)t��������E��3�8��J�~�~�%�\5($as�E�T��X��z����U��s���c9�k�j��+u:w��8e����J�P��X����z�����I��+���-w,��_/���2/��C}D������Ae
�������c�����uWZPH����E;�+�~��}�*�Lj���8�}��/�PPHt>���qUv-o��U���������.���F������;�lN&�f��LvK�iYTU�65($�M���X�����=u]�:�O��u~�V�F��q4L�Z�T�}5I\�����ATAW�R'[�n�6�vRLU��L,/�Z�o
(��*��?���A�W�PtU���(��N�V:�O�.4U:���GnU�1�:Q��G��W����:�j}�b���"�~������)������j=j�J+������O�A_�U P�B�
F�u@�.Yo�n�-�Q+T�RR��_f���	���w]����V��d>-��O����������"�GXi������u��E��J��:���w����W
�EK��u]�w���A������n��4�W�?EVi��-e�/�ePH��������aUJc�'�Iw����a��D,/������K-��M:��{���wh�����c�/�cny����-]P�vN������SR��c�e�>�����s_�1\�W��W�������t��z�<�Dy�Q�R�-��j�k�*��~]@��\V9�X�a�z���P����Zn���������z��q,��W<�#���,�S���	���e�o��*>�?�}
Nh9U>���r����
�����^��PV�_&|*�������M���M�|�qB������_n���>�Se&,����kc�}�/���W9�qH�*u��� ���X�cp?�����M�,������+�����2��uU�[*{�\7�}N��������cyi���rX���<~�B�De��'U�P�V��Ee@���U������j�������u����>��k�yT��>��h�v������h6���&�{S��R32��jg��*��iS�����5U����L�U��*c�o�{T���^"�m��
�V���S5�:�V�Tgq)MK�|�SF)f�E��d$���Ci���R���5��q�|P�Dj�H���*n�!��S�N�F;Q�dit�TR�
���&i�j2�c��{��k����8���k��:��;S�V�=�����;����=���ze�Pw�G�^������Q�����R+��S��k
���P]���i�K������_���,Hi��0�T:�N,t>
�:����o��n~r�!�&:TWq��4t�1��:��������K
Hjq����!�	
������d;������n�^�z���n����u���w�}��K/�+�����j+�UT��6%@���!��j)C��!�Z��@-E`��"0PK���R�j)C��!�Z��@-E`��"0PK���R�j)C��!�Z��@-E`��"0PK%���J�mY��x�����S���Z��y��G���ZRz��@|`=<)�^�4��/~��1��sjKK�&�Z�������5������Xz�����[�l�/���/~)�g&�z���[�����s��v#0�&X�:���%q����g��e�^{�u�����f;�����g��u��y���}���>��k�yS���g�����A���6z�h�����)5S��?�g���e���v�G���_o������i�G��eY�|�����=�<h�����OVh��!0@-����o�i��r�=�����qc;�����
7�`\p����/������>j�_~����xSP�V�\iw�u�nN�����.��~��o
�
��t�}���Zff����K6n�8{�������k���>��C{��g]�NX:v�h����]u�U��q��1�����#G�[o��M	_�&Ml��Q���>�-�Z�Hp��0a�k.t�������oIII��z���Yg�e]�t��S������vP�>5%<��cl�w������	NM�R��s����B���t����e�����k���S4e������j�9�idCs��u���y����N8a�~��.]j>��u������_|��.�)����`��Ev�=���{�����[�{�r/^lC�q�W�m��!?��+,,��Q"ZC*t�+7�`j����k[�;�15o������"�)C`�W�N�������+��^{yS����x)cd�-����f���v�9�����m��w�m���^|�E����������RSS]@�{��������������y�~������^SP��>p������*I�����%K�X���+��#}���� u��e�]\sE�i�����D�a��.����*C��j������d�1���������+W���������M��G`�Z*//��-�Q�.\h��v��h��{56������>��^~�e{��w\�$un=t��2G�~����G�O<����7�s�=������K
=��.���M��oX���|��w�����u��Q?W\q������+���������,j�\�;�����zY�;�<�9��v��!�bO=����Q�F.�������g�m��Ow�L�ci����{��>��
B�����P��|�v�3�<����/>|���53+��G���5k������;�eq���o.PP�����r){�]�vn�>2��k����7��3g�=��v�I'��G]�m���4���}�y�o�����:������Sj��f��g�}\F�O�R��2��U*�����)X��Y���?\����v��*:*��@���P� p���wQ_Q)))�������?�}���������Y�����]�Mr��������[�n.�I�KYC��
�T�R&��i�\����n��rp��7���P���Q.w�u�k���_�?�����?���k�Sm����mHD-[�tM���SQiii��G�����?�FRS#57S����>��T�M�;jBV�����w���"��(e�I��z*C�B�w�k��,'e"����G�`M��4����DK�A)cH�=
�Z��{�|�����cG���^���i35�R��zM}�T&�����O]�=��G>�l�o
2�}���re�X���)t�����U>�<T�!�F�R?1��g�C�����6%x��U+7"��)S\�
��k4��F��LF��:nV�Au�,��?"��?��#\ .V�C�GRs�M)j&����lL#�i�ze��se�it2�]Pq�Hp��o��n���.��G���G�X)����W�@��7�&M�xS+��^��H��FP�1cF�Y����_�h��������c�m�I���+]�i��v��M�zS+O�Wi���s�&M����&O�LG�T�!�3��������_|�M��.��M��w�ek����J�tA���4?e����/�$*J��#s�e���B���)��k�MM���T�G�P��<����b�N���S�{����s��rrrl��a��qe
ER��j����@e �)D9��C=�.����P����}�l�c�=��b��+��c�=��3he��"u��_�ip���+�S����������O�!�}Z���~�5}R8up��K��s�=�t�R����g�}�:9Ws��O>�����^+�����k���������?������������C��>���9U�� 0@���6W]u���=���:�(;���]��~�����r�-����������+���^�C=������v�>r�
7D�<�2UD�����RP��s�q��q�.�H���<t�P�J��W$��p\t�E.P�����<��\����We�l���.��3�t�U#���j���������/����z�����~6@��P1
����g��:o���9t��`�1�sv�O�#F�!���G��(Q�$���kL��~���]F�2O~��'�����fMZ>�Q����� Q'���w���6�=���u���D?����v�inH�O<1�Q���5�\�:���o]��_��F��v�=���v����yk����,RY�L�%���\rPa����cr�����=Sm���������,�'}�=�2�j)C��!6����{*m�}���]F��g���@�FCl�i��m�'��.7~��������N��TwEkV����m�����Gj�]-��������?�j)���R�j)C��!�Z��@-E`��"0PK���R�j)C��!�Z��@-E`��"0PK���R�j)C��!�Z��@-E`��"0PK���R�j)C�h��%6t�P�u�]-))�v�a���k����Q1����{o7?����6o�<�+,,����N=�Tk���5l���>�h{��W-''�{��4O��_v}��[�_RQ1�9*�����s�=��O�n�o���i���.]j_}�����{��^�z��K,~��7�����;v��,X��L�����~��t����DAA��9����
[�z�u������l����A��G����O����.-{��m�U�V6{�l�?�y�����������JZ�v�
6�Vn������l��������K/������-�����;������N����3m�����?��w��Q6��i����������N�j}��������nc����]B����[m��Yn��~���.��k��	���e���P%��;��L��2������9���n�i���^W�(�|��k������������gdd�9���2�F�m�'Ov�E���R�i����>�x�����Nv�m����n���|�r�s�����t�YgYjj�����fpg�}�[-H\�*I�����E�V�Noj	Z��n;�<��!�.\h��w�&M�xSKh~�.��������-rYB�r�kBi��w��]���"e�>�yH��=]�)H�u���-��	$.C���[�~�K�
)����~r���sy���m��9��n����F�r�-���R����zj����T�^=���~��7MM��l{����W(���[������d$.C����g���e����������U�\�>O>��k�Yt4
$-^����j��:��)��eK�%��_�i���D
4X�,�����1��S�2}�o��zi��u��yS@�!0TI
�\u�U�����������j���]z���c����{�(]~~���YZff�7�|~����������Qv�����w��C`�����_�c�=�������C�.`��������]��:J*R����{�����������>�eiu~��Wv�
7���y���,))��7�Q�y����/���L���g��QnT1e(�t�M�;7�����,M�5k��{��byOY0U'8RyE�1T	�a�����1���B���{�mw�y�k^����)�����������&l)))��>#� ����=/���������'���H<dU������Q��������.�I�&��1c����������C=d~��t�A�+����a��|M����~7
Y�!C���7�l#G�t�������c���k���l�8�.��b���{K���l���l��5�����]�:u������������9s�����.�G4�����P�+V�p����={�{���g�����~��nZ�o����Us5�B$.C����4
�h��hIW����}���.p��Ly�v���������?���\�<�d��w�}�k���/C����[��j��[om��L����'nY�L q�WN:�$�~���?�������)H�,�A����SO=���n;�ZY@�<G����iHyQ����������q��>�#n�Z�a���g�}�>H���?�_�W�
�<�@������HM�������{��g�y�}��	$.��$�6��~��-[��])@��?{��+�t�#5�����t��}��P����m��	n^m��q����P�v�����.]�x�.���F>SHM�����f3f�p�k��e��v�%���iZ�V�Z��W������t���M�U��?��#l���.���N�N��o��v��o4�WY�!Y�d�=���ch
{��M�=��+��v��{��
]��
�����O#5����g��.�(�y���L$�;t��2��?�|k����.���R�1PK���R�j)C�T��J�f����r����_�[��eff��PuB
��Y���_��_~�~��G��
u���z��a��v�������L"@�B	�������oC����'{SK(��ys�|�����w�n�]w�|������M@��=0��?���[o�e�o��w�q��gOj���F�@�*Z�t�}���6f��Y�l�2����o��������F�e�\s����v����a�����U�V���#�<b�}���s�=��wo���,n�!e�����z��e'�t�eddx�TNNN��=����~�m���[`HM���wf�2������+n�n"�B����/����*K�$(�E\V�^m�=���k��?�����-0���k�/���[[������.BeffZ��m���~r���z�[�����?����j�����.��:t�`M�4���t��\B-_��`�~��M�6��Z������Q���
���L#�-X� ���V�M�P}��1�v+\���=��-;�4�����������%��W�h��y�~[���-9�����B+Z��{5���<�������R���n����������s������URb���+m���yYNN���n�:�@�(����~���y�����V0���#M��4���o�9
��}�a[vv/�zy�,�������&M����wm,��9���3l�����H���[RF�eS`k�cXQv�7O�M����{��g��z�M�<�M�;��W��]s�5���f���M����P
�>���}�Q�o�#zX�>XJ��������{�Z������nuO<���|�%7j��#�����U7����-������*K��}������������Ho�Y�[�Z���y�@b5c(??�z�!;��]P�A���E�U���l�A�����o��n$35W��s,��,)%��`
��g��B�������8K.>v(0S��K�
�d���sA����Y����v{W([)o��=�UK�z[K���7O�������]w�e{���k>�p�B������Y�&Ml����O{��������^P�Y�/y����Q�����<.���(+�����N�����^)[���Y���]P����,�^=����M����WZrc2$��C6v�X������N�:Yr�;�-[��o����kg�|���"P�(����LK�S����e����R9�f?����-,�������L�X���q�a�VRS�����?X�cO����x/@b
-0�j�*���������m�zS�k���u������kk��=���I����K,e��,�N[5�z���AnD��'ak�������]��y?[�����s�����W^�F7s�;���{i��*
��~�mI��g[7������g����_�����oB�~���~�&e<@MZ`H�N��!u0�ZN�@R��WJJ����HA!��U������-g���Wr��V�z��{h����Q�bQ����o��_��+�wJ'5l�����k{�V�p��Y��'EYYV��/+*(�U��lk�|��rs]���~1�V\v���7��lB�yQ�r�2�DZ`(==�u4=��r��V�X����~}@��r�Ii��5}�k��8�h��]'��/pC��Wy�^QP�>���c�����W'X�{����\�(k���}R�li��o4��"k�����M�f��e�^��~���H��,Z�}�XD�4���%BeffZ���m��q�Q����*z��gl��I�����������|��l���_RZmo
����?���gV�g (S�"o~i��i
����6s�K�^���W�%%������������kRS-��k-��#��r%����:V���]315{�����.4!P[�R�0
S��kW���Km��6g����u�gee�>����gt�S{���sj��]�p#�EJ�y��P�����b����%e6�����C�����V��boj���[Xz���S�07t}����siB�6-0$;����y����N;����'�������5��?�:�6l��i���$��FA)\��e�s���RR\��2�";����_iA$7��T+���"o4��f[�~���L���I�_���w������	YQv����R�Q�����C-����}��5�M[q���/%H���K�.6q�DRVP�BC��	&����\�M����
�/���o���!����u��5_�|Y��i^������FAs����FM\?G�+��L�P������%����<ZXRZ�{�2���&M���@Mz`H�7on����i��Y^^�-[��r�/�~����?�i�mW���JUS��wr�P�N��M�[����kj������������k����7�o�xZA��-[ZJ�-���
-}�N.`���h�M�fcy3�4�M���^nZ�O�f����h��������}�W
t�fIu��iP��R�G��3fXAA�7U����I�
F����=����fM��| �����y7�z?���]k'����X^��Zy���c���n����g�=�WVn�M�&�����K����Y�;�����v��Yny5��]gC����6����{�I���������`�|[}����VF�6��(o�������j��{29�}F��<|���C4�X��:����f�]pj�sO���ce�}�^�l,��v��og7j������DK�.���{�~����^�[��������W_m����Q�FY�f�>�t��^��gOO��\/n��j�M�d���u�e��7KR�����m����G�c2����3P0���
l��<���B��bQ�[$����X������ws��������Z�$[�U���2k�wl�alY:k�j���SX��sr��X��W���n�k} G���w�,��� �������v}��~����In\<��B��#u�8�2�������?�V��oRS���
�h�r7T���o8���CeQ_B+o���3�����q�a�+���1�����+]@H�������5
������=�N��2��M��
-
$Q}/�F|�kyf]wI�'/�k��o#.��IA!����r��J���r��wr����V7=�vlQ�>��w����y�R�x��nc������g�^R�}~��">��@�O��o�������0�

�����_���~��=I
YZ�}\�C�;����M�m����g5�K�����5�����B�q���x��J����e�Q��?�LLA!�
��1���1�.��"oJ��t�Mv�-��))��f�����K9.k������.iq��XM�n#�e�h�KVE����9����Ni�K�d{gf���vN�C:"z)�>�[`G�Ku�.��E�v���]��n>��u���j��6%[�d��LZM������3�A�n��2������C�s�=��6m�Mj6�Y���q��(x2��:�����l��B�wT���i��]\50I��Z����~6��,��?O���n8�`�H������_�f�k�-�>�����������_��;�QGeo����V�����r��B"Y���~\X`�f������3~���H�{��2���YJ���N���M���i����%��y6`��k`((33�l}���`���?"�QT�� �t��*t�w5o��:o���l;�=���K��<����j�,����t�P<��P�c���yqK(TC��4��C�<�����,����/��N;�uZ
$�������v�����|e%Y�:I������[�'�*�u�&dO|P<�b��f[4�sT�X��I�e�d�Y��9.C)HN���.$��C���I@"�G��7hy�����5�������$��f#�/��<�M������*KM��-�5%��M����d�zQ������~�a��^�:�Vg���{����������������?�h_|��G��u�W����s�����Yf��qz�����G�HK����c��,;N���E�d�~?[huV������EYACFg�'�K@�3�\�hMv��b��I�hE�u�)���6@���!�,���,X`&L�����-[��>�&Mr�J{((��m[;��3	
!���"y�����m��H=�����YU��,H}i���H/^�$[�[��BmZ&�=g��N�K�O��Ps�u�z�j��U�#i������n;2d���SzV����Y��N�`sx��<{`|���eI0EY7A��y��\{izI`(ee��1T�����XXh�����{��@M���III��Q#k���{������6�����G�&M
!��	��_���������������OF�7�z�q����zI����
>iZ��ie���$s�,��K��Q�$�A����1�o+�����]g��n�u�0���T7=��<��krVa�1T���_��9���R��%���
�*���v�Z������'�pC�k����={���DI�j8e��5�9%��z9��E���	�.c��)�c��w�)?���r\���Pg��}�o�=���k�&Iv�����=0����FS_C����]p������`������������~����8z��f��Iq�����l;��uv�=���'��P����#��h����������0�����q�{��k��E+K���3�X�FD���50����A���k�q�~����];�\�����e���K/����{H���������@��+����K�3�O�{��q�E���I��������6-�'5S��ff$Y�R�����C����o�el�P�z��7�O�>v�	'��a�\��W_}���7�F��:�V�H�
<��n�_~��i�)���j&����N;�d7�x��l��{eCu�����?�<�@�9s��[��{a
-0�z�j�5k�u���Z�j�M�N�C����-X��e |�w���bI]P��R1e
)kh������-Zd3f�p�����C������;������g�u��G�&gw�y�M�>��_�@�BmJv�q�Y�^���������k���Z~~�������O[�=���v������$��p�2�|7D�����);��#���6m�xS��C�fdS�Nu�������'��{����jg�q������V��P�TY`H_�j�*���_l�����[�Q�233�w�*������I�������[���C��w��4h`'�x�kfF�@�
5cH���}��6p�@���M�]�v���fYYY��!
W��#�<��JJJr�@�B
�����O�i��l��av�a�Yjj������;���������Q�FY��]�W����)�3a��%4b�7$}0($�LM�z�!����W_}��@�B�]�����kGq��O�,;v��?�f����� |�5%[�|�kF�����������J�&eW_}����o����Z�&M�W�4�s~�u/<cy�N��u���5SRfK����;�lK���7GhC
6������M�f����75��K��l!�_�P3)(���,���5>($EkV�������&��PJJ��}�����i7�p������+Z�d��z���o!�J�����)T�����8���� ���)��5kl����r�JoJ�_~��}�Q�	u�=l�=��^1�?�������y����:���\0	@����C"S(5+���w�� 1�-0��`�{�����{S*�{��n��f��yS�$K��{���O��{�!n���������0��U�N����eddxS�$��f	mT2��!�YB�|��!�Z��@-E`��"0PK1*�9�
��iy���[�[�w���}[����l�����j2��)(���l�`V~�
��s�E�I�
P�m��Paa�-_��=�����@bQ�PN~�%��7��j��Ck���^x����^���u����o'�|�5m��=���6m�{
H$j>��>�K@j�PCK�,���?��8�����,''�=�n�G��
X�-���>�����f���}H��|�4jV��B
�������/Z��=���.������y�l��I��];�>}�-\���}�Y�5k��3���j�����/���:u�d<��p����l�}����9�;�0k����v���Z���m���f�oSh�!�-4w�\k����l��M+((��?��=�g�},==�=��f��������C�p���t�F S_Cm���=����
��!��P��u�U�Vn2e��?�hS�Lq�m���n�,Z��5#�a��^�z�T�)���<�;w�q���m��fc����o��V�^���o���{�:��tuD��cGP@�BmJ���N9����{�s��?�x;��3��+W��~�����?���=�IE!��fdo���}�����^{�	'��:�u4}��7������*k����$�n��4�LT���=+�����g����O�g�B����B`@���Q�QNN�����v��G[��
-))�?�p{���-++�{W��,YbC�������k�]w������y��wl������};��S]6��C������F�����.}��[�_�2�4����a�������_u�c��v���6i���R����~�C=d
4������aM�l_z�������aVEi~��7������I��iX����4���?l]�t��]����F�iW\q��Nu�����~
������]���m��n���g�Q��<�H{����c��1�X��h��1��1��s��	&���?[�������B}����~��>��#�P��G���^x��D��]t�w������������'���3m�����?��w��Q6��i���[n��w��>��s�:u��0|�p3f��������j�f�r����o�w}��wn^�<�@�2�@���!5Y������:���?;;�=�E�:u�g322�)���(q�e�������6�l��R��O?um������~[Y��+���'�� kJA(l4�����x��n���2��	N�}���v�����b�y���kv�I'����\��V�r�G
������~��62�CM�2�\PPC=��?��Cb~���	4���zN;����B������|�
��)Sl�����{������������hn�h�"�%���H;���u���e)�H�Y�Cz�����V�D��us��e����+AYP���j��l�}���V��7g��|��vs#m�����!5�R�A���7�|�@�7v�������m��~��7M�"����{�����u�����6�9���P%(X�N�w�qG�b�-��^�\s�����G{��w\�X�I����]������=j��,�����M�g$R���*�f��vr��!5SG��L��u��H�.�����[�M���P%��l}�����r������{��q���c�q�)C�<��<-Z����Loj���!5=KOOw����$-�C�.�xU�2n��3i�$�����_�:�V���|��}��g�ou��s�Y����+C���������.�����7KJJr�)(��a�(Z�b�{
�:��p�����K�0�����	&�G����%K���g���������m[����I�e�]�F{��Wl����^�����Tz��\���P:��#��npC����x����Q�����r�*�O#�i>�k[o��{�N�5����n����%�<_|������=KL���tj����K�g���;��@���>�x�*&��!����F�Q>�H��P4� Rg��P����={v��E
��iu�j�=|R����Nkt2Q��Z6muD��k���.0�P��f
-cH#w]}���������?_���5QAA��t�Mv������#�g��C�����)�����F6{��G���/������::t�Nid4}��G���3����1c\�077�����l�G}����k��s��>T^����|�&{w����-9|?�Ybj>�S�$��2�4���;��?���\��lu���e�(�-�����������{���*�:����<M�� �Z����p���4|��3�kj���_~��?��:w��~�z}V����zk�L#�u��e�2��Z`HM�\X�x�"=�t���z��e�<�����9J���d7�|����3�X�1uY@:���l���v�����^����~��n�w������SO=���~{6l��^?LM�
d����o���<�-��O>�2�����SYB�M�.-H\�v>=u�T���K\s�~���6�lc���3��r�����c�i����];��N�;�&Rs35�JMM�>a6e������k���Y3�s}i�{e!)���M[�`��Z�~����K�.��K(�H��R�1e���3f��5]�����m���:�VS?}�~���=������ihJ�XhJ ��Z�f��FQ�B}���h�������A�2_*:
���,�!�~��}�] :�����/�N�:m++0$����'���^z�5GS��G�nH��:�.,,�?����yyy��\`={�\?�~$5�S&�[
>u���e �����ys�]���!�&��PpH�X�,j*C����DZ`HY,+W�tc���F�%Ts3�n��!�&�>�����Pb!0 �TYj��O� R�C��Y#ai���'6P�B�)��I����������:��0�����+����-[�}U!����R�����O���'�!�[�h��j.0���52V��}m����+[�������]w�e{���k>�p�B�������a�G�a}����_~��~�m��-��PAA��;�=�����S�NQGk����x����];���O\�Z`h��U���_�Adm����F��U+������;���M�Q����C�tZ}��W�RSS���%%%YJJ���Bh����t��������Z�b�����9�/��Pff��o�����EEE�+RV�3�<������9�/�����i���]����^j�9s�Xnn�{=++��A��_?8p�����c�u�@���JK����S��%�\b3g���lL�S?��c��j �t�-�;SwP}�Y�%���=KL�'}�=��Z���K�.6q�D����\VP�BC��	&�b�gER�B�W�������4C���!�&����K�Z�>}l��a6k�,
]��I�B�Y�M��,Yb�_�������;���?���r��@�B5m��F�eo����R��������j[m������^x�[�|��	T�*�cH����7�����k��f��O�^1�����t�Iv�1�X�V�,99�>��*AC��>�$�*�|Z�����kV���o���cm��e��{w�e��Y3��@�F`(��h�<5G�P��?��{�=7n�}��G.(��z�PVV��l���6~�x�2e��o(���v���8��w�}��e@" c(��1 ��R��_|���������ZA =:u�dm����u�z����Pb!0 ��������V�Z���h��n��&�;w��[���^rA�����LBedd�>��c�o��-^����A�{��w�}��������y����2�-Zd����^#���ii���u����;�8;��C�u������^M�M�$�*��i42�J1\=
���B`@��������m�����O>�n�����k�M�6���*URb���Km���������;����jW^y��k��+��A�Y�F��O L�6%����o������B��O�^17T��'�h={��]v��RSS�W��AS��BS2�&�����z����B����u�f��r�v�a���[ZRR�{
HT��!�&��du��qM���}[�p��=��<�Lk��%A!��,��!�Vu6
�Vd%2�$�*�~���6m�4�����)f�������~4+C�"0�XH4����]kC������V�^�M���Rs����@"!0�XH4��0`�=��C��iS���������f�}���l��ev�����a��~�
/4����Pb!0 ��z��7�O�>v�A�<`;������~��g����}��G��+���G���|��!�&���sss��?�m���n����A!�t����7�}�-0���f��e]�v��w����^��~��'���},����rG��z_~~�zS��Cu���V�Z���sm�����������s_h��z��Y���m��I��SOYNN����4]��}������jS���:�=�P4h��{��6a����?m�������t����z���'�P���/���
G?e�o��������{���)@b`����p�M��O+�3~�x5j������6m������t�NP�j��1�fd%2�$��3�P=�-ch��56r�H[�r�7��5jdg�u�effzS������B��D���F�����/���w���j���7��%CM�C999��W_Yvv�7�����c:t���o
P�J,�$:�BD`(��h�|���[`H}�x�����?[���������y ���W���[����oo�_��{�bTRp��+�������S�@|�-0T�n]���������3����X�^���7�t?ee��a�=��w�q��uk�8q�=���v��w�y �B�|z��%.�s�����e���������i�F���W�H#���=������5m��h��w��������B��M���)��������#m�������u���A={���
zS����Pb!0 �T�p�+W���~��~��7�7%%�v�m7�f�ml��w���L��@� 0�XH4Uj#C����D���P����R�j)C��!�Z��@-��C����|�r���������Z�v����v���Znn��6�|;����i���������i��k����,Yb����q���W_YNN�{>�F�m
4�-Z�g�}fW]u���=��$�j`��7��_|�z��i]t���_����g�&M�v������m���������Y�l��1VTT�}a
-0���m_~��u���x�;��,99����;�9s�v�a��uk7��c�������3l��5���C�[h�����}{k����VPP`���{��>�Xzz�{����5�������
��� �@�����ic{���7�Ch���u�Z�V��d�����L����~���4Y�h�kF��;X�z���Sh�!x:w�l�����n����k��~��^��
O��Q#�>u>�����c��.������L���r�)v�����
��v��g��W�\i������M�{P5��B^���~�m����m����N8�u4-�h��ot�O_u�U��ys7H�n+iF���T�{Vb���y�S�I�z� 1�j3C����DS������[���5kl�������R��g�u�effzS������B��D�����K�w��6~�xoJ�u���F���"��#0�XH4q����W_}e�����s��q��+�s����n����b�`�R0��SO��/����o?������l��!�&������{����
4�����"RP���{\���g�u�����Pb!0 ����tAA�=���V�^=���K��D�A���g�:u�w�y�����W��C+V��O?��v�ek���75���[���m��y�n�:o*�Z`(99�RSS]�'??����%Z�x�{�>����Q�1eM�4�>��3o��������}{���5l��{a
-0���n'�t��{�y�����o������W��u�]v�W���nk'�|�������P�m����6t�P[�z�]u�U��6���ORR����`��7w#������'�PC
�v�i�)��W_m�o���J��m���!Cl��	v�QG���j$���*�f����q��gffzS���������������K��{���O��{����S0�Y�f�6�*
������KK},_�|}��W��!�R���/��SOu�����[lQ���3�p�!�/���_|a'�x�����nd����������[��}-++���%K������{o�����j����y��y�������}�S3=��>�h{��W]N��<5o}��K����2��Z`����^{�5�?�]{����o�����J{�7�6j*e;�r�-6s�LoJ�h�{��v�
7��e����w�5��	'�`S�Nu�i=?���v�����.���SS�L��O>�n��V[�v��g��a��~�����������:�f������Z`h��U���_���5�\c�l�MBG���o�=���T������;v�
<�eM�8��F�a?����y��e�L�6��v�}w����]����>r��=z����m��1��Kh
��5����o�u���w��yM�0�x��Je<@m���[9�
���An4����j��8�r>�@i���bTTd�_}�>�����W���n�{�,E99�;u����:[q�%V����K[v-Cy���Z`H�A
��n��7n�MM\��y���]��6m�xSc��7���_�z��~��Y��u�t��t�9��W\a�G����'���fb/�����0`����>�+f;����v�m�������n�<4/e');(55�M�_����}��nY�L���"�zy������|�YZ�%7����������U�h������y(�����w��W��k>��������b[��Sl���]Zz�a�����3�C��|��e�����^�}WF��Z�u��Gp��	-0T�^=�a�l��u.@��~��w������������o�(�.\h��w�&M�xSK(p��������d��E.K��C���;�iA;���u���e)�H�Y�Cz���OA���[�nnY�L���};��>��{��o�m��Dk��k��xk��{,�A�yo��}��{Oyr��`Yo��>��k>�����'��X�k/�,���w�j���`y3���������+�+�}��}�q+�/�zg_h[��`��JM�u/<�~#j��C�xQ�j���g�yS��v���^�Q�-Zx��.;;���)�����n�o�-�����k���y+x����ee)@��v������n������l�=��V�Z�i���%?��#��  {�;V��eGcu�=��,��F��,�SWK?�+��+i�U������'�V��������8�����������Xn���/��In����O��O�h�^z�����{�t�?}o�K� R����{����=�K����my3>/��Z!������K.��.��R{��'��?�L�@�:�V�+�-4h� l��/^l[m��k����o���@���_n�>� Q�
�7��&e�g��'5+�g��j��]
Bi�2e|���UR�k~��C���8���S�4�X�_1��X����n~)���M�[RFKnT�A��NP���?�|.r9JQ���7�y�dm����-�rObYv$��C
>((4~�x���y����bQ'��Gu�-]���C��,u���O�7�~Ce���<�6��������R����t��<�NR��C�.�����
���_�=�to���_P����i{����E��F�����������)S�`�<�Im��7��R[����?�G+\Vr��W��m��Kn ��PrC�CR��^B�O���]`(V�Gg��Q5b�ze��Ch5�R6��L��v�i��51+-+'�_W�z���m���+���+u��n��&���|��|
����cyOY�������{���u/i�����K�g���;��A�le9������:�){4(%%���zk����7o�A��l�����
�����W�5�.:U����,���=;��'���=��������t�d����������ys�<*
���l[������t�s�1���FKJM������������(����sA'����-�W��s�{oz�.�y����M++\��}W���6Zve��������:HU�e)�0n�8W��������PAA�=���nd�n���~� ��zq�A��~+���������n����C����g�;3����]��1^����M���*>�G?���k>��i�^����
�B�r���r��������|��{���7sl����;�-
�����5�U0��@��{_rD'[v��.(�~�
��VD���A��,P�h������X2�#;���},��/]'��W
\�2(X���t��<
���y4��f����8-;�$�}KO:�����b
o���e�o]�f�{��Z�P"��������hd����d�I�e�]�F{��Wl��6n��g����^pNO�8��<�H�2dH���~���������7�5�����
u����n4Jb���D���
�)�Ef"k>)���j�A��a��4-������e�%�Q���(��l�6)���Xz�E����kl��7�$(��/'���r]k.;�c�]�-�=��X[d�^�����I�o�UY��`��Q����:�c�
����E�=�n��^-_�����������6�.�F[��(��h�J�+�ACk��F��zH�*`��,g���g���b���:e�m��
��;�X���h����W���.�T��e���5��we^}������16Z�P$u����o�w�����������4�����]�Ew�u������6s�L�Q�;����w���h��>{�lw9���t����3z�'�E���tZ���U��Z����6����S��766}n���iI�������W��W���
<6���'��96��������<���9}~L~����%Y3o}���7��/���E��,3���]�^/�����g������"��,��S�6��4���guL�T{����w���$����o����w�����^C����\{�k��?���O?��;
_2�������l������������wU����|���p����=��|��Zi��}��}���	����B��m��r�������?��.��b���?�w�\hhxe�D{x��.X���������R��,
�h����o8$�O}<(X�����o���=4����P�:U�I�S�J�:�0�j����~��j�c�xP{M�&�r����%�z��f)�YE�2��#���1�e}1/zp%huV���mI��	���{�����{�!i�K�d�}j����>��d��vN�v���=�]�d;��4�H5�z~����k�V��_
��UE���=��n��K�L�uj�4��U���.������lu�:�
!�L���^S�J������y��w:�uB�=q��J�e�m�/�:�~�A���
e)j�5K��!�`����]pD�w
:t�C�k�2o(��ku:�N��=�q7D�FXSF�:����/K��]]G�PG�A���>p�8���}c��}�����fh�~������]�6�Y�C�z���2��E��'��e�2@i�Q(�q�$�~eM�L���-�L\����Pa��-�:i�Ap������L�o��"�iP'�:l����n���Q���d���~�n['�������v������l~V�0
/_�KD�������{����k�\,o�7�yz��%'B�>�\47j�����H<��&M��������}��g��3����_����5]��}S�L�>Y�(����NrZ�����d
�<���6|�p;����uD���z��.;i��an]�����l��A.I�
���&
����F��>8�����{�v�wi��4�-��+�5����6�K���y���M���-%���W������%0$K����||���~��M�&�<�g�������iz�|m��v�"���,^���$Y���V��U���3�-��X���9�(\��V\q�-;�4�)��g��b�����o$m��GQa��
K�
_h�!
��N��O�2�J���t���iT2}.�)�����Q�~~|j���_?�MZ���v�GX�v���.p�IYU":�S�Nv�����Y�l��������:� �����c]p��N��]B�$_w�un����C����wk���O32���/�m��l7���[&��{���V��K�D�l����E+����\��}v�;H�`�=����V?#��xA�xC��E�6�$��Yg�`���"�G4oIJK���+\��1,��g��I����`eJKs��
~�o�?��&����[(�E���M%����-�t����Q��"�;�������[ZR�z�T$��C��F�
5G���)�^��~�����,m��q<��~��YWr�5�����n]�tq�iD�K/���x�
;��S�:�1c�[�����ov�
E��4O�[�]�N}��A�����n$>
��,�:�����bw�Qg��Z�8�����UE�a��
CaRV�FWs���u���f�y�r>z��K���i[��o���:G�AfN��/l�#����|�
ZF���?[hk�|d}S/�����~�r�l�
Y�C��^���$���h�l�c^��I�l������}��qp�RG5@�	m�zx�v���a���������y���!��5k���lW�X��lO}�kc�*�g]N�kJ�~�.="�������+C��?41�e��~�����=#��~;�Z�Hv#�5���
|!�>���
uY��������m��B�L��,�������������$U��gk����x�t�!�-7�
W�p����\h�z���1R��/[��+X��er�5���t�0�����r?�������Yz��!��ip�����Tb���t�n��l������u��a�W����3��B�R �U�V6w��R�`��u�O����L�gH�
������o�t+,*��������|��~P(=-��9&�B�Ao����������Q�$����h�pK���Y^�kZV������`PH�6��v������zSK(8�h�0k8h������[��'b���t�A��/����}�Z����^���L�����������j��j����s�a���SO=���W>M��z��b��5��:�S�u�#�
W�����}�T��sl�y�����~���	Y����.7�\���F�j|�C����\&����h#~)��������O���g{S��]���C���L)����Z���,u���b�2<�}�������x�\V���/{�w6�?�Z?�=j�Pk��`��Cu �{��6a����?]33�������>��H�n]����.�"�z���������R:�nX�,��k��YVIi�#����6���d��N������"h��|����,)�F���j`h�m������u�����[����e����[���_������j\�}.�N���[NS1nR+p������y;��r��W�4��oZd"�zM3�-#-�������6�xzv�[�D�����\=��he���Y���R���B��{��m����S��={���D����u�Ps��&��wV�-������������n��
)������"�Y������VM�l��I.���//���\��A;oY��$�L���{���z���:��r^I&�?�Mv�$ �
�~��g��1c\32
����_��
���,9d����2������n�����/p�B��#u�����
l�[96��
�q����7s\P��}�
��N��R2�������|�.-��S�\�F
m������&�o�
���r��#�`�������^����#��"���}���6y�d7��g�]\�/��ggg�M7��^���+���@�a����p�Sf�]cs��Y%A�����u�OWS3���sI��<�VY�g�\��N)6��:%/���l�6����a��2�B�vHu#���:�z5�e�3�I��UYEn���"���Q��h��?�1�n3����eV��s9yE�Y�Z��n�wa�.$�P3������;��C9�>��c����^)�L��{�����g{��\&��P0���2��^�v���M��
����
I��$�u�d�i�S�
OC���:�\W��U�C}���'�-'e����Y�zI�#j}>�N���T�����Br@�T0�m����|���y��xB�����[�Co�������v�uW����eGq������+SH���z�-���;\�+��b����{Hd%2�$��2�rss��?�m���F�aG}����$''[����p��>�����k�YAA�L_��V�^m�f�rC��������4"�Ad_���Z���
�0����h8�2z-�L�z���>����-0T�n]k����R_BeY�b���;��_�@�B)�s��n��'�xb���4:�SO=e�&M�v���� |��J�����_�F';����3���v��{�l��6j�(?~�����-���{���,�0*�Dj`Hf��m�\s���4={��{�����i�M���B`@�	=0$�Tz���6v�X���/��f����y����K��������Pb!0 �TI`��%CM����Z�r���1��M��:���u�����Z��
��c|�5n��:t�`��5k�Xvv��������J[�l��	T�PC�[����O<�
[��Ak��������F&>|������/_�����R��w�u����^������W�^��fM�4�#FX�>}���_���~�{a-0TPP�F!�����:u�d��]��-��o�v���'�|�����C�V������:� k���75�V�ZY��m����vmb��P]�R���c�^�z����M�.))�RRR��PB���������_n��+\����� |��233�}��6n�8�(**�^�����y�7����� |���<L��w���.��R0`���3�rss��YYY��~�����]���{����TTZ*O�L�:�.���9s�7ec�����sU���m�������{�J,9|?�Ybj>�S�$��2�|]�t��'�]w�����2d�M�0��@=c(��Z�z���_������J,d��?�G[��3���+\���Z3%e6��};[�������zSP[��1IC�7i����FRPh��X�k|PH���v�E�I�
�K�����B������dn�2�n��fk���5l���;�<[�`�{
��L�BE99��C�I�
�K����k��M7�d���������S\�Tp��t}-[����|�I���+��?��>	@���c�*��i�3���&M�d��w�L���>���b�������[�����N�/^l��r����n:�Y"4+����v	-0���k~��m�����K/Y��}-##�~��'���O�����8��7on�_~����i�.�+��Ih��-_�����c[m�����V�^=�W��_�r�Gy�.��b����,����m��y6j�(����,�0*Y��,*������s
l]n�vw�f��N�3:���-7�B9@"	-cH�Nkhz5KJJr�V�Ze�}������n�fSP��3������59�~�~�~��Bi8�-Z���K]�2�3g�M�2���k����X�����k�Z���u�zS5�2�r�k~@(�~�~��BeffZ���m�����!u:��O����k�����i����z�uT��n��F����s��g@b
-0��c�{�����.r�@?��u���N:�$����+��3��A���z���P3$B����Y��BI�6m���_�k���
W������?�2�D���l��z���:���P5B�,V��N�:��j�
�,�,�0ZSt���B9�]6{4F���zDdj)C��!�Z��@-E`��"0PKUY`����V�\i3f��i��YNN���n�:�U+��Paa�M�4�>�`k���u���lk�����l����]y���l�2��
���������O<�&O�l
4�-Zx��-X���n}������{� l���O�nw�u����^������W�^��fM�4�#FX�>}���_���~�{a-0TPP`c��u����>����%'o�u-[��o����kg�|���"@�B�Z�����k;����m�����Z�je;v��s����k��Sh�!u:�>����g���������,%%��U!��Pzz��hz����f�X��e����Z`(33���oo���s���"��
)���g�qC�����Z`H��4L}��]��K/���9s,77������� ����
8�uN}����� |IE�������S��K.��3gzS6���{�1�Q5�H�������;����������%���>��!�r�X(��KhC�.]��������rYAA

2�&L�@P�������[���m��i���g��-�����������m��v�;PU�$0����4i��c�Y�}i������=�


�����S�:t�`��f����B�+�Q���v	5cHM�.��r�j��\B�zh������5k�O L�ei�<��]u�U��iS�����o��RRR�wl�Q�Fv�YgYff�7����H,dRDG9O,�s��%�����+��.�����F�i{����
P{p��X�`��r�X(��KhM����m����	�n���M@uZ`�n����U+��tHII�����������m��Y�TT��Jv���[���]_Cc�������51��j`(##��t�b���v�q�Y���---�����>�:�([�t��i�)�����N>�d�����)�.Beee�;����?����l�2�	uY�q��Y�f��g��C����y���	'�`g�}�5i��{�Ah����Tk����L�P3.����|��_x���?oj�,Zhk�����eK��l+�������^5�\���_L����Xn��=5�{�G�g��Beff�n������Ci��E��u�=i��9������<0���S�B����+����O>l���eY/�����[R�������$�]���kZr�-�|$e6poO�~K�[�=w���('���%�H���DM��:�,�}���?�����M����w"�T�{VBw`Y�I�z�D9O,���,ZQd����	_�Y�6�v����+���_����<���[���vj�lw��c
��%�����I�6mn���*���}�[�X���h\����=#��~;���l'��f�n�)f�-���������k�����NV���e��?�"����S�SPh��C-{�XKJO��'�nuO>��5��Q1EkV���[�������[����W�����V�g)[mc����%7h��"��1���c�-�.���}�]k���t�Av����w����#���5k�9T
=�n����:;��u�������"+��[l��f�������q_���5EV'��a�$K�RK�j^��_��������"kT�>=�\��>�e�,�;�'�x^��'��������V��~L��I�6�,������q����,u�x/VN��Z�{�,�Akx�P����
I�73,������~����TOq=���,)��%��x@4�e��X���m������u���F���dHdR$2)���'�yt,/�~�d���E.��u�$��+���)�;��eGT<cHA���m��.���#�L�>��
6���V���e�������)6�����A��M/g������C��vZ7�X��i�x]qd��O�7uc��}��|0�RCj�����X�W�W:�(h����a�n������R"g�{��_-��#��?��M�����������������s�IrR��h�dg�f/\^�������b��Q����s
�nz�]�#��>:����L�[`�-+t�qM�{�M���L��I���E��g�k��u�E��w���b�z����V��oV��\Kn��2��M���Y�Z���Y�Nm6hB�SS8I��n(Oh5�������r�!1?�~}�:i�8�:���s`�=������Y~��Q{�Z�=��u��BS���[$�����m��a�$��/r�M����H��y�&��R0�g+\��RwnkEk5�����]]���{���F��Yt~�e��r��s���'���:H�I��sl�������X�q������������J�HJ"�D��)��RQ�t%WQ9S����G"�b����?��>���������5;������<�<���~��.n���9y�s�u��{gDDDD������O����j������}���z�-"Z$�.d�8r��l��}$5���y����z������vm+TD�BvO�����:iI��K���(�W_#A���)%:����o�D������q�������"��FDDD9r8r��Y����?����Z�������9n�������1���#��Fi�]��-����"Wy���~R�����=IR#��t��O�~?h�]G�rM1?iz}��SYe?zD���xk})>k���y��D�L�*~�
k�(f��:_�Cb\��	����+aNH��|E���KH��t�;����Rh��-�d5u�f�w9BP�K�.z�Dx\�D���e����|�?g��>�������:�X����p�_�M�A_�JD��m��.����#�=� 0����Y���	�(����4�K�a`k��BC_�b	��f)��i?n�/��p��r���{%~���WQ�]���K��[J�[.���tq�3??	n�D��~V$  iT�#���clJFDDDt�a�3��f�_�/��������wCB��B$4��}��X��*-MZ/�q���9PG,K����e��6ekV���B������O ������8u�,�T����qV���5fi��i +i�26'#""��������������
?�7kY""""_f7[�_[�OFv�2�/f��V�_i���o�g�3id
!(���X
"�V�_z5
����<��y�%:��FU���������zo5)sg�,#1A�X��Pc���?��`XR�PzO46����7�n7K����/��	X}�;wN�>i��������%..�,%"""�m7W��"��s����$5+;��<
�G��C��*���!wKP��lm�A�f%��uy������{��3:��;�,1Ql�b	1KSB2d�\W�T7K/�a���r�\xc��>��=i{6�i���UDDD��k�!�����^bS�����#G�����Kd$�%"""��N��jR��@���s�D���=�=/V���kP��C<�<��)AN7���jW]��H`���_����I��?�����4�F~�����f�E�3�$f�<�*
i}������x����1��"��Q��	����R""���k���8q�����?�DDDD��j:v.����� &>��Y��M3{\�����n�]�3��`)�����6��Kp������Iuf%l�,�S�I��������#6F.�3F�G�S��BQM��!-���Q�ok�~�	�u��R�,���\	�y�fE~���%dA���~�m �*�S�������v3f��l6�a�������S%444����b���h�"�Q����_z2@DDD�K�\��?�?��M.��eb
)f���\��u���2s��pN{�s�\[2��s��$���IC��X>gDV��7FK��_I�gS�R��|�`���}�y�A9�@9}+��G�7!-�H�}��\dD^�������;�&��4G��?�>~�j9���r��=r���r�[I���N}��43������;#�����c�52K2��x@���+AAAf)Q��f�]�/L���9�d����$^��8}��L_�4Y��� �N�7%��
A��d.(t��!���k��u%4(cYFe+TXk�����������	T�������TiqDD�����}E��}Y�:��7����{���k$�N}�4mh"Vt�'R�G�.�4�����L
e��fdDDDIlFz=Cg�����k���
���oK��)D�G����G�i�����$'[�3����6�����oa=����b�YV����������f[���������_�>�2�#�
�&V�>�@B�����<+�2�jX�_�wI�Pz�!��'����A�z`�hhR6��hg�nH�)���\q?y�k��$""""���9�.����}����I�&�E�����S�"��W�fqn�s�����?��l�u��I;v��r�-�>��?x��9���l���*$w�u���3'�Q��N�}�6�m�e��U��O3t<
	����]C�������34��`M#���D��;��=}!���-�p�\���B�!K�%j���U=��FDDD���2�|^����Z�z�)9s��T�VM*T� ��������<��
�[��)mN
:�~��'��%��J�*r�������v�����m��f��};M�>]�~�i�p��4h�@e��-�j�����o����^�\9��g�:tHZ�j%����n������������s"""���kC���?��Q�Fi�H.�;vhg�k���
6H������_�����K�-&&F���A!��L��K����[e��i��~y��7.��Aj���q��M�d��u����j��m��2i�$�������`�w�3��}�v��_���Z�d�L�8Q���������|CY�|�r
�0@���n��Kz)�$�n���A#��������C��N�O&4�{��G5#h��y�j�*-4���o4������[o���"r�u�i`��k��y��=kN]���gOy��G$��YE�������`���������w10���������`�;4Cp���o��n��5r��1i���-Z�,M��
�a�����;~��f	5m�T����\�����"d��:��{����+�O��E���.�� $$DF�-�v�Jux~d�DGG���
���{������^���.]Z�B�/+	�d� T�H-s*�������%�w��Z�ji�B�T�TI������.��S/[�L;s�W�����+��YT�l�T;�.Q���)SF��N�:�eXA"4Y����C�XC������\��� F-�hp������������?��fd�t��)���5�S�T)	3K�ge��YPP�>N���5��Pf�EDDDDDDD���
W�� [��������5���J�\��OO^�Q�����:S�L�.]����N�>-]�v�l �����y�g���y4:�>|�9gJ���y��>��G�V�xqs��22OZ6o�l>��W7������(Z�?��|�����|D�X�}�9Q��: Ufx=c��ph��&M�h�*�0{dd���"����2�~��'�A���~[:w��3/"""""""�=^�B)d�6,�/d!8�L���[7Y�h�<��2u��KF���<�|0�;L�4Iz���!��������������[����)YC�_h��Y�����K�U�V��/hg����YC�������k�UW]��]YC���/����In���(��oZ>,e�X'[�3����6�����oa='"""�_��1��o���o���=0:V���I}�L�6M�C�g��~����w���#FH�=�d��2g�m>���j��UGDD��)! �fd��
�`�N�<�X{bu:����9tp�@��y�mm��UC���������|��Cv�]3���w�����{���_|QG�Z�v�6/�+������j62w���/M�6�t�1_�;w��{w��`M�54���c�{O%��M��z��>@�Z��s��-s�}�v�G?F��\FDDDDDDDy��C�F��m��q�;SvU�\9�[�����O3p�����C��>�fp�|�Ir�%+5j�����p���Tm�������oO�A�:u��4dd�CG��6m�
$[�e�X�p�%�Fx����`���������wy-0�N��

�����3d��7�{n����k�;����>����jsJ� ������y������� C����?�m�w�}�A��+T� �����7j'��&d��	HX�k?A�7�&}�~��L�>=98�mN�0A�������������|��C�L)U��:t(�, 4�B����ar3���`
�j-X�@3w��tk�����cY�f��r4�0`�6I{��W�f��r��wj�^�zi�2���B�g�yFv��!�����
j��M7��M�j���9w���&pX'��m`[�&��}�hqlFFDDDDDD���
���p�-����U�i���\n�`���iU�T��1c�H�b��5�v���n��6��������*4i��{�l��E��a���_~Y�r�ua��Hj���mc�/DDDDDDD���:\���u�t�5��O��������:d:F#;s��g�~z����YH�/�0����x{�z�[X��������`��u��o�43l�9��~�M��|	/�}/�=c=�-��DDDD�����Y�di������ojV�+�F�-K�,aP���������2�z`��1:3���			��#a���K^z�%)_��9'].�%0�
�%c���0��/�,�!��!;w����'K��=�w��r��Y����!��;f�IDDDDDDDD���C'O��@P�5������>�L�9��"U�!��5k����DDDDDDDD���BV�A�|��4n�X^y�i���95)��X�b:|���Ce����""""""""�6�����+�g���#G������g���+�SE���d��12i�$��2k�����������Z`(&&F6n���B�?��(P��������m�V��i�C����""""""""�&�����������P����R�
,(*T�'Nh��DDDDDD����cra�H9����lYON�j g�,��~D��\�������j{����]�����I�E�ia$&H���l�����������,6�Km�N�>-]�v�r������+����E4h� 
��1#9`� ����Sj�r�����Q�#��|XA�Q������6�����oa='W�{���Y_H��5�p���e��p	��@B�.���������pE�l9d��x�kg)]�&���v��&Z���|�<^�����D������GJ�j�~w����/V%��y6��`������8�����&�?����	rN�T�O��$����d[	;����C�qVl��b+RT$>^�"�O�OBZ���AC�h.���H���b$$�-8Dl�
�-F�]_�G{Kh���^T7����W�����I~~�W���yQ��5L*s��m��Z���*U�����I���{e��5:?�#"""�+A����$n��<\,���9���g��e����n�]E���$4�&��2yY��27V�;���<=VvM��)R�&�"O:d��8
y��:2�|�A��1���:���@�nA,l��obe����2�b%��5(|Gs)��b)>s��~u����K���$as����G$����H�Kh��R�����}/%�.����-�?)��}���EP�{�)
T�.E��"%Z+��^�1(��m��Z�,X�@�u�&=�����k���{���C�t��E���9s�c����Dy3)|3)<c=�-��i�{�!3����%:�k�P�EX�H�J��A�T.s�o��_}Q)�(���R�������:���X�q�!�^�/��k`W��'��%��`�s�K�.f��f�	�<�U���1������e�j������U����C�Aes�$Cg�j��O� ������vC�LY/�l2�U��t�e\�U,�O>x,D�h0f����J�7?���i�r>��'I������zat��7q?/����$�5kK�1�8������(�^y^������>!��z��T�G<'���Kp�>d��SZ��-""O��1-[����G}$u���^xA�o�.:��O>)��r������b({"""��A�_����y>(�q���	�����*\���v���`'O�J� �@K����<k�_��|��y�.��\W2����
�6���K\�����	���(�����1��<�.c��� w���<
��X7@���Y�k�S��������������&�
�n�# ������Y���W��J�J��z�m�O��{K��%~�&���)�;)P���n������,l���2l�09s���5�&c�������W�N���
�=������(�B�P\b���s�ss���R�}��%�:j�XgU(_�O�Oy��@���5���YC�F��Y8�4O�0��H�	�)�Tv:2��b�
��5$$P�T��C�v�%��t�b�K/o���s���:��
�4��H1�( �����8�4u���z�T�q��Y�YA����������%n�j��As6�2n�Tdu[DD�x50��������P�3g��_|Q����ge��ir��ay����X�b�DDDD�����M�2�i����%h��i��]�0��$*���Mz�D��C��A"���9���V�D��OL2�
�-�~`A�	���������������C�����f6�_�0�w�W���
��"�������RW�o�C�	���h���u��7%a��x��r��Z���{�,��n������w%�0v���u�1wY�Qj��������tcT�L�kA����������Q�l���%"""��|��Xj�������S0���9�w���n!k(=q�vy9q������fjp��!������R�����%eB�vC�|��FGk�v��g�rL���0F~����l�_h�a��2'V�:����~�>�������}YB�v������G�����[I���T�6)���:B������Sb��r~����1����`=O=*q+����l��(5^]�pA���+y�����������@�>/w�&�h�����h���I��f�]j^�/#��&`�tB�):��M�r!&)
�`��SI����0�
Wc;��F�������C:�
�Gn�&q�d��_I��Y�����_��I��!��%��s�$�3������dxB�#���h��KH��t8������xR�7Kf�ED���������:}�"E�R"""""�/��6d�Q�q���@�����:4h���*��M���,K��"�~:TGK���mN���yn���eX:�p^� �����r�d*��b?vT��|^��!��<�K�y����c�����(	m3�p��D�s�$��3:b��%]���C�`�A:|���$a�F-��l��(5^ah�j��i�B�`��������'2jn�6Q��$H.��}?���;X�*zcQ�lp��y��}
�7BuX|k4��6�>X*���Q�����o���v��/T�
�l�s��|�K��&x/8�h���|��R����<HS
����vzX61�g$e��+VB�n�k�w�_�"X�}���/���l�����C���2x�`�����oM��IDDDDDD�m�����a�����p�h�������@m.6���>#�
�����������4�����(#�%�+QR�S��j�e+&�%K���1����O}�m�o��C��Hb�#���x	����<�:�FG�z��m��k���g�jG���������.��#t6����M9}���������������t"��&a6��h��� `����fb7U�s�����U����1j����	IZ��9.m�v	���a��W������t���:����G�A��~�
�Wo�4��v
��Wx�7;��cP T�
��`{�8��T*��""J��C�A���#�~�z�������r����.g��<�%:�WH��H��#��@���7�-��(=�U�<�^�%M�,��I�"��T������:9ZG!KMT��y�#k�^S�-��wi�=)8�'~��:&1Q�V�tF��!�S:)�(��-�����[5��G���BT��n'f�<1�������������Q���-"���X`�n���-[4����/����8�7�����������y����<O�)&���l�g�D��J�I���C��)�4�5X��Vgyl�H��6	7��t*T�&'����K�x���?)�su����1���;g���0
�':��(3��U���o���9�]��G9w6��5��G����f���P&�9���s�,qrNiq��%��9�\6�Ic}�WK���4����6�����W�����u�$�+�<f�+Q�-�{v���T���gg[DD��X`������K/�k��&���)S�L��3g�_!""""�\��5~�AG��/�OnR�������f�]�D���w����������.e0�e����2oS�k�B���(�7'�|
��k�A�f5�u;��J�E[��CP���	hB���I��XB�4�7s]Br�\�	
��5�.^�`�������LY
��y�n9��9����{i�8.\��:I�M��K�6;���h���+B�UP����11�ra�H9���s}��X_H��r���I��U��'�-(H���HN��4i?�������yI��]�=��""��f U'�o��]�J�����������(P���(�h�z���7a�XW'[�3����6�����oa=O��oa]��
��A!u����4�B���2�.��4��C|�����_�>�4�	W,�����B�>8������kX�_^j�Y?�;uE�|�!i9d:��%�t�T/P;���#"��Q��4	���s�wg���5P����wL�?�*q�V�yA���_S^B�)!�['�W3��W��c%v��R����Pws����	;�I���$����������$�������s���D}������%�����OrH)dc[DD�r,0t��9�������G��})]���FV�|y=z���\��H����U�pa�'����-�����������u�������i�|�"^��'bJ����5�b�.��"�����{��y�y�9%	�46������e��A�k�����j]��U�r��e�q�$:D�G�J�I�����u)���A�H������I� U�*�yG���������\�X`�Af��!C���k�����1�����E�o�E�g�����<u����u���(I���`��=z�����A�c4""""""""��r��V�b�d��Q�n�:9u��f���G�BFQz7�JFDDDDDDDt��h`��	*W���,YR����������(w�Z`�h����Ge��i""""""""���r�p8d���2i�$����61����S���#����������r���cra�H9���.p�U9�Xg�]�#.����.��Mr��&���U+����	r��gu�������0��aH�������������(�t���,5��A���y���K���O3��G�/�6��#�x�z�[X�S���[X���������x�r�.1�LI�t!��_/P��(��L�X�w��Y'q�"C���o
0�\m��e��~�].��ECm��f�<�0P�8���;�������"�}����<�8PW���J��]�"���b[��"���8�|�~����
*��@s���z�
 �{v���F������qZb�N.Lx���X�����B/~!p5�]�Y���c[�P1b������^�������7����C����8P�B�j��q����e�����%K�hG�(�<y�0@��=k.IDDDDDD��6�����ce��$$�`B�lr��!����+sc%�Y�U�.���
����vy��Y�=Qb���Blr6��9��Oc�����<��1����yqr�9-0���mz��Q����I4F\�D�����h.��],�g.����N��p�[��$l�b�����Y_$�2�~��D}6U>��[���������9��{)1w��v���`�	���c�:����4c�Y�`�:T��Y#�=���h�B�6m*w�y�m���}���s��5�&""""""�Y���|�*^.�r������P���PY08T��,�l��^�������2��/GN����2d��x�w�����'O�kK�i�j���A�y�d��D�G���C
��������NR�����r:��B���j��_�RR����V0,i��&A�Ip�{�HH���?&�|&%l�,�����������iC&P�G����i	���,M	��b����!�]zHP��/f��I���$��[�����5+��)������-[�H�f����nNI	���
�������uh{""""""������?���'[%7�B���
����6�Z�WbV�$�zw��p.["�&%yn����]��qH)���w�h2V���t����m�q�f��!��}jk��Y�b�1��X�@�Z�O�Bl��$��K��H	�\M�K�5KM�\G$ @�����2M�"?����(~E2�52�������k36O��h���OY7�6K/��H����c���zOY���P||��8qB�V��n�A���5jhG�111f)Q����]bD����r�S^#�rsE
��{�H�d���z6	�B!�Cv�Hx�\]��yo�(��a�"Qq���Ad8��2$$P�vy�D7h
W�)���w�,:���	���#sl
���9��8�4��M�B��_�n����6�	�_�����H��~�0��{�J�O���*�����f@(���zOY���PPP��*UJN�>�A��$&&j���rDDDDDDD9��sI���E��/����$8��"(������5� 
*{X��������C�`�������*S�&E&M/]�O��Q@>�*7z��a�C/���������A�V��f!C'��&dU�K�C�/6�J�k��=���
���t������)���)I��Yl!$��:f)e��Ch"�a�/^,�-r~PR�Pm��Q;�n��I�M�����������������yWp�HL��YCe5!CR�;��?�8�%�����d��x	u�_��A�� �	��2Elti���0d�Q�;����^���,���W��>N�k��;�2K��J_N�?�%���x�
��*���c�����3�<#o���?~<E�(**J�O����w��Yz�!s
Q����������PFm�o��oDI�����w�5�YKc�z�R�:r�K��V�����T)�'\2�r�2.M����,�C�C��K�cj4�}�	�M�(s�B�:��c���Q��e���Ol6������[�n���������[��[�6mt]DDDDDDD�	�2�M�n��� �0�<nV���,������|�{����(%�mr��;���7!KG���r��Qb+*�����W�S(;��1DDDDDDD�����e6(���f��>��*���UEl�����~���SZlN��K�%(�&����E�w����%��M"�{S��Fk���)��Vk0��O?��M��s�:�.""""""��";M�R������}�~��C��f�>�L3����}OM��J��{k/w��1b�tXx����R�b��M����x���lY/�v��f���V���+C�,b`o�=,�3>1��9��Q��0/`Y�eq�Vh�+�����h	��7keN���c����H�={�:tH��+/��P,,)x��<A?=q	��>_U4�@��
	��,.Qd��8�'���;Z��A��~H������~����m��4:�>���A����$>��>-���[<���_���c/It��G�1bb��Pa���{����_����J��z�DZV��s!?�}1�y]nf���:P���B�D��"��������+CM�(�r,0'��T�XQj���}m��!����������.����
g�I���0�K4���sD��`�2��Kn�6	0���.�r��9��':�~mA\�����:��s�!~���Q�����N`�q���55(q�.�;j��C�7���Q@������:>$�g~��V����������o��9^���F�L������,�u�,�^C-��������R�{o	���A!/����{��9S.\(������'K�����������Y�.���������r�y����:���'Sf��Ru�>����������i ����t��O�P"�r��I���p��B�l:4���z���iC��	�I��)���s�����S��t�(}����Q�u�u8w���9�]q�?�4A���H���R@B��NpI��Y"���C�_)�Wd
!(��q	}�'�B^�c�!(Z���m�V�������~����o�.]�t� �m��&����;vH��T6"""""""o�q��}�E�����&e
-��(k�����5�S� ������q������?"4C{Y|���;������i���Iy3������iP����������2e%~�j9���r��=r�Sk��q\� �uJ1�<��]xc��|��1�DM� 	nq>	��E��t�v�}���-f�,s)��
�


�����G����5P4c�
����R�fM�\�����C-Z$g��5�$""""""�y����*X�����#vy��h���hi7!Z�~��<��
)��?a��?���m�2���w��`�����f���>tt��0F�pH��~2��`	t�O��������y�s�w=���s����k��[S$���E�C�q��8.���%��R���Rd��
�����H@�jf��gX�$�f<9N������L����f\��]QQQ�q�F�;w�|��w�a5 �������#"_�N�|��a�GI0��/+�l���\����������u��
�9���-���)]�&���v���K
E|����ce�^�trN�y�9%u�dz��X��z���r�MM��'+��p���[����y��@
b�:+V6���1����s}�R��"�!W����#��?��!c������3x�[x���oa=O��oa]'""J���d��=*k��1�K����4g�)Q�����W>��C���������.�������n��F���/%&&���4�=:�����4k�L��_oN!"""""""�������k����C��u�]��tN��wo�������K������={��DDDDDDDD�m^�c�n��������>����K����))a�3g��'�|R�{�9y������{Dy���-���3�s��z�:�u���N���K��/$a�qDG��y�-,\��4����_����(o�Z�����e��m��eK�[��Yz)���m+�[���[��rDDDDDD���P��^�ri�
�yA���QFx-c������kW�X������R�@s������A�����2c�v@M>��.������oa=O��oa]O�����>A6��Kt��8���E�T
�.
�r�K?���H�E�M��B/�f�GD�:�e��R�J��C�$**����X9q������������CPh���rGb�
Ad��s�s�ss��c�*~�!���Z`(,,Ln��&����d��E�p\z $,�����l�2�Z��.GDDDDD�2���~@����;_h>�4+#"����w����+������~Z(6l�&f�m�����4:��Q��<����x������
A�1_�i_�����\y-0U�T�����UW]%�����J�(�|�U������:�a~"""""�2|��Xj�����.����q��f��4i�4i��,M��Z�j��GDDDDDDDD���C�Q����/+W��>���2d��,Y�����������.��""""""""������9��a��u���7�����86�%"""""""���:z��<���r�����1cd��%�7���Y3����:t�\���������.��N�<�C���=[���+�'O���9���Q>o�<4h��ODDDDDDDD��WC�}��}���+��/���M�&�\�1�������������Z`(&&F6n�(����g�}V����))��1���k%66��BDDDDDDDD����Ptt�<xPn��&)S��Y��c�}��ITT�YJDDDDDDDD�������������(w�Z`�`��R�R%��e�?~�,��1��rDDDDDDDD�}^���H�-���~�a���9cNI	����n��]������������M��5k&<��|��G:
����e����r�J���(�t���cGsI""""""""�6���-*'N��{L���Oy����U�V0�=�G9�c>�ODDDDDDDD���;���c�Bh��q��}{
���������������r��C���'7�p�<��s2o�<Y�x����c:]^���S6��|�-g�������%�W�|y?~<�"����(��oZ>���(�����G����
�#r�z�[X�S���[X�=c=�-����{�!3�'���v����K�+&,X�N��� P*�������o����$lX#��h�4o���KP��Pw	�\�,����������k���L,�Z�n-3f������%DyO�|O�<c=�-���c]�-�������s��E��%�������L�rIpA�������3K|�-8X��;����cM���3s�L9u�T�oX��BDDDDDD�-�����9���C�����	���/�C�@Ad�d����5y����M��G��������|D��(�y�O��DzH��}
����`>�����P\\��_�^V�\����z����������r,0)#G��f��e����""""""""���������s����+\��<��#f��m���pd�X�}�y�X�}��g������3�s��z�}9"�K�K���K�3�s��z�:�u������oa=�������g;�&"""""""��rE`�����?Y�x1�""""""""�L�B+�+V�w�!6�-�[xx��)SF�~�m�JFDDDDDDDt�x50�y�fy���d��UfI�4h����`������������k�!��.s���C���#���3-}���N�:���}���R�Bi���<��s����������2�Z`}���G�6m*O<��-ZT
( ��U����[�B��;v�L�6M��]k.MDDDDDDDD����Pbb�\�pAAYn�����?���f�H��-������~�I3����'Oj`��[n����W�.C����sdNV��p8������;K����P�Br�]w��9s���	����
l����DDDDDDD������'�K��*U���#G���js2d��Q������������,.�0a��o�^��[��gTV��@���k�Nf��-U�V��n�I��Y�M�F�%QQQ��m��Ez�!]7`[�&��,.�DDDDDDD���


��+jfPll�Y�T��0j�+d!�(/����q����E�d����s�NY�t�l��U�����_�x��g�du}����>�j��!�6m����������4i�$�������`���C��}�v��_���Z�d�L�8Q���������|��CVB��-�`�;A��%��k��m��IDD��?~\�>  @�r;4�CS�:����FU{��G�����y��ehD6����L��o����q����jN�������_�k��F�9{��9EtX���d����<x�t��]��DDDDDDDD���M�����d���� �@��m����	�P��E��}���~�~����dh�u��1i��u�>�������%>>^�%+�C@
YB������\��4j�H���qX��?<�����E���.�����;��#u����CHH�vr��k��>q^|�EY�x���__H�����������W#��	�R�]��6�B��du}� ��"E�h�+4�+_����k�.-CC�w��Z�jI�r���]�J�����9���z���d�0��G��� �1c����O�G���� ���	�N*[��6���D�R�L��9u��Y�YV��e$J�	����1t��y���2Vs5w��P�,::�,%"""""""_��� h����-dAA!�������
k(�R�J�H������B���� }�d'!k����l��������������u3y�d}��7o�P�;DDDDDDDD��l����9Y-f�����3g���U�PA��yy���.�9�:}�����f]_�����.#�X��>4�C��z~�����)���~�|�Am��i��7��(#���N�:�#"""""""�08UVx5cYBC�����}����k����c5P��M
���O��+���}����4�K�C����^7����Sc�A�&dhz��dd[~~�-���������.7dy��i���dt�����o�Y����mk������7Ks����_�~��~���4�]�v�k�6Z�nm�:u�,�,��[�n��n�z�2�������Q�t���O����9��w�Z���;wj����W�2}��1����R"""""""�5^KA����Vc��I�����K�,���s;���!�a���z������u�V�Q��f��%���(fvC�GDDh�+4���g��W�zu-C��U�V�a��9�e��o���h����eDDDDDDD��y-0��M<`������������u���}���Y^��Q#
�,^�X�w�Q�V�\��o�������� ���i��;p���1�{`��,�.��9��_�v�����������|��C���-�B`##0��Nn�L����_���'���nr@���?�\&M�$��w_r&=YY:��������/7nD�@���~6l�f a��W���qc����|���2}�����9a����/t[�'"""""""�]^���8q������h���9�R��m��zHz��%�
��fN���L������*U�h�F�B�>�@n��6s�$k����N���/�++�C�m��)B�1d��-[����&d���['}����ih2V�\9���C��U�V��{�����������w��t2�(w*U��A
���	��Q�<���?t�z4�B#,,����!�����+�:h�U�D	y��G5 �)���O>�D�B&Ohh�9%k�����d!;M����_m���z����� ��-�p�����
8P^}�U�NDDDDDDD��kC�'���������be�X���'Oj`��R�P!}�
����kS(""""""""�>��N�>-]�v�����S�+""""""""�>����;wN���E�N������������k�!""""""""����CDDDDDDD�O10DDDDDDDD�O10DDDDDDDD�O10D�G<xP,+V��&��W�W_}U���_s��=z�.����)..N���+��y�YBt���Dy���M�6:�ej�=���[�W�^������r�M7�������{��'111f	�'��l��M��>���(s������;e��!Z��Fi�����������x,�c�7a��-[&�}��YB�
�u�u��+8�f��+�����t}�\�4'O��w�}W��=k�����m���Q#������u�R�q��q�O�k_�#��3GZ�l��*TH:t���{��(�.�9
�����!�<��_~����~[BBB�U�VZ>|�p�����K)��0a�t��Ubcc���p�3e��4i�Y�N�f��)�7��?�X�+�'S�[�t��Q/�s��{�J�N�d���f	�'��_��4o�<E��t�~��������;��������O���j��2{�l]�{jA�+�v��w����O���:�����n�Z�����_~Y�;f��.���S~���l��Ly���>�l��8�y�����[�y����������zpG��6l���Y�|�xF���<]�18QBV��3gd��i�~Q����e���z��~���/�����O?�t�'����6L���:Y�z�f�-]�T���/=�����d�����D�n��3������2j�(	��}�:�d��:u��jlq]f����c�=��]�V/���/������?�\"w`=��p�������o��&�����+8o��KZ��x�
Y�f�Y�� �z�? @�����q:-�rC���p��q���MzG��l�]��5K&O�,m���-[��9nXK���9�	��ob`�(�A�� �k�#�<"���Z������}��'?��S�R����\��E�2v�X)U��^�
�}��^` ���Q#M������5�z��}���i9�������y����Y�u��'����+W��#G�@��A&��_�)��[�nr�����4�����<�#����8?��
�[�y��?�h�;|V,X �~���Ot%!���/�]w��?���$5���z��c�s�=��h�8�����\`�������+[��f��/�b����y�b�=yCDy��'��D��A!.2���j�9z��Y�q8a{��������M��C��bi����6�h�q�F}�/<@�|Y���F{}n����^�l�=6������
}�x���9���/b�v��_��������>���}��r�m���-ZT��������OP�>��i����'�{��y��_�=�M�a����D���.#+$44������7<F�k�+O�x�3���l��{�d8>5i�D�+W8��v��)����2,��g�j�43�}�P���q|'��$3�?��"Pg��,��,:w���J���eo`�QO�Y�����^[���~����c=�	��~����e�,>c�6��+
��U�Vi=G���z�>yp��E�'������?teWF�i���>������f����q��O��/dq�cO�C��g���{�s��u��W^�q���{�����z��z)�'������5k����D&��n�q�c8�N��2W�8�s���O��so�3{N��|.�<8Q�z�j#<<�����a�&q~��|��Ebl���,M��Q�p�d<�����"�(V����Y3���o�rl�yRd8�:bb���/�4�	���C9�;O�����<���Q��b1Z�je8�d��
c���F���u��b�h����gm��IFBB�9�a8/��~���tk��E��w�m8�H��)7��a���C����v�����uk�y2�e�f����1�VN���[��u����S�[��]�v��B�\�0�F�*U������5K/B�5l��p^'N47n�u����a����������n�`^��c�q�r�������g��33m����,�n�O�_������,M�US��������v�y��\��z����cSk�L�����~����>4h�~FP����^��[2{��c��5��uZ�'X^����[�\�~fi��x��@�c�?���u�1�2#��4�n�i��Z��}�.::����O�y�]����n���\�^��o����:km����y�9M������_7~��G#66V������oz�,Y���>a�`�/����Z�
u�2����9MN�3{N��|���Q�z1b����K\P<��3Z����/��X_t�a����n�.��6n����l�2=x�~�Y�=z����������/WH�H�����/���7���-^�X���~��}�������>F���[oy����-��N���.X�@���������kNP���L�bm��$uu�Y����N2dHr���8��{}�~>\�`m��3�ub����z����gz��a������\�|yr=�=�G9^����k9�N8��q�n��Z_\/V���P�Q7\�YZ��n���BZ���k��p�=A�Ml���[���_<e�Xom2X��u,0`@����q
������o�-Zh��8�!��ua���a���n9���1��t�o�Asq�|7�;����km�����{�s���9M����>8Vay�\Y?����9>��X���i��5<��9MN�������8�{�)Q��b�[�3"��X�b�r�a]�2�2L/P���D��o"t�Z�dI���M4��!�������_:�.��:hw�
i�h��h"�4B�*�d?�������4Rl?,,L�.��_���4h��?<F:8�����7���p���=����"��i�-��H�G*��O�<��&V��,�3�u���:��3��eK��hJ`}.��Qw�l���_��yB$_}��n�y����<Fs|6�9��*8O����*U�H�Z�����61�^���O���8�c���R�8O����J~/��c/p�Lk_.��3W8~�3&��%�i�Yh2ak1�w���h����Y��h��c��D"-Y9�GDDh?Kh���8��:��M!����R�c3���B��xq��1���������h��>�P�rBF�i��������l`���Z�����Y��X?��`{��	�1��5��Q��;xN���������O?��	�\����9��5l�P��zY�\9=&��;�2zN��������8�{00D��8����/�D���o�>Z�U����qR��8��2'0�����q��z��v��A�q��9N�\Y'Q8L�$
���hj�=0�\�m��}X#�X'N3f��$��5n��F}�h���)o��N��W�^Z�m��u�����K��F'�8)��I�r���;����	u'<���D	u
'N�$
'q�OF+A�F��e��1K/�g�����`�kb}�q���k��k��I�>�p��6���������?e]\���8����Kz���+W�l�w�u��X���8V#����S��Fz����z��0�XF.Lq���&�Gg��teX����A��������p����q����.
{����ndEf�ip��s�g�>�P�V�^��r��s��)���~�v<z����[Sl��4����`$U�����u����)S�}�<8A�A��q���x��9����<d��������!�<C���	�����7.�qB�qr�����q`�("�0Nvp��9N����������c���H4h��:�~C��<�<��	�t�����/+�������[����e��~�v�JC]�I~���E��"E���]������{��>��8a�g����_�8E�*-8�BP���K�����g�<����"�;~Q:t��\h��A\/*(������q��I4�c�U�o�"H
�k��g��z��N�Q<]|��z�0B�N�J�Rk�
�AQw1j��Ez���c=.�q�����\��#���|v�C	����
*hpA>�^ ���3���?����	��CG���r�.]Z�sBf�ip��9
.V]��;�yFF2�B��=��z������)��s��@?~�E�����+d?���>�H�V���sx��>}�hP����li��9MN��zN��|���Qr��Y
� ��b��0q1�fd�e���|�d�) C	����� ��b���xN��h&8)r�a}��'v��L�<Y���|�E�&4�B����3����!�IDAT����0���
��P7p��_�2���?��WP�1+�"d��� N��[��~�����?n���4p������y�%��C��U�������X�m������H�wg]`��	����>+��=��X�c�k�����wAz�z��/� `f}���.���8�Rx�|�Y�986{�x/���AdeR|����E��vN�L=���
������b|���Y?J��������z�r�����+����O���sr4�r?/�����m�����}��g��%��49q���9
���CDy���e
Ca��|��n�,i��UF��6-�q=������s��
�p���~\���0�K���)����6��r}���_7��H�n��"_��2��#���.>p"���'BV�)����:���pf�����
�
'U(Cp&=�d�R�����
'�<O4o@S2l�'�U���	�����t���
�-��E*2��)���t���oDf�w�G������/� (~���:���!��e�X�r\#p��dZ����\T�Y�k�]�������Y7h�s\"���5�f5�B���|#3��g���t�f�~�B�u�u��1�J��$��UOu��m��%��6�i|�����w6����z�z�ARH�+���
>'h:�Q�=����|v�ix��"�Q�HHH0�K�k�zWH�F0
m������0���%�_�/�2i�H�F���Bf�����S���/2|����M��
��^�9w8�����.N<A���a��#���=��!4@]C�$
�<[}F�'bV��u��X.���/��_&a���_#��P��&��yj���G�<N��k��~EF�(R��Cj�������t��f�%��$H�G�'�h�r�J=�g����c= c�:�F_D�X�����+���D��������Q��������@���X��`hf�ipQn5'C�:�E32����&n��\{��)��e�i�6�/d� ��=d� ��L�����d�8�S�4<�_9�!8Y��~�����>pr����B��{g�8�#�x����W�F2���h�(�~f~	��5.~0B���Wq�R�^=�������+~QF*�p�w�5�����A��G�����������(!��N��>ds�sTW�u�0#���_z3�I���0�Y����5�>'V�3�|7o�\�#po.�'O���c~
Ej���8��D'�81�qpR�>��,����0d5d�����@"��#�����c;��9���cF���r��I�&�r��>w���7��+
�S�x�c!:Vv���g�}�������Gkf����o����9�G,VS����9
��8��~�/���c�kfQZ�9���B��y�0F9���q���9��@������l/d������������
(�>��SV�i�{������8��8ZD����9�p(�����Q�n�k9��?�1}���Z��Q�F��Eo#F�0�'BZ��}��7F�
t��w��rws���e6lh8����9%%k;���Y������7�H�>8���n���rC�1�'PZ>m�4-k���q��!-��a8�\u��_N���)�W����p����q�y�l�^����?���U�V�_��ep��	�w��:m��aFBB������A=t��'����c��o�����bV�]�9s�p^��6�J�*�����+k;-[����DGG����mt��%E���w�yG?���y���A9>O���yi���W��e��BW���q�����|���z��m���Z���������Y7}�t���sb�C�������pl�1�yA���c�'�v�}�Z��r�_�lY�����N��?��S���7n4K�JZ�x��_�K��������x�w�n�;wN���z��q���9�A��1��9���c�;|7�;�V�Z���;��$�o,�>�V]F������o��g���
V��1��p������Kq|K���gV}u�l����y��<�Ew�=���q>+�4<���1��Qxo��f=��=�G9�g���/��^z)�d����?NL\��<�A!���IX_"��3�5k�����_~1�U���=l�q����_��F8){��Gun�s}
�|�����{��\�92���:������KZ��a�Gy���hA�%5&LH���N�N�<i�i�F�A=E�����>������3�z�2|6�q5o�����g�u�q����+��q��=v?f����ni}V\Y�������u,F}G9�
��������S�N:/����9%%\c]V]7n\�Hf����#��i��u���u�'M����;�.�rX��{=�� �����PV�i,X�����[oy���O�>:�s����;v�4����u,�t�:����SxN�7�r
��wC�A�`Y|60�:.�~�g�s��s��8�g��������!�<'�k��5���^�z[�l�����E�������`��0�;h��t���/����rjp��yk������3fL���F�~����o���=����=�U-��Nj����g`������-Zh���/u`m'j���Y�G��R����� E�45���:�n���,81���������|&<x����{F?3tey:n��=zt�,��i��j�y��e60�{#�5��n={���w��)St�n���3KS��;��>G���^�d�X�������s���{�����#���������N~�p�����SKvCY=�9z����iS���s�dY����,YbNIz
�����g�s���_�b���+�ir��C���n��!�g��&M��<�W8_�<y�e?����|f�ix��l��|�����y�����)*�W��N���
�����z�!=*��Q�5S�N���1J��"�,%���a���{��HQD�������OQ�>|X;��luD���`�����g��R������F�%w�J�k��.Fcj��%�B���8��10DD�����C+c���?�\v��!;v�
�s�.��~��M��7��HJ�j�2� �
��
��a�Aq�w��(�����{����5k&M�6�2"_��<Y"�L�0����W^yE�u���.����:-������z�����'�{���[o�U�����4o�\���>��l�Dy��q��Nc�}���~�UW�S�|��da`��2�t��*V��2DF���k�7p�����pi�������������(Q"��w��]&L���"_R�zu=�A�����+�;w�E1�<����O�S�""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��""""""""��"""�+b��b��d���f����-7�t��i�FN�>m��~qqq��W_������$k����9�U�������3�<#O>�����h��:�
�C�-[&�}��Yr�X��������(����(�""""��	&H��]%66�,��m��y�d�����(P�,�<f��-w�y�\�p�,�|����w����k`�9���(c""""����D�QJ�5������%y_x.��m<(�����o�^j��i�^>����K��U�g�����o�����R""��a`������,����+g���|P����)����w�}"_|��TQ�����u�t�R����T�XQ/V*$w�u������O��s�2D�W����=�G�Hj>,�����r�-���W�^�u�V]'n��_��u�d�������i���������'��/����~��:��OZ}���������Z��3G_KwX��u��<8�un��e����q}=�����+�>��:�/(��u��������'e�����'�����7�hS���+���:~��<����-��:t���9��<w��<��:��'�N����A������k��#��4�������}��'�[����x����E=F}��|����U+mN��z���!"""�69���)�7��T\����O�������rp������__��	

������[e��Y�\d����:�r!��b����,�7��3g��Z�y��7�a��2q�D)Z��.� �/��"O?��<�����������R�Z5����aaa�j���#;v��e��
R�N
� ��S'���[�6�-^�X����\w�u������r#G��pp��.���l�����n�Z>���4�4~~~R�\9]�����B�
:���A'q^x������.�����xY�b����o��E�o�L��3u�T��hn6�|i�������
.���������t���:r��W�kp��7�4���)RD���>_�b�����b����Z���m�6s���)��!x�:��{GDD���K����([�M���P�G���'�R�p8��U������7�����>}�.3j�(��0v��e��]�h���q��)��0���|�j���v�]�q��7�*T������8`4j�H������j9�i���:?�������e�������={���c��Y�f���f�2K��9��u�������s�����ky�~�R�^��3�u������oDGG�S.n�#F���Z��|��-kl��I���e�^a,�u������o���_��z�p���+W&���%��d���F�V�t���8���^�3�;��}��C���}�����a���7�|-���6m�'O�4K�$&&���ePW,��6Yy�����kg���?f���������$�������
�:u2"""�R""��1c�����Y@���}����%K�S��>�����S`da8/��)��#��#c�m�����G&��/�(��&EV��&���,�G}49����Y3m*V�D	�9/�����-[4���'���Y��b]����t��q�Fm����#F�x���)#c����C��?����r�@a�,����4i�D~�a9v�����W����V�REj����k]�d


���W�3������+�hv����?}�,x�xO�=������Gd[���Hv��v��!�\s��*U�,�T����uE�`{u���Q�F������y����O4�ru��	�B�Z�5���e��FDDh�2����*-,�u [M���?oN��Y�*U�������(#"""�lA�b��q��J�z������%;��9rD6o����0q��l4�A��M�6i_2hv�����r��wK@@�9w,3h� mj��N!���v�<���������0Y�j���_��h�t���j�Awh���+��x���fXx=0?�X}��'��a��z����u��j�����������@������h3*������T�  ����P7*T��f�k���!3�q������Es;����� ^3��KKV�/:��2V-\�0E����Q>���E�'[h��V�[DDD�"""������g5��+�3j���U������= bA�.�h>p��v�%\$##(+�A����
d~<��S;��d� 7�p��{���s��������Ij���/tl�>��������,�y���������'O�,��������� P���A9��o�����k�'�>�\Y�iPI
:��4Z)x���g���,!@�}9a�\LKV�/2�����d�Y��>�@��P/S�}����yDDD�Q��bY"h���,���M�����4/fs�=a�&d] ��U����c�=�x�PG��_~)			������|r�X�I��A��={������z��H��U5�������2���k���YM�,� ��{dM-Y�����e����SP(-��B���%hn�"dQY���%��K���o����<:�F�
��!sn����^��!��QF10DDDD��T������7�|S/|��������{�s{2\�Y�>s�&UY�����E9��!���< �������!��A���]��w������I C* 1}�ts���u6l����O����)!��"d&���MZ��6cKKz�5���^s�eVs2���b��JOv�/�~���~
��&������dA&uo�������!"""������FC�#s}�`��k��V�+e'�M���0�����h��,\����� 0��e�,]�T�w������u�)�5e����4��:PF&Gv�w,@G��&D� H`u ����l�����:IF�}�:�F��������?��Xr�l����wg��XNB=����9�2���"�� 1�[+W���v����s�-��/�O������I%�Bx��T�|A$""��``�����MlL@?.�����V�(�*R�,
�h�����@�t���2@��� ���!3����|
7���2;�/�$�}�`(�1�X'Fi��a���k\��B�����wN@�6��7���_�\�p{���<^x�}��SZA�B�
�sO�=i���$�Ez��
0ZZ,�?O#�!����N�:�|��-B'���~�z�'���,=Y}��&M��du�:�V_R\b{��QF00DDDD�be�l��M/����#S��g����(4�����^���E�%�Y�{4�B�/dHt��-yt*\l�y.�?�����Ag�3f���}qC@����_4B ��BM���/���,�,#��cT1��w�}����<y��"�15-x�049����,�D�6m�|���)�+xm2�u������n��:|>�������F[C��w�y'��[0�}D�Bg�3m4YC����^O��u#��1B3�\����Lt6����j������j2Y}��T����d��������2C�G]������z���
����eCDDD�e			���cq����o��p^@�=�o���1t�P}�����K�����l��Qf�a�����]����uk���Sfi�6��Fxxx�m8/�����1s�L��p�K$���_�����~a;k���`�3f$o���A���>�([����_?#&&F���`?�5k���������9����u���4n�8y�<��q��qs�$X�Y�vg�Cj����7��P��.���-[�~`P��[7���������Y�?������m������'�v�{�����4z��aT�R���u�Yz����6�����Z[�)����_v��\"����S�N:/����9%%��X��Z�7�HLL�i�}������a�tnV]w�g|��pw��A�a�����w""��``�����-66����o�-Z$_��i�F-�x�E;.�;t�`�9sF��.�3n�l�b0 �B�/���q��s�K:t�=zt�ep��rw�X��h��}��{�&M�B�s��Q�i���	�G��hy��40�Z`���i��u��p�v������������c�M�`�����t���[�g���O����1f��m7=x
=m\�u�w����g��Z������L���4h�g��� 
����j���7�f�����4:w��"x�������,�y'L�`����?�/""""�<��������y��0��5u�Tm���>>�BSIt�N�g��-5k�4���}Q���t:w���a����Q�0T<��B?Z�F�CP�S�N�<QF10DDDDDy:y�����^��12Wv`y������0�����s+4@+((H�v��f'�DDD�""""�<
YC���������^����t���z�����c�:�r���w��yO=��T�Z�,%""������(�k���4i�D>��#���1K3�D�:||xx�t��]&L�����V���Bs��~8W���(wb��DDDDDDDD�3����������)����������)����������)����������)����������)����������)����������)����������)����������)����������)����������)����������)����������)����������%������P\��IEND�B`�
v6-0002-Introduce-a-bump-memory-allocator.patchtext/plain; charset=US-ASCII; name=v6-0002-Introduce-a-bump-memory-allocator.patchDownload
From a5d07da6bf878ba49b96d9fbb48fb8e3d79b3d12 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 21:49:57 +1300
Subject: [PATCH v6 2/2] Introduce a bump memory allocator

---
 src/backend/nodes/gen_node_support.pl |   2 +-
 src/backend/utils/mmgr/Makefile       |   1 +
 src/backend/utils/mmgr/bump.c         | 818 ++++++++++++++++++++++++++
 src/backend/utils/mmgr/mcxt.c         |  15 +-
 src/backend/utils/mmgr/meson.build    |   1 +
 src/include/nodes/memnodes.h          |   3 +-
 src/include/utils/memutils.h          |   7 +
 src/include/utils/memutils_internal.h |  18 +-
 src/tools/pgindent/typedefs.list      |   2 +
 9 files changed, 862 insertions(+), 5 deletions(-)
 create mode 100644 src/backend/utils/mmgr/bump.c

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index d4244facbb..81df3bdf95 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -149,7 +149,7 @@ my @abstract_types = qw(Node);
 # they otherwise don't participate in node support.
 my @extra_tags = qw(
   IntList OidList XidList
-  AllocSetContext GenerationContext SlabContext
+  AllocSetContext GenerationContext SlabContext BumpContext
   TIDBitmap
   WindowObjectData
 );
diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index dae3432c98..01a1fb8527 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	alignedalloc.o \
 	aset.o \
+	bump.o \
 	dsa.o \
 	freepage.o \
 	generation.o \
diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
new file mode 100644
index 0000000000..f98a203a0c
--- /dev/null
+++ b/src/backend/utils/mmgr/bump.c
@@ -0,0 +1,818 @@
+/*-------------------------------------------------------------------------
+ *
+ * bump.c
+ *	  Bump allocator definitions.
+ *
+ * Bump is a MemoryContext implementation designed for memory usages which
+ * require allocating a large number of chunks, none of which ever need to be
+ * pfree'd or realloc'd.  Chunks allocated by this context have no chunk header
+ * and operations which ordinarily require looking at the chunk header cannot
+ * be performed.  For example, pfree, realloc, GetMemoryChunkSpace and
+ * GetMemoryChunkContext are all not possible with bump allocated chunks.  The
+ * only way to release memory allocated by this context type is to reset or
+ * delete the context.
+ *
+ * Portions Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/bump.c
+ *
+ *
+ *	Bump is best suited to cases which require a large number of short-lived
+ *	chunks where performance matters.  Because bump allocated chunks don't
+ *	have a chunk header, it can fit more chunks on each block.  This means we
+ *	can do more with less memory and fewer cache lines.  The reason it's best
+ *	suited for short-lived usages of memory is that ideally, pointers to bump
+ *	allocated chunks won't be visible to a large amount of code.  The more
+ *	code that operates on memory allocated by this allocator, the more chances
+ *	that some code will try to perform a pfree or one of the other operations
+ *	which are made impossible due to the lack of chunk header.  In order to
+ *	to detect accidental usage of the various disallowed operations, we do add
+ *	a MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
+ *	various disallowed functions raise an ERROR.
+ *
+ *	Allocations are MAXALIGNed.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "port/pg_bitutils.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
+
+#define Bump_BLOCKHDRSZ	MAXALIGN(sizeof(BumpBlock))
+
+/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
+#ifdef MEMORY_CONTEXT_CHECKING
+#define Bump_CHUNKHDRSZ	sizeof(MemoryChunk)
+#else
+#define Bump_CHUNKHDRSZ	0
+#endif
+
+#define Bump_CHUNK_FRACTION	8
+
+/* The keeper block is allocated in the same allocation as the set */
+#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
+#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))
+
+typedef struct BumpBlock BumpBlock; /* forward reference */
+
+typedef struct BumpContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* Bump context parameters */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
+
+	dlist_head	blocks;			/* list of blocks with the block currently
+								 * being filled at the head */
+} BumpContext;
+
+/*
+ * BumpBlock
+ *		BumpBlock is the unit of memory that is obtained by bump.c from
+ *		malloc().  It contains zero or more allocations, which are the
+ *		units requested by palloc().
+ */
+struct BumpBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+#ifdef MEMORY_CONTEXT_CHECKING
+	BumpContext *context;		/* pointer back to the owning context */
+#endif
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+/*
+ * BumpIsValid
+ *		True iff set is valid bump context.
+ */
+#define BumpIsValid(set) \
+	(PointerIsValid(set) && IsA(set, BumpContext))
+
+/*
+ * BumpBlockIsValid
+ *		True iff block is valid block of a bump context
+ */
+#define BumpBlockIsValid(block) \
+	(PointerIsValid(block) && BumpIsValid((block)->context))
+
+/*
+ * We always store external chunks on a dedicated block.  This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+	(BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
+
+/* Inlined helper functions */
+static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
+								 Size blksize);
+static inline bool BumpBlockIsEmpty(BumpBlock *block);
+static inline void BumpBlockMarkEmpty(BumpBlock *block);
+static inline Size BumpBlockFreeBytes(BumpBlock *block);
+static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
+
+
+/*
+* BumpContextCreate
+*		Create a new Bump context.
+*
+* parent: parent context, or NULL if top-level context
+* name: name of context (must be statically allocated)
+* minContextSize: minimum context size
+* initBlockSize: initial allocation block size
+* maxBlockSize: maximum allocation block size
+*/
+MemoryContext
+BumpContextCreate(MemoryContext parent,
+				  const char *name,
+				  Size minContextSize,
+				  Size initBlockSize,
+				  Size maxBlockSize)
+{
+	Size		firstBlockSize;
+	Size		allocSize;
+	BumpContext *set;
+	BumpBlock  *block;
+
+	/* ensure MemoryChunk's size is properly maxaligned */
+	StaticAssertDecl(Bump_CHUNKHDRSZ == MAXALIGN(Bump_CHUNKHDRSZ),
+					 "sizeof(MemoryChunk) is not maxaligned");
+
+	/*
+	 * First, validate allocation parameters.  Asserts seem sufficient because
+	 * nobody varies their parameters at runtime.  We somewhat arbitrarily
+	 * enforce a minimum 1K block size.  We restrict the maximum block size to
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+	 * regards to addressing the offset between the chunk and the block that
+	 * the chunk is stored on.  We would be unable to store the offset between
+	 * the chunk and block for any chunks that were beyond
+	 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+	 * larger than this.
+	 */
+	Assert(initBlockSize == MAXALIGN(initBlockSize) &&
+		   initBlockSize >= 1024);
+	Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
+		   maxBlockSize >= initBlockSize &&
+		   AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
+	Assert(minContextSize == 0 ||
+		   (minContextSize == MAXALIGN(minContextSize) &&
+			minContextSize >= 1024 &&
+			minContextSize <= maxBlockSize));
+	Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+
+	/* Determine size of initial block */
+	allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
+		Bump_CHUNKHDRSZ;
+	if (minContextSize != 0)
+		allocSize = Max(allocSize, minContextSize);
+	else
+		allocSize = Max(allocSize, initBlockSize);
+
+	/*
+	 * Allocate the initial block.  Unlike other bump.c blocks, it starts with
+	 * the context header and its block header follows that.
+	 */
+	set = (BumpContext *) malloc(allocSize);
+	if (set == NULL)
+	{
+		MemoryContextStats(TopMemoryContext);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("Failed while creating memory context \"%s\".",
+						   name)));
+	}
+
+	/*
+	 * Avoid writing code that can fail between here and MemoryContextCreate;
+	 * we'd leak the header if we ereport in this stretch.
+	 */
+	dlist_init(&set->blocks);
+
+	/* Fill in the initial block's block header */
+	block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
+	/* determine the block size and initialize it */
+	firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
+	BumpBlockInit(set, block, firstBlockSize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	/*
+	 * Fill in BumpContext-specific header fields.  The Asserts above should
+	 * ensure that these all fit inside a uint32.
+	 */
+	set->initBlockSize = (uint32) initBlockSize;
+	set->maxBlockSize = (uint32) maxBlockSize;
+	set->nextBlockSize = (uint32) initBlockSize;
+
+	/*
+	 * Compute the allocation chunk size limit for this context.
+	 *
+	 * Limit the maximum size a non-dedicated chunk can be so that we can fit
+	 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
+	 * block.  We must further limit this value so that it's no more than
+	 * MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks larger
+	 * than that value as we store the chunk size in the MemoryChunk 'value'
+	 * field in the call to MemoryChunkSetHdrMask().
+	 */
+	set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
+	while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
+		   (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
+		set->allocChunkLimit >>= 1;
+
+	/* Finally, do the type-independent part of context creation */
+	MemoryContextCreate((MemoryContext) set,
+						T_BumpContext,
+						MCTX_BUMP_ID,
+						parent,
+						name);
+
+	((MemoryContext) set)->mem_allocated = allocSize;
+
+	return (MemoryContext) set;
+}
+
+/*
+ * BumpReset
+ *		Frees all memory which is allocated in the given set.
+ *
+ * The code simply frees all the blocks in the context apart from the keeper
+ * block.
+ */
+void
+BumpReset(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_mutable_iter miter;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	BumpCheck(context);
+#endif
+
+	dlist_foreach_modify(miter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, miter.cur);
+
+		if (IsKeeperBlock(set, block))
+			BumpBlockMarkEmpty(block);
+		else
+			BumpBlockFree(set, block);
+	}
+
+	/* Reset block size allocation sequence, too */
+	set->nextBlockSize = set->initBlockSize;
+
+	/* Ensure there is only 1 item in the dlist */
+	Assert(!dlist_is_empty(&set->blocks));
+	Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
+}
+
+/*
+ * BumpDelete
+ *		Free all memory which is allocated in the given context.
+ */
+void
+BumpDelete(MemoryContext context)
+{
+	/* Reset to release all releasable BumpBlocks */
+	BumpReset(context);
+	/* And free the context header and keeper block */
+	free(context);
+}
+
+/*
+ * Helper for BumpAlloc() that allocates an entire block for the chunk.
+ *
+ * BumpAlloc()'s comment explains why this is separate.
+ */
+pg_noinline
+static void *
+BumpAllocLarge(MemoryContext context, Size size, int flags)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#endif
+	Size		chunk_size;
+	Size		required_size;
+	Size		blksize;
+
+	/* validate 'size' is within the limits for the given 'flags' */
+	MemoryContextCheckSize(context, size, flags);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+	blksize = required_size + Bump_BLOCKHDRSZ;
+
+	block = (BumpBlock *) malloc(blksize);
+	if (block == NULL)
+		return NULL;
+
+	context->mem_allocated += blksize;
+
+	/* the block is completely full */
+	block->freeptr = block->endptr = ((char *) block) + blksize;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* block with a single (used) chunk */
+	block->context = set;
+
+	chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
+
+	/* mark the MemoryChunk as externally managed */
+	MemoryChunkSetHdrMaskExternal(chunk, MCTX_BUMP_ID);
+
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+#endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* add the block to the list of allocated blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
+#endif
+}
+
+/*
+ * Small helper for allocating a new chunk from a chunk, to avoid duplicating
+ * the code between BumpAlloc() and BumpAllocFromNewBlock().
+ */
+static inline void *
+BumpAllocChunkFromBlock(MemoryContext context, BumpBlock *block, Size size,
+						Size chunk_size)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	MemoryChunk *chunk;
+#else
+	void	   *ptr;
+#endif
+
+	/* validate we've been given a block with enough free space */
+	Assert(block != NULL);
+	Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	chunk = (MemoryChunk *) block->freeptr;
+#else
+	ptr = (void *) block->freeptr;
+#endif
+
+	/* point the freeptr beyond this chunk */
+	block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
+	Assert(block->freeptr <= block->endptr);
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Prepare to initialize the chunk header. */
+	VALGRIND_MAKE_MEM_UNDEFINED(chunk, Bump_CHUNKHDRSZ);
+
+	MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
+	chunk->requested_size = size;
+	/* set mark to catch clobber of "unused" space */
+	Assert(size < chunk_size);
+	set_sentinel(MemoryChunkGetPointer(chunk), size);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+	/* fill the allocated space with junk */
+	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
+#endif
+
+	/* Ensure any padding bytes are marked NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
+							   chunk_size - size);
+
+	/* Disallow access to the chunk header. */
+	VALGRIND_MAKE_MEM_NOACCESS(chunk, Bump_CHUNKHDRSZ);
+
+	return MemoryChunkGetPointer(chunk);
+#else
+	return ptr;
+#endif							/* MEMORY_CONTEXT_CHECKING */
+}
+
+/*
+ * Helper for BumpAlloc() that allocates a new block and returns a chunk
+ * allocated from it.
+ *
+ * BumpAlloc()'s comment explains why this is separate.
+ */
+pg_noinline
+static void *
+BumpAllocFromNewBlock(MemoryContext context, Size size, int flags,
+					  Size chunk_size)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+	Size		blksize;
+	Size		required_size;
+
+	/*
+	 * The first such block has size initBlockSize, and we double the space in
+	 * each succeeding block, but not more than maxBlockSize.
+	 */
+	blksize = set->nextBlockSize;
+	set->nextBlockSize <<= 1;
+	if (set->nextBlockSize > set->maxBlockSize)
+		set->nextBlockSize = set->maxBlockSize;
+
+	/* we'll need space for the chunk, chunk hdr and block hdr */
+	required_size = chunk_size + Bump_CHUNKHDRSZ + Bump_BLOCKHDRSZ;
+	/* round the size up to the next power of 2 */
+	if (blksize < required_size)
+		blksize = pg_nextpower2_size_t(required_size);
+
+	block = (BumpBlock *) malloc(blksize);
+
+	if (block == NULL)
+		return MemoryContextAllocationFailure(context, size, flags);
+
+	context->mem_allocated += blksize;
+
+	/* initialize the new block */
+	BumpBlockInit(set, block, blksize);
+
+	/* add it to the doubly-linked list of blocks */
+	dlist_push_head(&set->blocks, &block->node);
+
+	return BumpAllocChunkFromBlock(context, block, size, chunk_size);
+}
+
+/*
+ * BumpAlloc
+ *		Returns a pointer to allocated memory of given size or raises an ERROR
+ *		on allocation failure, or returns NULL when flags contains
+ *		MCXT_ALLOC_NO_OOM.
+ *
+ * No request may exceed:
+ *		MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
+ * All callers use a much-lower limit.
+ *
+ *
+ * Note: when using valgrind, it doesn't matter how the returned allocation
+ * is marked, as mcxt.c will set it to UNDEFINED.
+ * This function should only contain the most common code paths.  Everything
+ * else should be in pg_noinline helper functions, thus avoiding the overhead
+ * of creating a stack frame for the common cases.  Allocating memory is often
+ * a bottleneck in many workloads, so avoiding stack frame setup is
+ * worthwhile.  Helper functions should always directly return the newly
+ * allocated memory so that we can just return that address directly as a tail
+ * call.
+ */
+void *
+BumpAlloc(MemoryContext context, Size size, int flags)
+{
+	BumpContext *set = (BumpContext *) context;
+	BumpBlock  *block;
+	Size		chunk_size;
+	Size		required_size;
+
+	Assert(BumpIsValid(set));
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* ensure there's always space for the sentinel byte */
+	chunk_size = MAXALIGN(size + 1);
+#else
+	chunk_size = MAXALIGN(size);
+#endif
+
+	/*
+	 * If requested size exceeds maximum for chunks we hand the the request
+	 * off to BumpAllocLarge().
+	 */
+	if (chunk_size > set->allocChunkLimit)
+		return BumpAllocLarge(context, size, flags);
+
+	required_size = chunk_size + Bump_CHUNKHDRSZ;
+
+	/*
+	 * Not an oversized chunk.  We try to first make use of the latest block,
+	 * but if there's not enough space in it we must allocate a new block.
+	 */
+	block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
+
+	if (BumpBlockFreeBytes(block) < required_size)
+		return BumpAllocFromNewBlock(context, size, flags, chunk_size);
+
+	/* The current block has space, so just allocate chunk there. */
+	return BumpAllocChunkFromBlock(context, block, size, chunk_size);
+
+}
+
+/*
+ * BumpBlockInit
+ *		Initializes 'block' assuming 'blksize'.  Does not update the context's
+ *		mem_allocated field.
+ */
+static inline void
+BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+	block->context = context;
+#endif
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+	block->endptr = ((char *) block) + blksize;
+
+	/* Mark unallocated space NOACCESS. */
+	VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - Bump_BLOCKHDRSZ);
+}
+
+/*
+ * BumpBlockIsEmpty
+ *		Returns true iff 'block' contains no chunks
+ */
+static inline bool
+BumpBlockIsEmpty(BumpBlock *block)
+{
+	/* it's empty if the freeptr has not moved */
+	return (block->freeptr == ((char *) block + Bump_BLOCKHDRSZ));
+}
+
+/*
+ * BumpBlockMarkEmpty
+ *		Set a block as empty.  Does not free the block.
+ */
+static inline void
+BumpBlockMarkEmpty(BumpBlock *block)
+{
+#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
+	char	   *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
+#endif
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(datastart, block->freeptr - datastart);
+#else
+	/* wipe_mem() would have done this */
+	VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
+#endif
+
+	/* Reset the block, but don't return it to malloc */
+	block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
+}
+
+/*
+ * BumpBlockFreeBytes
+ *		Returns the number of bytes free in 'block'
+ */
+static inline Size
+BumpBlockFreeBytes(BumpBlock *block)
+{
+	return (block->endptr - block->freeptr);
+}
+
+/*
+ * BumpBlockFree
+ *		Remove 'block' from 'set' and release the memory consumed by it.
+ */
+static inline void
+BumpBlockFree(BumpContext *set, BumpBlock *block)
+{
+	/* Make sure nobody tries to free the keeper block */
+	Assert(!IsKeeperBlock(set, block));
+
+	/* release the block from the list of blocks */
+	dlist_delete(&block->node);
+
+	((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
+
+#ifdef CLOBBER_FREED_MEMORY
+	wipe_mem(block, ((char *) block->endptr - (char *) block));
+#endif
+
+	free(block);
+}
+
+/*
+ * BumpFree
+ *		Unsupported.
+ */
+void
+BumpFree(void *pointer)
+{
+	elog(ERROR, "pfree is not supported by the bump memory allocator");
+}
+
+/*
+ * BumpRealloc
+ *		Unsupported.
+ */
+void *
+BumpRealloc(void *pointer, Size size, int flags)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * BumpGetChunkContext
+ *		Unsupported.
+ */
+MemoryContext
+BumpGetChunkContext(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+* BumpGetChunkSpace
+*		Given a currently-allocated chunk, determine the total space
+*		it occupies (including all memory-allocation overhead).
+*/
+Size
+BumpGetChunkSpace(void *pointer)
+{
+	elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
+	return 0;					/* keep compiler quiet */
+}
+
+/*
+ * BumpIsEmpty
+ *		Is a BumpContext empty of any allocated space?
+ */
+bool
+BumpIsEmpty(MemoryContext context)
+{
+	BumpContext *set = (BumpContext *) context;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		if (!BumpBlockIsEmpty(block))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * BumpStats
+ *		Compute stats about memory consumption of a Bump context.
+ *
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ * print_to_stderr: print stats to stderr if true, elog otherwise.
+ */
+void
+BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+		  void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
+{
+	BumpContext *set = (BumpContext *) context;
+	Size		nblocks = 0;
+	Size		totalspace = 0;
+	Size		freespace = 0;
+	dlist_iter	iter;
+
+	Assert(BumpIsValid(set));
+
+	dlist_foreach(iter, &set->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+
+		nblocks++;
+		totalspace += (block->endptr - (char *) block);
+		freespace += (block->endptr - block->freeptr);
+	}
+
+	if (printfunc)
+	{
+		char		stats_string[200];
+
+		snprintf(stats_string, sizeof(stats_string),
+				 "%zu total in %zu blocks; %zu free; %zu used",
+				 totalspace, nblocks, freespace, totalspace - freespace);
+		printfunc(context, passthru, stats_string, print_to_stderr);
+	}
+
+	if (totals)
+	{
+		totals->nblocks += nblocks;
+		totals->totalspace += totalspace;
+		totals->freespace += freespace;
+	}
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * BumpCheck
+ *		Walk through chunks and check consistency of memory.
+ *
+ * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
+ * find yourself in an infinite loop when trouble occurs, because this
+ * routine will be entered again when elog cleanup tries to release memory!
+ */
+void
+BumpCheck(MemoryContext context)
+{
+	BumpContext *bump = (BumpContext *) context;
+	const char *name = context->name;
+	dlist_iter	iter;
+	Size		total_allocated = 0;
+
+	/* walk all blocks in this context */
+	dlist_foreach(iter, &bump->blocks)
+	{
+		BumpBlock  *block = dlist_container(BumpBlock, node, iter.cur);
+		int			nchunks;
+		char	   *ptr;
+		bool		has_external_chunk = false;
+
+		if (IsKeeperBlock(bump, block))
+			total_allocated += block->endptr - (char *) bump;
+		else
+			total_allocated += block->endptr - (char *) block;
+
+		/* check block belongs to the correct context */
+		if (block->context != bump)
+			elog(WARNING, "problem in Bump %s: bogus context link in block %p",
+				 name, block);
+
+		/* now walk through the chunks and count them */
+		nchunks = 0;
+		ptr = ((char *) block) + Bump_BLOCKHDRSZ;
+
+		while (ptr < block->freeptr)
+		{
+			MemoryChunk *chunk = (MemoryChunk *) ptr;
+			BumpBlock  *chunkblock;
+			Size		chunksize;
+
+			/* allow access to the chunk header */
+			VALGRIND_MAKE_MEM_DEFINED(chunk, Bump_CHUNKHDRSZ);
+
+			if (MemoryChunkIsExternal(chunk))
+			{
+				chunkblock = ExternalChunkGetBlock(chunk);
+				chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+				has_external_chunk = true;
+			}
+			else
+			{
+				chunkblock = MemoryChunkGetBlock(chunk);
+				chunksize = MemoryChunkGetValue(chunk);
+			}
+
+			/* move to the next chunk */
+			ptr += (chunksize + Bump_CHUNKHDRSZ);
+
+			nchunks += 1;
+
+			/* chunks have both block and context pointers, so check both */
+			if (chunkblock != block)
+				elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
+					 name, block, chunk);
+		}
+
+		if (has_external_chunk && nchunks > 1)
+			elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
+				 name, block);
+
+	}
+
+	Assert(total_allocated == context->mem_allocated);
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 52ae120af9..80abfdc9d7 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -100,6 +100,19 @@ static const MemoryContextMethods mcxt_methods[] = {
 	[MCTX_ALIGNED_REDIRECT_ID].check = NULL,	/* not required */
 #endif
 
+	/* bump.c */
+	[MCTX_BUMP_ID].alloc = BumpAlloc,
+	[MCTX_BUMP_ID].free_p = BumpFree,
+	[MCTX_BUMP_ID].realloc = BumpRealloc,
+	[MCTX_BUMP_ID].reset = BumpReset,
+	[MCTX_BUMP_ID].delete_context = BumpDelete,
+	[MCTX_BUMP_ID].get_chunk_context = BumpGetChunkContext,
+	[MCTX_BUMP_ID].get_chunk_space = BumpGetChunkSpace,
+	[MCTX_BUMP_ID].is_empty = BumpIsEmpty,
+	[MCTX_BUMP_ID].stats = BumpStats,
+#ifdef MEMORY_CONTEXT_CHECKING
+	[MCTX_BUMP_ID].check = BumpCheck,
+#endif
 
 	/*
 	 * Unused (as yet) IDs should have dummy entries here.  This allows us to
@@ -107,8 +120,6 @@ static const MemoryContextMethods mcxt_methods[] = {
 	 * seems sufficient to provide routines for the methods that might get
 	 * invoked from inspection of a chunk (see MCXT_METHOD calls below).
 	 */
-
-	BOGUS_MCTX(MCTX_7_UNUSED_ID),
 	BOGUS_MCTX(MCTX_8_UNUSED_ID),
 	BOGUS_MCTX(MCTX_9_UNUSED_ID),
 	BOGUS_MCTX(MCTX_10_UNUSED_ID),
diff --git a/src/backend/utils/mmgr/meson.build b/src/backend/utils/mmgr/meson.build
index 9dcf990cdc..dd43a6844c 100644
--- a/src/backend/utils/mmgr/meson.build
+++ b/src/backend/utils/mmgr/meson.build
@@ -3,6 +3,7 @@
 backend_sources += files(
   'alignedalloc.c',
   'aset.c',
+  'bump.c',
   'dsa.c',
   'freepage.c',
   'generation.c',
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index edc0257f36..c4c9fd3e3e 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -146,6 +146,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), BumpContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 6e5fa72b0e..4446e14223 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -108,6 +108,13 @@ extern void ProcessLogMemoryContextInterrupt(void);
  * Memory-context-type-specific functions
  */
 
+/* bump.c */
+extern MemoryContext BumpContextCreate(MemoryContext parent,
+									   const char *name,
+									   Size minContextSize,
+									   Size initBlockSize,
+									   Size maxBlockSize);
+
 /* aset.c */
 extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
 												   const char *name,
diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h
index c3f010b595..d39392a873 100644
--- a/src/include/utils/memutils_internal.h
+++ b/src/include/utils/memutils_internal.h
@@ -79,6 +79,22 @@ extern void *AlignedAllocRealloc(void *pointer, Size size, int flags);
 extern MemoryContext AlignedAllocGetChunkContext(void *pointer);
 extern Size AlignedAllocGetChunkSpace(void *pointer);
 
+ /* These functions implement the MemoryContext API for the Bump context. */
+extern void *BumpAlloc(MemoryContext context, Size size, int flags);
+extern void BumpFree(void *pointer);
+extern void *BumpRealloc(void *pointer, Size size, int flags);
+extern void BumpReset(MemoryContext context);
+extern void BumpDelete(MemoryContext context);
+extern MemoryContext BumpGetChunkContext(void *pointer);
+extern Size BumpGetChunkSpace(void *pointer);
+extern bool BumpIsEmpty(MemoryContext context);
+extern void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc,
+					  void *passthru, MemoryContextCounters *totals,
+					  bool print_to_stderr);
+#ifdef MEMORY_CONTEXT_CHECKING
+extern void BumpCheck(MemoryContext context);
+#endif
+
 /*
  * How many extra bytes do we need to request in order to ensure that we can
  * align a pointer to 'alignto'.  Since palloc'd pointers are already aligned
@@ -111,7 +127,7 @@ typedef enum MemoryContextMethodID
 	MCTX_GENERATION_ID,
 	MCTX_SLAB_ID,
 	MCTX_ALIGNED_REDIRECT_ID,
-	MCTX_7_UNUSED_ID,
+	MCTX_BUMP_ID,
 	MCTX_8_UNUSED_ID,
 	MCTX_9_UNUSED_ID,
 	MCTX_10_UNUSED_ID,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 01845ee71d..4f47218dc6 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -335,6 +335,8 @@ BulkInsertState
 BulkInsertStateData
 BulkWriteBuffer
 BulkWriteState
+BumpBlock
+BumpContext
 CACHESIGN
 CAC_state
 CCFastEqualFN
-- 
2.40.1.windows.1

v6-0003-Use-bump-memory-context-for-tuplesorts.patchtext/plain; charset=US-ASCII; name=v6-0003-Use-bump-memory-context-for-tuplesorts.patchDownload
From 068f2cba7722f3459c9f38543016befabf215791 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 20 Feb 2024 22:23:42 +1300
Subject: [PATCH v4 2/2] Use bump memory context for tuplesorts

---
 src/backend/utils/sort/tuplesort.c         | 52 ++++++++++++----------
 src/backend/utils/sort/tuplesortvariants.c | 38 +++++++++++++---
 src/include/utils/tuplesort.h              | 21 ++++++---
 3 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index f50a9c1a8e..7c4d6dc106 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -191,6 +191,11 @@ struct Tuplesortstate
 								 * tuples to return? */
 	bool		boundUsed;		/* true if we made use of a bounded heap */
 	int			bound;			/* if bounded, the maximum number of tuples */
+	int64		tupleMem;		/* memory consumed by individual tuples.
+								 * storing this separately from what we track
+								 * in availMem allows us to subtract the
+								 * memory consumed by all tuples when dumping
+								 * tuples to tape */
 	int64		availMem;		/* remaining memory available, in bytes */
 	int64		allowedMem;		/* total memory allowed, in bytes */
 	int			maxTapes;		/* max number of input tapes to merge in each
@@ -764,18 +769,18 @@ tuplesort_begin_batch(Tuplesortstate *state)
 	 * in the parent context, not this context, because there is no need to
 	 * free memtuples early.  For bounded sorts, tuples may be pfreed in any
 	 * order, so we use a regular aset.c context so that it can make use of
-	 * free'd memory.  When the sort is not bounded, we make use of a
-	 * generation.c context as this keeps allocations more compact with less
-	 * wastage.  Allocations are also slightly more CPU efficient.
-	 */
-	if (state->base.sortopt & TUPLESORT_ALLOWBOUNDED)
+	 * free'd memory.  When the sort is not bounded, we make use of a bump.c
+	 * context as this keeps allocations more compact with less wastage.
+	 * Allocations are also slightly more CPU efficient.
+	 */
+	if (TupleSortUseBumpTupleCxt(state->base.sortopt))
+		state->base.tuplecontext = BumpContextCreate(state->base.sortcontext,
+													 "Caller tuples",
+													 ALLOCSET_DEFAULT_SIZES);
+	else
 		state->base.tuplecontext = AllocSetContextCreate(state->base.sortcontext,
 														 "Caller tuples",
 														 ALLOCSET_DEFAULT_SIZES);
-	else
-		state->base.tuplecontext = GenerationContextCreate(state->base.sortcontext,
-														   "Caller tuples",
-														   ALLOCSET_DEFAULT_SIZES);
 
 
 	state->status = TSS_INITIAL;
@@ -1181,15 +1186,16 @@ noalloc:
  * Shared code for tuple and datum cases.
  */
 void
-tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev)
+tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple,
+						  bool useAbbrev, Size tuplen)
 {
 	MemoryContext oldcontext = MemoryContextSwitchTo(state->base.sortcontext);
 
 	Assert(!LEADER(state));
 
-	/* Count the size of the out-of-line data */
-	if (tuple->tuple != NULL)
-		USEMEM(state, GetMemoryChunkSpace(tuple->tuple));
+	/* account for the memory used for this tuple */
+	USEMEM(state, tuplen);
+	state->tupleMem += tuplen;
 
 	if (!useAbbrev)
 	{
@@ -2397,13 +2403,6 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 		SortTuple  *stup = &state->memtuples[i];
 
 		WRITETUP(state, state->destTape, stup);
-
-		/*
-		 * Account for freeing the tuple, but no need to do the actual pfree
-		 * since the tuplecontext is being reset after the loop.
-		 */
-		if (stup->tuple != NULL)
-			FREEMEM(state, GetMemoryChunkSpace(stup->tuple));
 	}
 
 	state->memtupcount = 0;
@@ -2411,12 +2410,19 @@ dumptuples(Tuplesortstate *state, bool alltuples)
 	/*
 	 * Reset tuple memory.  We've freed all of the tuples that we previously
 	 * allocated.  It's important to avoid fragmentation when there is a stark
-	 * change in the sizes of incoming tuples.  Fragmentation due to
-	 * AllocSetFree's bucketing by size class might be particularly bad if
-	 * this step wasn't taken.
+	 * change in the sizes of incoming tuples.  In bounded sorts,
+	 * fragmentation due to AllocSetFree's bucketing by size class might be
+	 * particularly bad if this step wasn't taken.
 	 */
 	MemoryContextReset(state->base.tuplecontext);
 
+	/*
+	 * Now update the memory accounting to subtract the memory used by the
+	 * tuple.
+	 */
+	FREEMEM(state, state->tupleMem);
+	state->tupleMem = 0;
+
 	markrunend(state->destTape);
 
 #ifdef TRACE_SORT
diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c
index 08f9f16262..42293b5095 100644
--- a/src/backend/utils/sort/tuplesortvariants.c
+++ b/src/backend/utils/sort/tuplesortvariants.c
@@ -674,6 +674,7 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 	SortTuple	stup;
 	MinimalTuple tuple;
 	HeapTupleData htup;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tuple = ExecCopySlotMinimalTuple(slot);
@@ -686,9 +687,15 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
 							   tupDesc,
 							   &stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -705,6 +712,7 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 	TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
+	Size		tuplen;
 
 	/* copy the tuple into sort storage */
 	tup = heap_copytuple(tup);
@@ -722,10 +730,16 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
 								   &stup.isnull1);
 	}
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
+	else
+		tuplen = GetMemoryChunkSpace(tup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->haveDatum1 &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -743,6 +757,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 	IndexTuple	tuple;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
+	Size		tuplen;
 
 	stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
 										  isnull, base->tuplecontext);
@@ -754,10 +769,16 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
 								RelationGetDescr(arg->indexRel),
 								&stup.isnull1);
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
+	else
+		tuplen = GetMemoryChunkSpace(tuple);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 }
 
 /*
@@ -770,6 +791,7 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	BrinSortTuple *bstup;
 	TuplesortPublic *base = TuplesortstateGetPublic(state);
 	MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
+	Size		tuplen;
 
 	/* allocate space for the whole BRIN sort tuple */
 	bstup = palloc(BRINSORTTUPLE_SIZE(size));
@@ -781,10 +803,16 @@ tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
 	stup.datum1 = tuple->bt_blkno;
 	stup.isnull1 = false;
 
+	/* GetMemoryChunkSpace is not supported for bump contexts */
+	if (TupleSortUseBumpTupleCxt(base->sortopt))
+		tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
+	else
+		tuplen = GetMemoryChunkSpace(bstup);
+
 	tuplesort_puttuple_common(state, &stup,
 							  base->sortKeys &&
 							  base->sortKeys->abbrev_converter &&
-							  !stup.isnull1);
+							  !stup.isnull1, tuplen);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -833,7 +861,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
 
 	tuplesort_puttuple_common(state, &stup,
 							  base->tuples &&
-							  base->sortKeys->abbrev_converter && !isNull);
+							  base->sortKeys->abbrev_converter && !isNull, 0);
 
 	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 1013c64dab..e7941a1f09 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -98,6 +98,15 @@ typedef enum
 /* specifies if the tuplesort is able to support bounded sorts */
 #define TUPLESORT_ALLOWBOUNDED			(1 << 1)
 
+/*
+ * For bounded sort, tuples get pfree'd when they fall outside of the bound.
+ * When bounded sorts are not required, we can use a bump context for tuple
+ * allocation as there's no risk that pfree will ever be called for a tuple.
+ * Define a macro to make it easier for code to figure out if we're using a
+ * bump allocator.
+ */
+#define TupleSortUseBumpTupleCxt(opt) (((opt) & TUPLESORT_ALLOWBOUNDED) == 0)
+
 typedef struct TuplesortInstrumentation
 {
 	TuplesortMethod sortMethod; /* sort algorithm used */
@@ -109,10 +118,11 @@ typedef struct TuplesortInstrumentation
  * The objects we actually sort are SortTuple structs.  These contain
  * a pointer to the tuple proper (might be a MinimalTuple or IndexTuple),
  * which is a separate palloc chunk --- we assume it is just one chunk and
- * can be freed by a simple pfree() (except during merge, when we use a
- * simple slab allocator).  SortTuples also contain the tuple's first key
- * column in Datum/nullflag format, and a source/input tape number that
- * tracks which tape each heap element/slot belongs to during merging.
+ * can be freed by a simple pfree() (except during merge, where we use a
+ * simple slab allocator, and during a non-bounded sort where we use a bump
+ * allocator).  SortTuples also contain the tuple's first key column in
+ * Datum/nullflag format, and a source/input tape number that tracks which
+ * tape each heap element/slot belongs to during merging.
  *
  * Storing the first key column lets us save heap_getattr or index_getattr
  * calls during tuple comparisons.  We could extract and save all the key
@@ -367,7 +377,8 @@ extern Tuplesortstate *tuplesort_begin_common(int workMem,
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
 extern bool tuplesort_used_bound(Tuplesortstate *state);
 extern void tuplesort_puttuple_common(Tuplesortstate *state,
-									  SortTuple *tuple, bool useAbbrev);
+									  SortTuple *tuple, bool useAbbrev,
+									  Size tuplen);
 extern void tuplesort_performsort(Tuplesortstate *state);
 extern bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
 									  SortTuple *stup);
-- 
2.40.1

3bit_vs_4bit_MemoryContextMethodIDs_bench_results.txttext/plain; charset=US-ASCII; name=3bit_vs_4bit_MemoryContextMethodIDs_bench_results.txtDownload
#40Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: David Rowley (#39)
Re: Add bump memory context type and use it for tuplesorts

On Sat, 6 Apr 2024, 14:36 David Rowley, <dgrowleyml@gmail.com> wrote:

On Sat, 6 Apr 2024 at 02:30, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

On Thu, 4 Apr 2024 at 22:43, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Matthias van de Meent <boekewurm+postgres@gmail.com> writes:

It extends memory context IDs to 5 bits (32 values), of which
- 8 have glibc's malloc pattern of 001/010;
- 1 is unused memory's 00000
- 1 is wipe_mem's 11111
- 4 are used by existing contexts

(Aset/Generation/Slab/AlignedRedirect)

- 18 are newly available.

This seems like it would solve the problem for a good long time
to come; and if we ever need more IDs, we could steal one more bit
by requiring the offset to the block header to be a multiple of 8.
(Really, we could just about do that today at little or no cost ...
machines with MAXALIGN less than 8 are very thin on the ground.)

Hmm, it seems like a decent idea, but I didn't want to deal with the
repercussions of that this late in the cycle when these 2 bits were
still relatively easy to get hold of.

Thanks for writing the patch.

I think 5 bits is 1 too many. 4 seems fine. I also think you've
reserved too many slots in your patch as I disagree that we need to
reserve the glibc malloc pattern anywhere but in the 1 and 2 slots of
the mcxt_methods[] array. I looked again at the 8 bytes prior to a
glibc malloc'd chunk and I see the lowest 4 bits of the headers
consistently set to 0001 for all powers of 2 starting at 8 up to
65536.

Malloc's docs specify the minimum chunk size at 4*sizeof(void*) and itself
uses , so using powers of 2 for chunks would indeed fail to detect 1s in
the 4th bit. I suspect you'll get different results when you check the
allocation patterns of multiples of 8 bytes, starting from 40, especially
on 32-bit arm (where MALLOC_ALIGNMENT is 8 bytes, rather than the 16 bytes
on i386 and 64-bit architectures, assuming [0]https://sourceware.org/glibc/wiki/MallocInternals#Platform-specific_Thresholds_and_Constants is accurate)

131072 seems to vary and beyond that, they seem to be set to

0010.

In your updated 0001, you don't seem to fill the RESERVED_GLIBC memctx
array entries with BOGUS_MCTX().

With that, there's no increase in the number of reserved slots from

what we have reserved today. Still 4. So having 4 bits instead of 3
bits gives us a total of 12 slots rather than 4 slots. Having 3x
slots seems enough. We might need an extra bit for something else
sometime. I think keeping it up our sleeve is a good idea.

Another reason not to make it 5 bits is that I believe that would make
the mcxt_methods[] array 2304 bytes rather than 576 bytes. 4 bits
makes it 1152 bytes, if I'm counting correctly.

I don't think I understand why this would be relevant when only 5 of the
contexts are actually in use (thus in caches). Is that size concern about
TLB entries then?

I revised the patch to simplify hdrmask logic. This started with me
having trouble finding the best set of words to document that the
offset is "half the bytes between the chunk and block". So, instead
of doing that, I've just made it so these two fields effectively
overlap. The lowest bit of the block offset is the same bit as the
high bit of what MemoryChunkGetValue returns.

Works for me, I suppose.

I also updated src/backend/utils/mmgr/README to explain this and

adjust the mentions of 3-bits and 61-bits to 4-bits and 60-bits. I
also explained the overlapping part.

Thanks!

[0]: https://sourceware.org/glibc/wiki/MallocInternals#Platform-specific_Thresholds_and_Constants
https://sourceware.org/glibc/wiki/MallocInternals#Platform-specific_Thresholds_and_Constants

Show quoted text
#41David Rowley
dgrowleyml@gmail.com
In reply to: Matthias van de Meent (#40)
Re: Add bump memory context type and use it for tuplesorts

On Sun, 7 Apr 2024 at 05:45, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

Malloc's docs specify the minimum chunk size at 4*sizeof(void*) and itself uses , so using powers of 2 for chunks would indeed fail to detect 1s in the 4th bit. I suspect you'll get different results when you check the allocation patterns of multiples of 8 bytes, starting from 40, especially on 32-bit arm (where MALLOC_ALIGNMENT is 8 bytes, rather than the 16 bytes on i386 and 64-bit architectures, assuming [0] is accurate)

I'm prepared to be overruled, but I just don't have strong feelings
that 32-bit is worth making these reservations for. Especially so
given the rate we're filling these slots. The only system that I see
the 4th bit change is Cygwin. It doesn't look like a very easy system
to protect against pfreeing of malloc'd chunks as the prior 8-bytes
seem to vary depending on the malloc'd size and I see all bit patterns
there, including the ones we use for our memory contexts.

Since we can't protect against every possible bit pattern there, we
need to draw the line somewhere. I don't think 32-bit systems are
worth reserving these precious slots for. I'd hazard a guess that
there are more instances of Postgres running on Windows today than on
32-bit CPUs and we don't seem too worried about the bit-patterns used
for Windows.

In your updated 0001, you don't seem to fill the RESERVED_GLIBC memctx array entries with BOGUS_MCTX().

Oops. Thanks

Another reason not to make it 5 bits is that I believe that would make
the mcxt_methods[] array 2304 bytes rather than 576 bytes. 4 bits
makes it 1152 bytes, if I'm counting correctly.

I don't think I understand why this would be relevant when only 5 of the contexts are actually in use (thus in caches). Is that size concern about TLB entries then?

It's a static const array. I don't want to bloat the binary with
something we'll likely never need. If we one day need it, we can
reserve another bit using the same overlapping method.

I revised the patch to simplify hdrmask logic. This started with me
having trouble finding the best set of words to document that the
offset is "half the bytes between the chunk and block". So, instead
of doing that, I've just made it so these two fields effectively
overlap. The lowest bit of the block offset is the same bit as the
high bit of what MemoryChunkGetValue returns.

Works for me, I suppose.

hmm. I don't detect much enthusiasm for it.

Personally, I quite like the method as it adds no extra instructions
when encoding the MemoryChunk and only a simple bitwise-AND when
decoding it. Your method added extra instructions in the encode and
decode. I went to great lengths to make this code as fast as
possible, so I know which method that I prefer. We often palloc and
never do anything that requires the chunk header to be decoded, so not
adding extra instructions on the encoding stage is a big win.

The only method I see to avoid adding instructions in encoding and
decoding is to reduce the bit-space for the MemoryChunkGetValue field
to 29 bits. Effectively, that means non-external chunks can only be
512MB rather than 1GB. As far as I know, that just limits slab.c to
only being able to do 512MB pallocs as generation.c and aset.c use
external chunks well below that threshold. Restricting slab to 512MB
is probably far from the end of the world. Anything close to that
would be a terrible use case for slab. I was just less keen on using
a bit from there as that's a field we allow the context implementation
to do what they like with. Having bitspace for 2^30 possible values in
there just seems nice given that it can store any possible value from
zero up to MaxAllocSize.

David

#42Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: David Rowley (#41)
Re: Add bump memory context type and use it for tuplesorts

On Sun, 7 Apr 2024, 01:59 David Rowley, <dgrowleyml@gmail.com> wrote:

On Sun, 7 Apr 2024 at 05:45, Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

Malloc's docs specify the minimum chunk size at 4*sizeof(void*) and

itself uses , so using powers of 2 for chunks would indeed fail to detect
1s in the 4th bit. I suspect you'll get different results when you check
the allocation patterns of multiples of 8 bytes, starting from 40,
especially on 32-bit arm (where MALLOC_ALIGNMENT is 8 bytes, rather than
the 16 bytes on i386 and 64-bit architectures, assuming [0] is accurate)

I'd hazard a guess that

there are more instances of Postgres running on Windows today than on
32-bit CPUs and we don't seem too worried about the bit-patterns used
for Windows.

Yeah, that is something I had some thoughts about too, but didn't check if
there was historical context around. I don't think it's worth bothering
right now though.

Another reason not to make it 5 bits is that I believe that would make

the mcxt_methods[] array 2304 bytes rather than 576 bytes. 4 bits
makes it 1152 bytes, if I'm counting correctly.

I don't think I understand why this would be relevant when only 5 of the

contexts are actually in use (thus in caches). Is that size concern about
TLB entries then?

It's a static const array. I don't want to bloat the binary with
something we'll likely never need. If we one day need it, we can
reserve another bit using the same overlapping method.

Fair points.

I revised the patch to simplify hdrmask logic. This started with me

having trouble finding the best set of words to document that the
offset is "half the bytes between the chunk and block". So, instead
of doing that, I've just made it so these two fields effectively
overlap. The lowest bit of the block offset is the same bit as the
high bit of what MemoryChunkGetValue returns.

Works for me, I suppose.

hmm. I don't detect much enthusiasm for it.

I had a tiring day leaving me short on enthousiasm, after which I realised
there were some things to this patch that would need fixing.

I could've worded this better, but nothing against this code.

-Matthias

#43John Naylor
johncnaylorls@gmail.com
In reply to: David Rowley (#39)
Re: Add bump memory context type and use it for tuplesorts

On Sat, Apr 6, 2024 at 7:37 PM David Rowley <dgrowleyml@gmail.com> wrote:

I'm planning on pushing these, pending a final look at 0002 and 0003

on Sunday morning NZ time (UTC+12), likely in about 10 hours time.

+1

I haven't looked at v6, but I've tried using it in situ, and it seems
to work as well as hoped:

/messages/by-id/CANWCAZZQFfxvzO8yZHFWtQV+Z2gAMv1ku16Vu7KWmb5kZQyd1w@mail.gmail.com

#44David Rowley
dgrowleyml@gmail.com
In reply to: John Naylor (#43)
Re: Add bump memory context type and use it for tuplesorts

On Sun, 7 Apr 2024 at 22:05, John Naylor <johncnaylorls@gmail.com> wrote:

On Sat, Apr 6, 2024 at 7:37 PM David Rowley <dgrowleyml@gmail.com> wrote:

I'm planning on pushing these, pending a final look at 0002 and 0003

on Sunday morning NZ time (UTC+12), likely in about 10 hours time.

+1

I've now pushed all 3 patches. Thank you for all the reviews on
these and for the extra MemoryContextMethodID bit, Matthias.

I haven't looked at v6, but I've tried using it in situ, and it seems
to work as well as hoped:

/messages/by-id/CANWCAZZQFfxvzO8yZHFWtQV+Z2gAMv1ku16Vu7KWmb5kZQyd1w@mail.gmail.com

I'm already impressed with the radix tree work. Nice to see bump
allowing a little more memory to be saved for TID storage.

David

#45Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: David Rowley (#44)
1 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On 4/7/24 14:37, David Rowley wrote:

On Sun, 7 Apr 2024 at 22:05, John Naylor <johncnaylorls@gmail.com> wrote:

On Sat, Apr 6, 2024 at 7:37 PM David Rowley <dgrowleyml@gmail.com> wrote:

I'm planning on pushing these, pending a final look at 0002 and 0003

on Sunday morning NZ time (UTC+12), likely in about 10 hours time.

+1

I've now pushed all 3 patches. Thank you for all the reviews on
these and for the extra MemoryContextMethodID bit, Matthias.

I haven't looked at v6, but I've tried using it in situ, and it seems
to work as well as hoped:

/messages/by-id/CANWCAZZQFfxvzO8yZHFWtQV+Z2gAMv1ku16Vu7KWmb5kZQyd1w@mail.gmail.com

I'm already impressed with the radix tree work. Nice to see bump
allowing a little more memory to be saved for TID storage.

David

There seems to be some issue with this on 32-bit machines. A couple
animals (grison, mamba) already complained about an assert int
BumpCheck() during initdb, I get the same crash on my rpi5 running
32-bit debian - see the backtrace attached.

I haven't investigated, but I'd considering it works on 64-bit, I guess
it's not considering alignment somewhere. I can dig more if needed.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

backtrace.txttext/plain; charset=UTF-8; name=backtrace.txtDownload
#46Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Tomas Vondra (#45)
Re: Add bump memory context type and use it for tuplesorts

On 4/7/24 22:35, Tomas Vondra wrote:

On 4/7/24 14:37, David Rowley wrote:

On Sun, 7 Apr 2024 at 22:05, John Naylor <johncnaylorls@gmail.com> wrote:

On Sat, Apr 6, 2024 at 7:37 PM David Rowley <dgrowleyml@gmail.com> wrote:

I'm planning on pushing these, pending a final look at 0002 and 0003

on Sunday morning NZ time (UTC+12), likely in about 10 hours time.

+1

I've now pushed all 3 patches. Thank you for all the reviews on
these and for the extra MemoryContextMethodID bit, Matthias.

I haven't looked at v6, but I've tried using it in situ, and it seems
to work as well as hoped:

/messages/by-id/CANWCAZZQFfxvzO8yZHFWtQV+Z2gAMv1ku16Vu7KWmb5kZQyd1w@mail.gmail.com

I'm already impressed with the radix tree work. Nice to see bump
allowing a little more memory to be saved for TID storage.

David

There seems to be some issue with this on 32-bit machines. A couple
animals (grison, mamba) already complained about an assert int
BumpCheck() during initdb, I get the same crash on my rpi5 running
32-bit debian - see the backtrace attached.

I haven't investigated, but I'd considering it works on 64-bit, I guess
it's not considering alignment somewhere. I can dig more if needed.

I did try running it under valgrind, and there doesn't seem to be
anything particularly wrong - just a bit of noise about calculating CRC
on uninitialized bytes.

regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#47Andres Freund
andres@anarazel.de
In reply to: Tomas Vondra (#45)
Re: Add bump memory context type and use it for tuplesorts

Hi,

On 2024-04-07 22:35:47 +0200, Tomas Vondra wrote:

I haven't investigated, but I'd considering it works on 64-bit, I guess
it's not considering alignment somewhere. I can dig more if needed.

I think I may the problem:

#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))

BumpContextCreate():
...
/* Fill in the initial block's block header */
block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
/* determine the block size and initialize it */
firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
BumpBlockInit(set, block, firstBlockSize);
...
((MemoryContext) set)->mem_allocated = allocSize;

void
BumpCheck(MemoryContext context)
...
if (IsKeeperBlock(bump, block))
total_allocated += block->endptr - (char *) bump;
...

I suspect that KeeperBlock() isn't returning true, because IsKeeperBlock misses
the MAXALIGN(). I think that about fits with:

#4 0x008f0088 in BumpCheck (context=0x131e330) at bump.c:808
808 Assert(total_allocated == context->mem_allocated);
(gdb) p total_allocated
$1 = 8120
(gdb) p context->mem_allocated
$2 = 8192

Greetings,

Andres Freund

#48Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Andres Freund (#47)
Re: Add bump memory context type and use it for tuplesorts

On 4/7/24 23:09, Andres Freund wrote:

Hi,

On 2024-04-07 22:35:47 +0200, Tomas Vondra wrote:

I haven't investigated, but I'd considering it works on 64-bit, I guess
it's not considering alignment somewhere. I can dig more if needed.

I think I may the problem:

#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + sizeof(BumpContext)))
#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))

BumpContextCreate():
...
/* Fill in the initial block's block header */
block = (BumpBlock *) (((char *) set) + MAXALIGN(sizeof(BumpContext)));
/* determine the block size and initialize it */
firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
BumpBlockInit(set, block, firstBlockSize);
...
((MemoryContext) set)->mem_allocated = allocSize;

void
BumpCheck(MemoryContext context)
...
if (IsKeeperBlock(bump, block))
total_allocated += block->endptr - (char *) bump;
...

I suspect that KeeperBlock() isn't returning true, because IsKeeperBlock misses
the MAXALIGN(). I think that about fits with:

#4 0x008f0088 in BumpCheck (context=0x131e330) at bump.c:808
808 Assert(total_allocated == context->mem_allocated);
(gdb) p total_allocated
$1 = 8120
(gdb) p context->mem_allocated
$2 = 8192

Yup, changing it to this:

#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) +
MAXALIGN(sizeof(BumpContext))))

fixes the issue for me.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#49Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tomas Vondra (#48)
Re: Add bump memory context type and use it for tuplesorts

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

Yup, changing it to this:

#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) +
MAXALIGN(sizeof(BumpContext))))

fixes the issue for me.

Mamba is happy with that change, too.

regards, tom lane

#50Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#49)
Re: Add bump memory context type and use it for tuplesorts

On 8 Apr 2024, at 00:41, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

Yup, changing it to this:

#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) +
MAXALIGN(sizeof(BumpContext))))

fixes the issue for me.

Mamba is happy with that change, too.

Unrelated to that one, seems like turaco ran into another issue:

running bootstrap script ... TRAP: failed Assert("total_allocated == context->mem_allocated"), File: "bump.c", Line: 808, PID: 7809

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=turaco&amp;dt=2024-04-07%2022%3A42%3A54

--
Daniel Gustafsson

#51Andres Freund
andres@anarazel.de
In reply to: Daniel Gustafsson (#50)
Re: Add bump memory context type and use it for tuplesorts

Hi,

On 2024-04-08 00:55:42 +0200, Daniel Gustafsson wrote:

On 8 Apr 2024, at 00:41, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Tomas Vondra <tomas.vondra@enterprisedb.com> writes:

Yup, changing it to this:

#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) +
MAXALIGN(sizeof(BumpContext))))

fixes the issue for me.

Mamba is happy with that change, too.

Unrelated to that one, seems like turaco ran into another issue:

running bootstrap script ... TRAP: failed Assert("total_allocated == context->mem_allocated"), File: "bump.c", Line: 808, PID: 7809

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=turaco&amp;dt=2024-04-07%2022%3A42%3A54

What makes you think that's unrelated? To me that looks like the same issue?

Greetings,

Andres Freund

#52David Rowley
dgrowleyml@gmail.com
In reply to: Andres Freund (#47)
Re: Add bump memory context type and use it for tuplesorts

On Mon, 8 Apr 2024 at 09:09, Andres Freund <andres@anarazel.de> wrote:

I suspect that KeeperBlock() isn't returning true, because IsKeeperBlock misses
the MAXALIGN(). I think that about fits with:

Thanks for investigating that.

I've just pushed a fix for the macro and also adjusted a location
which was *correctly* calculating the keeper block address manually to
use the macro. If I'd used the macro there to start with the Assert
likely wouldn't have failed, but there'd have been memory alignment
issues.

David

#53Daniel Gustafsson
daniel@yesql.se
In reply to: Andres Freund (#51)
Re: Add bump memory context type and use it for tuplesorts

On 8 Apr 2024, at 01:04, Andres Freund <andres@anarazel.de> wrote:

What makes you think that's unrelated? To me that looks like the same issue?

Nvm, I misread the assert, ETOOLITTLESLEEP. Sorry for the noise.

--
Daniel Gustafsson

#54Amul Sul
sulamul@gmail.com
In reply to: Daniel Gustafsson (#53)
1 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

Attached is a small patch adding the missing BumpContext description to the
README.

Regards,
Amul

Attachments:

0001-Add-BumpContext-description-to-mmgr-README.patchapplication/octet-stream; name=0001-Add-BumpContext-description-to-mmgr-README.patchDownload
From 62687f036cd1959db93a6b87016d28cf9d32b0bc Mon Sep 17 00:00:00 2001
From: Amul Sul <amul.sul@enterprisedb.com>
Date: Tue, 16 Apr 2024 10:37:52 +0530
Subject: [PATCH] Add BumpContext description to mmgr/README

---
 src/backend/utils/mmgr/README | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/mmgr/README b/src/backend/utils/mmgr/README
index f484f7d6f5b..d0824349b14 100644
--- a/src/backend/utils/mmgr/README
+++ b/src/backend/utils/mmgr/README
@@ -472,7 +472,7 @@ Alternative Memory Context Implementations
 ------------------------------------------
 
 aset.c is our default general-purpose implementation, working fine
-in most situations. We also have two implementations optimized for
+in most situations. We also have three implementations optimized for
 special use cases, providing either better performance or lower memory
 usage compared to aset.c (or both).
 
@@ -483,7 +483,11 @@ usage compared to aset.c (or both).
   are allocated in groups with similar lifespan (generations), or
   roughly in FIFO order.
 
-Both memory contexts aim to free memory back to the operating system
+* bump.c (BumpContext) is designed for cases that require allocating a
+  large number of short-lived chunks, none of which ever need to be
+  pfree'd or realloc'd.
+
+These three memory contexts aim to free memory back to the operating system
 (unlike aset.c, which keeps the freed chunks in a freelist, and only
 returns the memory when reset/deleted).
 
-- 
2.18.0

#55Daniel Gustafsson
daniel@yesql.se
In reply to: Amul Sul (#54)
Re: Add bump memory context type and use it for tuplesorts

On 16 Apr 2024, at 07:12, Amul Sul <sulamul@gmail.com> wrote:

Attached is a small patch adding the missing BumpContext description to the
README.

Nice catch, we should add it to the README.

+ pfree'd or realloc'd.
I think it's best to avoid mixing API:s, "pfree'd or repalloc'd" keeps it to
functions in our API instead.

--
Daniel Gustafsson

#56David Rowley
dgrowleyml@gmail.com
In reply to: Amul Sul (#54)
1 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Tue, 16 Apr 2024 at 17:13, Amul Sul <sulamul@gmail.com> wrote:

Attached is a small patch adding the missing BumpContext description to the
README.

Thanks for noticing and working on the patch.

There were a few things that were not quite accurate or are misleading:

1.

+These three memory contexts aim to free memory back to the operating system

That's not true for bump. It's the worst of the 4. Worse than aset.
It only returns memory when the context is reset/deleted.

2.

"These memory contexts were initially developed for ReorderBuffer, but
may be useful elsewhere as long as the allocation patterns match."

The above isn't true for bump. It was written for tuplesort. I think
we can just remove that part now. Slab and generation are both old
enough not to care why they were conceived.

Also since adding bump, I think the choice of which memory context to
use is about 33% harder than it used to be when there were only 3
context types. I think this warrants giving more detail on what these
3 special-purpose memory allocators are good for. I've added more
details in the attached patch. This includes more details about
freeing malloc'd blocks

I've tried to detail out enough of the specialities of the context
type without going into extensive detail. My hope is that there will
be enough detail for someone to choose the most suitable looking one
and head over to the corresponding .c file to find out more.

Is that about the right level of detail?

David

Attachments:

v2-Add-BumpContext-description-to-mmgr-README.patchtext/plain; charset=US-ASCII; name=v2-Add-BumpContext-description-to-mmgr-README.patchDownload
diff --git a/src/backend/utils/mmgr/README b/src/backend/utils/mmgr/README
index f484f7d6f5..695088bb66 100644
--- a/src/backend/utils/mmgr/README
+++ b/src/backend/utils/mmgr/README
@@ -471,25 +471,32 @@ thrashing.
 Alternative Memory Context Implementations
 ------------------------------------------
 
-aset.c is our default general-purpose implementation, working fine
-in most situations. We also have two implementations optimized for
-special use cases, providing either better performance or lower memory
-usage compared to aset.c (or both).
-
-* slab.c (SlabContext) is designed for allocations of fixed-length
-  chunks, and does not allow allocations of chunks with different size.
-
-* generation.c (GenerationContext) is designed for cases when chunks
-  are allocated in groups with similar lifespan (generations), or
-  roughly in FIFO order.
-
-Both memory contexts aim to free memory back to the operating system
-(unlike aset.c, which keeps the freed chunks in a freelist, and only
-returns the memory when reset/deleted).
-
-These memory contexts were initially developed for ReorderBuffer, but
-may be useful elsewhere as long as the allocation patterns match.
-
+aset.c (AllocSetContext) is our default general-purpose allocator.  Three other
+allocator types also exist which are special-purpose:
+
+* slab.c (SlabContext) is designed for allocations of fixed-sized
+  chunks.  The fixed chunk size must be specified when creating the context.
+  New chunks are allocated to the fullest block, keeping used chunks densely
+  packed together to avoid memory fragmentation.  This also increases the
+  chances that pfree'ing a chunk will result in a block becoming empty of all
+  chunks and allow it to be free'd back to the operating system.
+
+* generation.c (GenerationContext) is best suited for cases when chunks are
+  allocated in groups with similar lifespan (generations), or roughly in FIFO
+  order.  No attempt is made to reuse space left by pfree'd chunks.  Blocks
+  are returned to the operating system when all chunks on them have been
+  pfree'd.
+
+* bump.c (BumpContext) is best suited for use cases that require densely
+  allocated chunks of memory that never need to be individually pfree'd or
+  repalloc'd.  These operations are unsupported due to BumpContext chunks
+  having no chunk header.  No chunk header means more densely packed chunks,
+  which is especially useful for workloads that perform lots of small
+  allocations.  Blocks are only free'd back to the operating system when the
+  context is reset or deleted.
+
+For further details, please read the header comment in the corresponding .c
+file.
 
 Memory Accounting
 -----------------
#57David Rowley
dgrowleyml@gmail.com
In reply to: David Rowley (#44)
1 attachment(s)
Re: Add bump memory context type and use it for tuplesorts

On Mon, 8 Apr 2024 at 00:37, David Rowley <dgrowleyml@gmail.com> wrote:

I've now pushed all 3 patches. Thank you for all the reviews on
these and for the extra MemoryContextMethodID bit, Matthias.

I realised earlier today when working on [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=bea97cd02ebb347ab469b78673c2b33a72109669 that bump makes a pretty
brain-dead move when adding dedicated blocks to the blocks list. The
problem is that I opted to not have a current block field in
BumpContext and just rely on the head pointer of the blocks list to be
the "current" block. The head block is the block we look at to see if
we've any space left when new allocations come in. The problem there
is when adding a dedicated block in BumpAllocLarge(), the code adds
this to the head of the blocks list so that when a new allocation
comes in that's normal-sized, the block at the top of the list is full
and we have to create a new block for the allocation.

The attached fixes this by pushing these large/dedicated blocks to the
*tail* of the blocks list. This means the partially filled block
remains at the head and is available for any new allocation which will
fit. This behaviour is evident by the regression test change that I
added earlier today when working on [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=bea97cd02ebb347ab469b78673c2b33a72109669. The 2nd and smaller
allocation in that text goes onto the keeper block rather than a new
block.

I plan to push this tomorrow.

David

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=bea97cd02ebb347ab469b78673c2b33a72109669

Attachments:

v1-0001-Push-dedicated-BumpBlocks-to-the-tail-of-the-bloc.patchtext/plain; charset=US-ASCII; name=v1-0001-Push-dedicated-BumpBlocks-to-the-tail-of-the-bloc.patchDownload
From 39a60420a83e5a059de50869c0981be9732909ab Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 16 Apr 2024 22:26:00 +1200
Subject: [PATCH v1] Push dedicated BumpBlocks to the tail of the blocks list

BumpContext relies on using the head block from its 'blocks' field to
use as the current block to allocate new chunks to.  When we receive an
allocation request larger than allocChunkLimit, we place these chunks on
a new dedicated block and push this block onto the head of the 'blocks'
list.  This behavior caused the previous bump block to no longer be
available for new normal-sized (non-large) allocations and would result
in wasting space on blocks when a large request arrived before the
current block was full.

Here adjust the code to push these dedicated blocks onto the *tail* of
the blocks list so that the head block remains intact and available to
be used by normal allocation request sizes.

Discussion: https://postgr.es/m/CAApHDvp9___r-ayJj0nZ6GD3MeCGwGZ0_6ZptWpwj+zqHtmwCw@mail.gmail.com
---
 src/backend/utils/mmgr/bump.c          | 8 ++++++--
 src/test/regress/expected/sysviews.out | 2 +-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/backend/utils/mmgr/bump.c b/src/backend/utils/mmgr/bump.c
index 449bd29344..83798e2245 100644
--- a/src/backend/utils/mmgr/bump.c
+++ b/src/backend/utils/mmgr/bump.c
@@ -342,8 +342,12 @@ BumpAllocLarge(MemoryContext context, Size size, int flags)
 	randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
 #endif
 
-	/* add the block to the list of allocated blocks */
-	dlist_push_head(&set->blocks, &block->node);
+	/*
+	 * Add the block to the tail of allocated blocks list.  The current block
+	 * is left at the head of the list as it may still have space for
+	 * non-large allocations.
+	 */
+	dlist_push_tail(&set->blocks, &block->node);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 	/* Ensure any padding bytes are marked NOACCESS. */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 634dc8d8b8..2f3eb4e7f1 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -47,7 +47,7 @@ select name, parent, total_bytes > 0, total_nblocks, free_bytes > 0, free_chunks
 from pg_backend_memory_contexts where name = 'Caller tuples';
      name      |     parent     | ?column? | total_nblocks | ?column? | free_chunks 
 ---------------+----------------+----------+---------------+----------+-------------
- Caller tuples | TupleSort sort | t        |             3 | t        |           0
+ Caller tuples | TupleSort sort | t        |             2 | t        |           0
 (1 row)
 
 rollback;
-- 
2.40.1

#58Amul Sul
sulamul@gmail.com
In reply to: David Rowley (#56)
Re: Add bump memory context type and use it for tuplesorts

On Tue, Apr 16, 2024 at 3:44 PM David Rowley <dgrowleyml@gmail.com> wrote:

On Tue, 16 Apr 2024 at 17:13, Amul Sul <sulamul@gmail.com> wrote:

Attached is a small patch adding the missing BumpContext description to

the

README.

Thanks for noticing and working on the patch.

There were a few things that were not quite accurate or are misleading:

1.

+These three memory contexts aim to free memory back to the operating

system

That's not true for bump. It's the worst of the 4. Worse than aset.
It only returns memory when the context is reset/deleted.

2.

"These memory contexts were initially developed for ReorderBuffer, but
may be useful elsewhere as long as the allocation patterns match."

The above isn't true for bump. It was written for tuplesort. I think
we can just remove that part now. Slab and generation are both old
enough not to care why they were conceived.

Also since adding bump, I think the choice of which memory context to
use is about 33% harder than it used to be when there were only 3
context types. I think this warrants giving more detail on what these
3 special-purpose memory allocators are good for. I've added more
details in the attached patch. This includes more details about
freeing malloc'd blocks

I've tried to detail out enough of the specialities of the context
type without going into extensive detail. My hope is that there will
be enough detail for someone to choose the most suitable looking one
and head over to the corresponding .c file to find out more.

Is that about the right level of detail?

Yes, it looks much better now, thank you.

Regards,
Amul