Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 68689c6

Browse files
committed
Micro-optimize GenericXLogFinish().
Make the inner comparison loops of computeDelta() as tight as possible by pulling considerations of valid and invalid ranges out of the inner loops, and extending a match or non-match detection as far as possible before deciding what to do next. To keep this tractable, give up the possibility of merging fragments across the pd_lower to pd_upper gap. The fraction of pages where that could happen (ie, there are 4 or fewer bytes in the gap, *and* data changes immediately adjacent to it on both sides) is too small to be worth spending cycles on. Also, avoid two BLCKSZ-length memcpy()s by computing the delta before moving data into the target buffer, instead of after. This doesn't save nearly as many cycles as being tenser about computeDelta(), but it still seems worth doing. On my machine, this patch cuts a full 40% off the runtime of contrib/bloom's regression test.
1 parent c7a141a commit 68689c6

File tree

1 file changed

+138
-80
lines changed

1 file changed

+138
-80
lines changed

src/backend/access/transam/generic_xlog.c

+138-80
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,17 @@
3434
* the delta. For this reason, we merge adjacent fragments if the unchanged
3535
* region between them is <= MATCH_THRESHOLD bytes.
3636
*
37-
* The worst case for delta sizes occurs when we did not find any unchanged
38-
* region in the page. The size of the delta will be the size of the page plus
39-
* the size of the fragment header in that case.
37+
* We do not bother to merge fragments across the "lower" and "upper" parts
38+
* of a page; it's very seldom the case that pd_lower and pd_upper are within
39+
* MATCH_THRESHOLD bytes of each other, and handling that infrequent case
40+
* would complicate and slow down the delta-computation code unduly.
41+
* Therefore, the worst-case delta size includes two fragment headers plus
42+
* a full page's worth of data.
4043
*-------------------------------------------------------------------------
4144
*/
4245
#define FRAGMENT_HEADER_SIZE (2 * sizeof(OffsetNumber))
4346
#define MATCH_THRESHOLD FRAGMENT_HEADER_SIZE
44-
#define MAX_DELTA_SIZE (BLCKSZ + FRAGMENT_HEADER_SIZE)
47+
#define MAX_DELTA_SIZE (BLCKSZ + 2 * FRAGMENT_HEADER_SIZE)
4548

4649
/* Struct of generic xlog data for single page */
4750
typedef struct
@@ -62,7 +65,11 @@ struct GenericXLogState
6265

6366
static void writeFragment(PageData *pageData, OffsetNumber offset,
6467
OffsetNumber len, const char *data);
65-
static void computeDelta(PageData *pageData);
68+
static void computeRegionDelta(PageData *pageData,
69+
const char *curpage, const char *targetpage,
70+
int targetStart, int targetEnd,
71+
int validStart, int validEnd);
72+
static void computeDelta(PageData *pageData, Page curpage, Page targetpage);
6673
static void applyPageRedo(Page page, const char *delta, Size deltaSize);
6774

6875

@@ -94,102 +101,155 @@ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
94101
}
95102

