Hi hackers,

I did some experiments with this patch, after previous discussions. This
probably does not answer all questions, but would be happy to do more if
needed.

First, I updated the patch according to what suggested here [1]. PSA  v2.
I tweaked the master branch a bit to not allow any buffering. I compared
HEAD, this patch and no buffering at all.
I also added a simple GUC to control PqSendBufferSize, this change only
allows to modify the buffer size and should not have any impact on
performance.

I again ran the COPY TO STDOUT command and timed it. AFAIU COPY sends data
row by row, and I tried running the command under different scenarios with
different # of rows and row sizes. You can find the test script attached
(see test.sh).
All timings are in ms.

1- row size = 100 bytes, # of rows = 1000000
┌───────────┬────────────┬──────┬──────┬──────┬──────┬──────┐
│           │ 1400 bytes │ 2KB  │ 4KB  │ 8KB  │ 16KB │ 32KB │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ HEAD      │ 1036       │ 998  │ 940  │ 910  │ 894  │ 874  │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ patch     │ 1107       │ 1032 │ 980  │ 957  │ 917  │ 909  │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ no buffer │ 6230       │ 6125 │ 6282 │ 6279 │ 6255 │ 6221 │
└───────────┴────────────┴──────┴──────┴──────┴──────┴──────┘

2-  row size = half of the rows are 1KB and rest is 10KB , # of rows =
1000000
┌───────────┬────────────┬───────┬───────┬───────┬───────┬───────┐
│           │ 1400 bytes │ 2KB   │ 4KB   │ 8KB   │ 16KB  │ 32KB  │
├───────────┼────────────┼───────┼───────┼───────┼───────┼───────┤
│ HEAD      │ 25197      │ 23414 │ 20612 │ 19206 │ 18334 │ 18033 │
├───────────┼────────────┼───────┼───────┼───────┼───────┼───────┤
│ patch     │ 19843      │ 19889 │ 19798 │ 19129 │ 18578 │ 18260 │
├───────────┼────────────┼───────┼───────┼───────┼───────┼───────┤
│ no buffer │ 23752      │ 23565 │ 23602 │ 23622 │ 23541 │ 23599 │
└───────────┴────────────┴───────┴───────┴───────┴───────┴───────┘

3-  row size = half of the rows are 1KB and rest is 1MB , # of rows = 1000
┌───────────┬────────────┬──────┬──────┬──────┬──────┬──────┐
│           │ 1400 bytes │ 2KB  │ 4KB  │ 8KB  │ 16KB │ 32KB │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ HEAD      │ 3137       │ 2937 │ 2687 │ 2551 │ 2456 │ 2465 │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ patch     │ 2399       │ 2390 │ 2402 │ 2415 │ 2417 │ 2422 │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ no buffer │ 2417       │ 2414 │ 2429 │ 2418 │ 2435 │ 2404 │
└───────────┴────────────┴──────┴──────┴──────┴──────┴──────┘

3-  row size = all rows are 1MB , # of rows = 1000
┌───────────┬────────────┬──────┬──────┬──────┬──────┬──────┐
│           │ 1400 bytes │ 2KB  │ 4KB  │ 8KB  │ 16KB │ 32KB │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ HEAD      │ 6113       │ 5764 │ 5281 │ 5009 │ 4885 │ 4872 │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ patch     │ 4759       │ 4754 │ 4754 │ 4758 │ 4782 │ 4805 │
├───────────┼────────────┼──────┼──────┼──────┼──────┼──────┤
│ no buffer │ 4756       │ 4774 │ 4793 │ 4766 │ 4770 │ 4774 │
└───────────┴────────────┴──────┴──────┴──────┴──────┴──────┘

Some quick observations:
1- Even though I expect both the patch and HEAD behave similarly in case of
small data (case 1: 100 bytes), the patch runs slightly slower than HEAD.
2- In cases where the data does not fit into the buffer, the patch starts
performing better than HEAD. For example, in case 2, patch seems faster
until the buffer size exceeds the data length. When the buffer size is set
to something larger than 10KB (16KB/32KB in this case), there is again a
slight performance loss with the patch as in case 1.
3- With large row sizes (i.e. sizes that do not fit into the buffer) not
buffering at all starts performing better than HEAD. Similarly the patch
performs better too as it stops buffering if data does not fit into the
buffer.



[1]
https://www.postgresql.org/message-id/CAGECzQTYUhnC1bO%3DzNiSpUgCs%3DhCYxVHvLD2doXNx3My6ZAC2w%40mail.gmail.com


