parallel-checksum.patch
application/octet-stream
Filename: parallel-checksum.patch
Type: application/octet-stream
Part: 0
Message:
Re: Enabling Checksums
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: context
| File | + | − |
|---|---|---|
| src/backend/storage/page/bufpage.c | 30 | 0 |
*** a/src/backend/storage/page/bufpage.c
--- b/src/backend/storage/page/bufpage.c
***************
*** 928,1011 **** PageSetChecksumInplace(Page page, BlockNumber blkno)
/*
* Calculate checksum for a PostgreSQL Page. This includes the page number (to
* detect the case when a page is somehow moved to a different location), the
* page header (excluding the checksum itself), and the page data.
*
! * The checksum algorithm is a modified Fletcher 64-bit (which is
! * order-sensitive). The modification is because, at the end, we have two
! * 64-bit sums, but we only have room for a 16-bit checksum. So, instead of
! * using a modulus of 2^32 - 1, we use 2^8 - 1; making it also resemble a
! * Fletcher 16-bit. We don't use Fletcher 16-bit directly, because processing
! * single bytes at a time is slower.
*/
static uint16
PageCalcChecksum16(Page page, BlockNumber blkno)
{
! PageHeaderData header_copy;
! uint32 *ptr32Header = (uint32 *) &header_copy;
! uint32 *ptr32Page = (uint32 *) page;
! int64 sum1 = 0;
! int64 sum2 = 0;
uint16 checksum = 0;
uint8 *p8Checksum = (uint8 *) &checksum;
! int i;
/* only calculate the checksum for properly-initialized pages */
Assert(!PageIsNew(page));
/*
* Initialize the checksum calculation with the page number. This helps
* catch corruption from whole pages being transposed with other whole
* pages.
*/
! sum1 = sum2 = (uint64) blkno;
/*
! * Make a copy of the page header and set the checksum to zero in the
! * copy. That allows us to calculate the checksum 32 bits at a time while
! * ignoring only the checksum field during calculation.
*/
! memcpy(&header_copy, page, SizeOfPageHeaderData);
! header_copy.pd_checksum = 0;
!
! /* compute the checksum of the header */
! for (i = 0; i < SizeOfPageHeaderData / sizeof(uint32); i++)
! {
! sum1 += ptr32Header[i];
! sum2 += sum1;
! }
/* now checksum the rest of the page */
! for (i = SizeOfPageHeaderData; i < BLCKSZ / sizeof(uint32); i++)
! {
! sum1 += ptr32Page[i];
! sum2 += sum1;
! /*
! * Testing for overflow makes the algorithm slower, but we know that
! * overflow won't happen, so only use an Assert. The overflow won't
! * happen because sum2 (the larger sum) can grow to a maximum of:
! *
! * 2^32 * (N^2 - N)/2
! *
! * where N is the number of iterations of this loop. The largest block
! * size is 32KB, which is 8192 iterations, which yields a number less
! * than 2^61, which is still within the range of a signed int64.
! */
! Assert(BLCKSZ <= 32768 && sum1 >=0 && sum2 >= 0);
! }
/*
* Store the sums as bytes in the checksum. Mod 255 is used instead of 256
* because 256 would miss errors in the high 24 bits of a 32-bit word read
* from the page. We add one to shift the range from 0..255 to 1..256, to
* make zero invalid for checksum bytes (which seems wise).
*/
! p8Checksum[0] = (sum1 % 255) + 1;
! p8Checksum[1] = (sum2 % 255) + 1;
#ifdef DEBUG_CHECKSUM
elog(LOG, "checksum %u", checksum);
#endif
return checksum;
--- 928,997 ----
/*
* Calculate checksum for a PostgreSQL Page. This includes the page number (to
* detect the case when a page is somehow moved to a different location), the
* page header (excluding the checksum itself), and the page data.
*
! * The checksum algorithm is to accumulate 64 parallel 16bit checksum values
! * using the evolution function value = old_value * prime + data. The parallel
! * accumulation is done to cut data dependencies and enable vectorization of
! * the inner loop. Without vector instructions this code is likely to be
! * load/store bound.
*/
+ #define N_SUMS 64
+ #define HASH_PRIME 31
+ #define PageChecksumOffset (offsetof(PageHeaderData, pd_checksum))
+
static uint16
PageCalcChecksum16(Page page, BlockNumber blkno)
{
! uint16 (*pageArray)[N_SUMS] = (uint16 (*)[N_SUMS]) page;
! uint16 sums[N_SUMS];
! uint32 combined_sum;
uint16 checksum = 0;
uint8 *p8Checksum = (uint8 *) &checksum;
! int i, j;
/* only calculate the checksum for properly-initialized pages */
Assert(!PageIsNew(page));
/*
* Initialize the checksum calculation with the page number. This helps
* catch corruption from whole pages being transposed with other whole
* pages.
*/
! combined_sum = blkno;
!
! /* First iteration needs to consider the checksum position as zero */
! for (i = 0; i < N_SUMS; i++)
! sums[i] = (i == PageChecksumOffset / sizeof(uint16)) ? 0 : pageArray[0][i];
/*
! * As buffer size is a power of two we can safely do it a smaller power
! * of two steps a time.
*/
! Assert(BLCKSZ / sizeof(uint16) % N_SUMS == 0);
/* now checksum the rest of the page */
! for (i = 1; i < BLCKSZ / sizeof(uint16) / N_SUMS; i++)
! for (j = 0; j < N_SUMS; j++)
! /* Written as two separate lines to unconfuse gcc */
! sums[j] = sums[j]*HASH_PRIME + pageArray[i][j];
! /* combine the separate sums into a single value */
! for (i = 0; i < N_SUMS; i++)
! combined_sum = combined_sum*HASH_PRIME + sums[i];
/*
* Store the sums as bytes in the checksum. Mod 255 is used instead of 256
* because 256 would miss errors in the high 24 bits of a 32-bit word read
* from the page. We add one to shift the range from 0..255 to 1..256, to
* make zero invalid for checksum bytes (which seems wise).
*/
! p8Checksum[0] = (combined_sum % 255) + 1;
! p8Checksum[1] = (combined_sum*HASH_PRIME % 255) + 1;
#ifdef DEBUG_CHECKSUM
elog(LOG, "checksum %u", checksum);
#endif
return checksum;