96103
/*
97-
* Compute the delta record for given page.
104+
* Compute the XLOG fragments needed to transform a region of curpage into the
105+
* corresponding region of targetpage, and append them to pageData's delta
106+
* field. The region to transform runs from targetStart to targetEnd-1.
107+
* Bytes in curpage outside the range validStart to validEnd-1 should be
108+
* considered invalid, and always overwritten with target data.
109+
*
110+
* This function is a hot spot, so it's worth being as tense as possible
111+
* about the data-matching loops.
98112
*/
99113
static void
100-
computeDelta(PageData *pageData)
114+
computeRegionDelta(PageData *pageData,
115+
const char *curpage, const char *targetpage,
116+
int targetStart, int targetEnd,
117+
int validStart, int validEnd)
101118
{
102-
Page page = BufferGetPage(pageData->buffer, NULL, NULL,
103-
BGP_NO_SNAPSHOT_TEST),
104-
image = (Page) pageData->image;
105119
int i,
120+
loopEnd,
106121
fragmentBegin = -1,
107122
fragmentEnd = -1;
108-
uint16 pageLower = ((PageHeader) page)->pd_lower,
109-
pageUpper = ((PageHeader) page)->pd_upper,
110-
imageLower = ((PageHeader) image)->pd_lower,
111-
imageUpper = ((PageHeader) image)->pd_upper;
112-
113-
pageData->deltaLen = 0;
114123

115-
for (i = 0; i < BLCKSZ; i++)
124+
/* Deal with any invalid start region by including it in first fragment */
125+
if (validStart > targetStart)
116126
{
117-
bool match;
118-
119-
/*
120-
* Check if bytes in old and new page images match. We do not care
121-
* about data in the unallocated area between pd_lower and pd_upper.
122-
* We assume the unallocated area to expand with unmatched bytes.
123-
* Bytes inside the unallocated area are assumed to always match.
124-
*/
125-
if (i < pageLower)
126-
{
127-
if (i < imageLower)
128-
match = (page[i] == image[i]);
129-
else
130-
match = false;
131-
}
132-
else if (i >= pageUpper)
133-
{
134-
if (i >= imageUpper)
135-
match = (page[i] == image[i]);
136-
else
137-
match = false;
138-
}
139-
else
140-
{
141-
match = true;
142-
}
127+
fragmentBegin = targetStart;
128+
targetStart = validStart;
129+
}
143130

144-
if (match)
145-
{
146-
if (fragmentBegin >= 0)
147-
{
148-
/* Matched byte is potentially part of a fragment. */
149-
if (fragmentEnd < 0)
150-
fragmentEnd = i;
131+
/* We'll deal with any invalid end region after the main loop */
132+
loopEnd = Min(targetEnd, validEnd);
151133

152-
/*
153-
* Write next fragment if sequence of matched bytes is longer
154-
* than MATCH_THRESHOLD.
155-
*/
156-
if (i - fragmentEnd >= MATCH_THRESHOLD)
157-
{
158-
writeFragment(pageData, fragmentBegin,
159-
fragmentEnd - fragmentBegin,
160-
page + fragmentBegin);
161-
fragmentBegin = -1;
162-
fragmentEnd = -1;
163-
}
164-
}
165-
}
166-
else
134+
/* Examine all the potentially matchable bytes */
135+
i = targetStart;
136+
while (i < loopEnd)
137+
{
138+
if (curpage[i] != targetpage[i])
167139
{
168-
/* On unmatched byte, start new fragment if it is not done yet */
140+
/* On unmatched byte, start new fragment if not already in one */
169141
if (fragmentBegin < 0)
170142
fragmentBegin = i;
143+
/* Mark unmatched-data endpoint as uncertain */
171144
fragmentEnd = -1;
145+
/* Extend the fragment as far as possible in a tight loop */
146+
i++;
147+
while (i < loopEnd && curpage[i] != targetpage[i])
148+
i++;
149+
if (i >= loopEnd)
150+
break;
151+
}
152+
153+
/* Found a matched byte, so remember end of unmatched fragment */
154+
fragmentEnd = i;
155+
156+
/*
157+
* Extend the match as far as possible in a tight loop. (On typical
158+
* workloads, this inner loop is the bulk of this function's runtime.)
159+
*/
160+
i++;
161+
while (i < loopEnd && curpage[i] == targetpage[i])
162+
i++;
163+
164+
/*
165+
* There are several possible cases at this point:
166+
*
167+
* 1. We have no unwritten fragment (fragmentBegin < 0). There's
168+
* nothing to write; and it doesn't matter what fragmentEnd is.
169+
*
170+
* 2. We found more than MATCH_THRESHOLD consecutive matching bytes.
171+
* Dump out the unwritten fragment, stopping at fragmentEnd.
172+
*
173+
* 3. The match extends to loopEnd. We'll do nothing here, exit the
174+
* loop, and then dump the unwritten fragment, after merging it with
175+
* the invalid end region if any. If we don't so merge, fragmentEnd
176+
* establishes how much the final writeFragment call needs to write.
177+
*
178+
* 4. We found an unmatched byte before loopEnd. The loop will repeat
179+
* and will enter the unmatched-byte stanza above. So in this case
180+
* also, it doesn't matter what fragmentEnd is. The matched bytes
181+
* will get merged into the continuing unmatched fragment.
182+
*
183+
* Only in case 3 do we reach the bottom of the loop with a meaningful
184+
* fragmentEnd value, which is why it's OK that we unconditionally
185+
* assign "fragmentEnd = i" above.
186+
*/
187+
if (fragmentBegin >= 0 && i - fragmentEnd > MATCH_THRESHOLD)
188+
{
189+
writeFragment(pageData, fragmentBegin,
190+
fragmentEnd - fragmentBegin,
191+
targetpage + fragmentBegin);
192+
fragmentBegin = -1;
193+
fragmentEnd = -1; /* not really necessary */
172194
}
173195
}
174196

197+
/* Deal with any invalid end region by including it in final fragment */
198+
if (loopEnd < targetEnd)
199+
{
200+
if (fragmentBegin < 0)
201+
fragmentBegin = loopEnd;
202+
fragmentEnd = targetEnd;
203+
}
204+
205+
/* Write final fragment if any */
175206
if (fragmentBegin >= 0)
207+
{
208+
if (fragmentEnd < 0)
209+
fragmentEnd = targetEnd;
176210
writeFragment(pageData, fragmentBegin,
177-
BLCKSZ - fragmentBegin,
178-
page + fragmentBegin);
211+
fragmentEnd - fragmentBegin,
212+
targetpage + fragmentBegin);
213+
}
214+
}
215+
216+
/*
217+
* Compute the XLOG delta record needed to transform curpage into targetpage,
218+
* and store it in pageData's delta field.
219+
*/
220+
static void
221+
computeDelta(PageData *pageData, Page curpage, Page targetpage)
222+
{
223+
int targetLower = ((PageHeader) targetpage)->pd_lower,
224+
targetUpper = ((PageHeader) targetpage)->pd_upper,
225+
curLower = ((PageHeader) curpage)->pd_lower,
226+
curUpper = ((PageHeader) curpage)->pd_upper;
227+
228+
pageData->deltaLen = 0;
229+
230+
/* Compute delta records for lower part of page ... */
231+
computeRegionDelta(pageData, curpage, targetpage,
232+
0, targetLower,
233+
0, curLower);
234+
/* ... and for upper part, ignoring what's between */
235+
computeRegionDelta(pageData, curpage, targetpage,
236+
targetUpper, BLCKSZ,
237+
curUpper, BLCKSZ);
179238

180239
/*
181240
* If xlog debug is enabled, then check produced delta. Result of delta
182-
* application to saved image should be the same as current page state.
241+
* application to curpage should be equivalent to targetpage.
183242
*/
184243
#ifdef WAL_DEBUG
185244
if (XLOG_DEBUG)
186245
{
187246
char tmp[BLCKSZ];
188247

189-
memcpy(tmp, image, BLCKSZ);
248+
memcpy(tmp, curpage, BLCKSZ);
190249
applyPageRedo(tmp, pageData->delta, pageData->deltaLen);
191-
if (memcmp(tmp, page, pageLower) != 0 ||
192-
memcmp(tmp + pageUpper, page + pageUpper, BLCKSZ - pageUpper) != 0)
250+
if (memcmp(tmp, targetpage, targetLower) != 0 ||
251+
memcmp(tmp + targetUpper, targetpage + targetUpper,
252+
BLCKSZ - targetUpper) != 0)
193253
elog(ERROR, "result of generic xlog apply does not match");
194254
}
195255
#endif
@@ -264,7 +324,7 @@ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
264324
XLogRecPtr
265325
GenericXLogFinish(GenericXLogState *state)
266326
{
267-
XLogRecPtr lsn = InvalidXLogRecPtr;
327+
XLogRecPtr lsn;
268328
int i;
269329

270330
if (state->isLogged)
@@ -278,22 +338,17 @@ GenericXLogFinish(GenericXLogState *state)
278338
{
279339
PageData *pageData = &state->pages[i];
280340
Page page;
281-
char tmp[BLCKSZ];
282341

283342
if (BufferIsInvalid(pageData->buffer))
284343
continue;
285344

286345
page = BufferGetPage(pageData->buffer, NULL, NULL,
287346
BGP_NO_SNAPSHOT_TEST);
288347

289-
/* Swap current and saved page image. */
290-
memcpy(tmp, pageData->image, BLCKSZ);
291-
memcpy(pageData->image, page, BLCKSZ);
292-
memcpy(page, tmp, BLCKSZ);
293-
294348
if (pageData->fullImage)
295349
{
296350
/* A full page image does not require anything special */
351+
memcpy(page, pageData->image, BLCKSZ);
297352
XLogRegisterBuffer(i, pageData->buffer, REGBUF_FORCE_IMAGE);
298353
}
299354
else
@@ -302,8 +357,9 @@ GenericXLogFinish(GenericXLogState *state)
302357
* In normal mode, calculate delta and write it as xlog data
303358
* associated with this page.
304359
*/
360+
computeDelta(pageData, page, (Page) pageData->image);
361+
memcpy(page, pageData->image, BLCKSZ);
305362
XLogRegisterBuffer(i, pageData->buffer, REGBUF_STANDARD);
306-
computeDelta(pageData);
307363
XLogRegisterBufData(i, pageData->delta, pageData->deltaLen);
308364
}
309365
}
@@ -341,6 +397,8 @@ GenericXLogFinish(GenericXLogState *state)
341397
MarkBufferDirty(pageData->buffer);
342398
}
343399
END_CRIT_SECTION();
400+
/* We don't have a LSN to return, in this case */
401+
lsn = InvalidXLogRecPtr;
344402
}
345403

346404
pfree(state);

0 commit comments

Comments
 (0)