Thanks,
-- 
Melih Mutlu
Microsoft
From 7f1b72a0948156f8e35ce3b07b5e763a5578d641 Mon Sep 17 00:00:00 2001
From: Melih Mutlu <m.melihmutlu@gmail.com>
Date: Mon, 26 Feb 2024 14:41:35 +0300
Subject: [PATCH] Added pq_send_buffer_size GUC

---
 src/backend/libpq/pqcomm.c          |  2 +-
 src/backend/utils/misc/guc_tables.c | 11 +++++++++++
 src/include/libpq/libpq.h           |  1 +
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index c606bf3447..92708e46e6 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -116,7 +116,7 @@ static List *sock_paths = NIL;
  * enlarged by pq_putmessage_noblock() if the message doesn't fit otherwise.
  */
 
-#define PQ_SEND_BUFFER_SIZE 8192
+int PQ_SEND_BUFFER_SIZE = 8192;
 #define PQ_RECV_BUFFER_SIZE 8192
 
 static char *PqSendBuffer;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 527a2b2734..1f198b19ca 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -3595,6 +3595,17 @@ struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"pq_send_buffer_size", PGC_USERSET, DEVELOPER_OPTIONS,
+			gettext_noop("Sets the PqSendBufferSize"),
+			NULL,
+			GUC_NOT_IN_SAMPLE | GUC_UNIT_BYTE
+		},
+		&PQ_SEND_BUFFER_SIZE,
+		8192, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 6171a0d17a..37e91008fb 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -123,6 +123,7 @@ extern PGDLLIMPORT char *SSLECDHCurve;
 extern PGDLLIMPORT bool SSLPreferServerCiphers;
 extern PGDLLIMPORT int ssl_min_protocol_version;
 extern PGDLLIMPORT int ssl_max_protocol_version;
+extern PGDLLIMPORT int PQ_SEND_BUFFER_SIZE;
 
 enum ssl_protocol_versions
 {
-- 
2.34.1

#!/bin/bash

DESTDIR="/home/melih-desktop/code/build/"
TESTDIR="/home/melih-desktop/pqcomm_test"

test_cases=(
"100 100 1000000"		# only 100 bytes	
"1024 10240 1000000"    # 1Kb and 10Kb 
"1024 1048576 1000"		# 1Kb and 1Mb 
"1048576 1048576 1000"	# all 1Mb
)

branches=(
"master" 
"pqcomm_buf"
"pqcomm_no_buffer"
)


buffer_sizes=(
1400
2048
4096
8192
16384
32768
)

insert_rows(){
	psql -d postgres -p 5432 -c "
	DO \$\$
	DECLARE
	    counter INT;
	BEGIN
	    FOR counter IN 1..$3 LOOP
	        IF counter % 2 = 1 THEN
	            INSERT INTO test_table VALUES (repeat('a', $1)::text);
	        ELSE
	            INSERT INTO test_table VALUES (repeat('b', $2)::text);
	        END IF;
	    END LOOP;
	END \$\$;
	"
}


for branch in "${branches[@]}"
do

git checkout $branch
	rm -rf $DESTDIR

	meson setup --buildtype debug -Dcassert=true --prefix="$DESTDIR/usr/local/pgsql" $DESTDIR && \
	ninja -C $DESTDIR && \
	meson install --quiet -C $DESTDIR

	for case in "${test_cases[@]}" 
	do
		pkill postgres
		rm -rf $TESTDIR/test
		initdb -D $TESTDIR/test
		pg_ctl -D $TESTDIR/test -l $TESTDIR/logfile start

		psql -d postgres -p 5432 -c "CREATE TABLE test_table(data text not null);"
		insert_rows $case

		for buf_size in "${buffer_sizes[@]}"
		do

			echo "pq_send_buffer_size = ${buf_size}" >> $TESTDIR/test/postgresql.conf
			pg_ctl -D $TESTDIR/test -l $TESTDIR/logfile restart


			elapsed_time=0
			for a in {1..3}
			do
				echo "Run ${a}"

				echo 3 | sudo tee /proc/sys/vm/drop_caches

				pg_ctl -D $TESTDIR/test -l $TESTDIR/logfile restart

				start_time=$(date +%s%N)
				psql -d postgres -p 5432 -c "COPY test_table TO STDOUT;" 1> /dev/null
				end_time=$(date +%s%N)

				elapsed_time=$(( (end_time - start_time) + elapsed_time))

			done

			avg_elapsed_time_in_ms=$((elapsed_time / 3 / 1000000))
			echo -e "${branch}-${case}-${buf_size} : ${avg_elapsed_time_in_ms}" >> $TESTDIR/results_tiny.txt

		done

		pg_ctl -D $TESTDIR/test -l $TESTDIR/logfile stop
	done
done

Attachment: v2-0001-Flush-large-data-immediately-in-pqcomm.patch
Description: Binary data

Reply via email to