pg_lzcompress.patch

text/plain

Filename: pg_lzcompress.patch
Type: text/plain
Part: 0
Message: Improve compression speeds in pg_lzcompress.c

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/utils/adt/pg_lzcompress.c 87 26
src/include/utils/pg_lzcompress.h 2 2
diff --git src/backend/utils/adt/pg_lzcompress.c src/backend/utils/adt/pg_lzcompress.c
index 466982e..3980334 100644
--- src/backend/utils/adt/pg_lzcompress.c
+++ src/backend/utils/adt/pg_lzcompress.c
@@ -62,8 +62,10 @@
  *			Otherwise the first byte after the header tells what to do
  *			the next 8 times. We call this the control byte.
  *
- *			An unset bit in the control byte means, that one uncompressed
- *			byte follows, which is copied from input to output.
+ *			An unset bit in the control byte means, that one byte header
+ *			and uncompressed 1-255 literals follow. The header includes
+ *			the length of the literals, and the literals are just copied
+ *			from input to output.
  *
  *			A set bit in the control byte means, that a tag of 2-3 bytes
  *			follows. A tag contains information to copy some bytes, that
@@ -184,6 +186,8 @@
 #define PGLZ_HISTORY_MASK		(PGLZ_HISTORY_LISTS - 1)
 #define PGLZ_HISTORY_SIZE		4096
 #define PGLZ_MAX_MATCH			273
+#define PGLZ_SKIP_SIZE			3
+#define PGLZ_HASH_GAP			8
 
 
 /* ----------
@@ -322,16 +326,20 @@ do { \
 
 
 /* ----------
- * pglz_out_literal -
+ * pglz_out_literals -
  *
  *		Outputs a literal byte to the destination buffer including the
  *		appropriate control bit.
+ *		Outputs multiple literals from the source to the destination
+ *		buffer including the appropriate control bit.
  * ----------
  */
-#define pglz_out_literal(_ctrlp,_ctrlb,_ctrl,_buf,_byte) \
+#define pglz_out_literals(_ctrlp,_ctrlb,_ctrl,_buf,_sp,_len) \
 do { \
 	pglz_out_ctrl(_ctrlp,_ctrlb,_ctrl,_buf);								\
-	*(_buf)++ = (unsigned char)(_byte);										\
+	*(_buf)++ = (unsigned char)(_len);										\
+	memcpy(_buf, _sp, _len);												\
+	_buf += _len;															\
 	_ctrl <<= 1;															\
 } while (0)
 
@@ -468,6 +476,12 @@ pglz_find_match(PGLZ_HistEntry **hstart, const char *input, const char *end,
 		return 1;
 	}
 
+	/*
+	 * This match information decides not to output a backward
+	 * reference tag.
+	 */
+	*lenp = 0;
+
 	return 0;
 }
 
@@ -586,37 +600,81 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
 			return false;
 
 		/*
-		 * Try to find a match in the history
+		 * Remember a base position. A loop below skips some literals
+		 * that might be incompressible, and the literals from the
+		 * base to a next match are just copied to the output buffer.
 		 */
-		if (pglz_find_match(hist_start, dp, dend, &match_len,
-							&match_off, good_match, good_drop))
+		const char *anchor = dp;
+
+		do {
+			/*
+			 * Try to find a match in the history
+			 */
+			if (pglz_find_match(hist_start, dp, dend, &match_len,
+								&match_off, good_match, good_drop))
+			{
+				/*
+				 * Check literals backward to find as many matches
+				 * as possible.
+				 */
+				while (dp > anchor && dp[-1] == dp[-match_off - 1] &&
+					   match_off < 0x0fff && match_len < PGLZ_MAX_MATCH)
+				{
+					dp--;
+					match_len++;
+				}
+
+				found_match = true;
+				break;
+			}
+
+			pglz_hist_add(hist_start, hist_entries,
+						  hist_next, hist_recycle,
+						  dp, dend);
+
+			/*
+			 * Skip PGLZ_SKIP_SIZE literals.
+			 */
+			dp += PGLZ_SKIP_SIZE;
+
+			if (dp >= dend) {
+				dp = dend;
+				break;
+			}
+		} while (dp - anchor < CHAR_MAX - PGLZ_SKIP_SIZE);
+
+		if (dp != anchor)
+		{
+			/*
+			 * No match found. Copy skipped multiple literals.
+			 */
+			int32 len = dp - anchor;
+			pglz_out_literals(ctrlp, ctrlb, ctrl, bp, anchor, len);
+		}
+
+		if (match_len > 0)
 		{
 			/*
 			 * Create the tag and add history entries for all matched
 			 * characters.
 			 */
 			pglz_out_tag(ctrlp, ctrlb, ctrl, bp, match_len, match_off);
-			while (match_len--)
+
+			/*
+			 * Skip hash updates by PGLZ_HASH_GAP. As the updates eat
+			 * much processor time, the large skip improves compression
+			 * speeds with little loss of compression ratios.
+			 */
+			const char *hashp = dp;
+
+			for (; hashp < dp + match_len; hashp += PGLZ_HASH_GAP)
 			{
 				pglz_hist_add(hist_start, hist_entries,
 							  hist_next, hist_recycle,
-							  dp, dend);
-				dp++;			/* Do not do this ++ in the line above! */
-				/* The macro would do it four times - Jan.	*/
+							  hashp, dend);
 			}
-			found_match = true;
-		}
-		else
-		{
-			/*
-			 * No match found. Copy one literal byte.
-			 */
-			pglz_out_literal(ctrlp, ctrlb, ctrl, bp, *dp);
-			pglz_hist_add(hist_start, hist_entries,
-						  hist_next, hist_recycle,
-						  dp, dend);
-			dp++;				/* Do not do this ++ in the line above! */
-			/* The macro would do it four times - Jan.	*/
+
+			dp += match_len;
 		}
 	}
 
@@ -720,7 +778,10 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
 				if (dp >= destend)		/* check for buffer overrun */
 					break;		/* do not clobber memory */
 
-				*dp++ = *sp++;
+				int32 len = *sp++;
+				memcpy(dp, sp, len);
+				sp += len;
+				dp += len;
 			}
 
 			/*
diff --git src/include/utils/pg_lzcompress.h src/include/utils/pg_lzcompress.h
index 4af24a3..5e5ab59 100644
--- src/include/utils/pg_lzcompress.h
+++ src/include/utils/pg_lzcompress.h
@@ -28,10 +28,10 @@ typedef struct PGLZ_Header
  * PGLZ_MAX_OUTPUT -
  *
  *		Macro to compute the buffer size required by pglz_compress().
- *		We allow 4 bytes for overrun before detecting compression failure.
+ *		We allow 259 bytes for overrun before detecting compression failure.
  * ----------
  */
-#define PGLZ_MAX_OUTPUT(_dlen)			((_dlen) + 4 + sizeof(PGLZ_Header))
+#define PGLZ_MAX_OUTPUT(_dlen)			((_dlen) + 259 + sizeof(PGLZ_Header))
 
 /* ----------
  * PGLZ_RAW_SIZE -