30
30
* For simplicity, we allocate and release space in the underlying file
31
31
* in BLCKSZ-size blocks. Space allocation boils down to keeping track
32
32
* of which blocks in the underlying file belong to which logical tape,
33
- * plus any blocks that are free (recycled and not yet reused). Normally
34
- * there are not very many free blocks, so we just keep those in a list.
33
+ * plus any blocks that are free (recycled and not yet reused).
35
34
* The blocks in each logical tape are remembered using a method borrowed
36
35
* from the Unix HFS filesystem: we store data block numbers in an
37
36
* "indirect block". If an indirect block fills up, we write it out to
53
52
* not clear this helps much, but it can't hurt. (XXX perhaps a LIFO
54
53
* policy for free blocks would be better?)
55
54
*
55
+ * To support the above policy of writing to the lowest free block,
56
+ * ltsGetFreeBlock sorts the list of free block numbers into decreasing
57
+ * order each time it is asked for a block and the list isn't currently
58
+ * sorted. This is an efficient way to handle it because we expect cycles
59
+ * of releasing many blocks followed by re-using many blocks, due to
60
+ * tuplesort.c's "preread" behavior.
61
+ *
56
62
* Since all the bookkeeping and buffer memory is allocated with palloc(),
57
63
* and the underlying file(s) are made with OpenTemporaryFile, all resources
58
64
* for a logical tape set are certain to be cleaned up even if processing
64
70
* Portions Copyright (c) 1994, Regents of the University of California
65
71
*
66
72
* IDENTIFICATION
67
- * $PostgreSQL: pgsql/src/backend/utils/sort/logtape.c,v 1.20 2006/03/07 19:06:49 tgl Exp $
73
+ * $PostgreSQL: pgsql/src/backend/utils/sort/logtape.c,v 1.21 2006/03/07 23:46:24 tgl Exp $
68
74
*
69
75
*-------------------------------------------------------------------------
70
76
*/
@@ -143,15 +149,19 @@ struct LogicalTapeSet
143
149
144
150
/*
145
151
* We store the numbers of recycled-and-available blocks in freeBlocks[].
146
- * When there are no such blocks, we extend the underlying file. Note
147
- * that the block numbers in freeBlocks are always in *decreasing* order,
148
- * so that removing the last entry gives us the lowest free block.
152
+ * When there are no such blocks, we extend the underlying file.
149
153
*
150
154
* If forgetFreeSpace is true then any freed blocks are simply forgotten
151
155
* rather than being remembered in freeBlocks[]. See notes for
152
156
* LogicalTapeSetForgetFreeSpace().
157
+ *
158
+ * If blocksSorted is true then the block numbers in freeBlocks are in
159
+ * *decreasing* order, so that removing the last entry gives us the lowest
160
+ * free block. We re-sort the blocks whenever a block is demanded; this
161
+ * should be reasonably efficient given the expected usage pattern.
153
162
*/
154
163
bool forgetFreeSpace ; /* are we remembering free blocks? */
164
+ bool blocksSorted ; /* is freeBlocks[] currently in order? */
155
165
long * freeBlocks ; /* resizable array */
156
166
int nFreeBlocks ; /* # of currently free blocks */
157
167
int freeBlocksLen ; /* current allocated length of freeBlocks[] */
@@ -223,6 +233,23 @@ ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
223
233
blocknum )));
224
234
}
225
235
236
+ /*
237
+ * qsort comparator for sorting freeBlocks[] into decreasing order.
238
+ */
239
+ static int
240
+ freeBlocks_cmp (const void * a , const void * b )
241
+ {
242
+ long ablk = * ((const long * ) a );
243
+ long bblk = * ((const long * ) b );
244
+
245
+ /* can't just subtract because long might be wider than int */
246
+ if (ablk < bblk )
247
+ return 1 ;
248
+ if (ablk > bblk )
249
+ return -1 ;
250
+ return 0 ;
251
+ }
252
+
226
253
/*
227
254
* Select a currently unused block for writing to.
228
255
*
@@ -234,11 +261,19 @@ ltsGetFreeBlock(LogicalTapeSet *lts)
234
261
{
235
262
/*
236
263
* If there are multiple free blocks, we select the one appearing last in
237
- * freeBlocks[]. If there are none, assign the next block at the end of
238
- * the file.
264
+ * freeBlocks[] (after sorting the array if needed). If there are none,
265
+ * assign the next block at the end of the file.
239
266
*/
240
267
if (lts -> nFreeBlocks > 0 )
268
+ {
269
+ if (!lts -> blocksSorted )
270
+ {
271
+ qsort ((void * ) lts -> freeBlocks , lts -> nFreeBlocks ,
272
+ sizeof (long ), freeBlocks_cmp );
273
+ lts -> blocksSorted = true;
274
+ }
241
275
return lts -> freeBlocks [-- lts -> nFreeBlocks ];
276
+ }
242
277
else
243
278
return lts -> nFileBlocks ++ ;
244
279
}
@@ -250,7 +285,6 @@ static void
250
285
ltsReleaseBlock (LogicalTapeSet * lts , long blocknum )
251
286
{
252
287
int ndx ;
253
- long * ptr ;
254
288
255
289
/*
256
290
* Do nothing if we're no longer interested in remembering free space.
@@ -269,19 +303,13 @@ ltsReleaseBlock(LogicalTapeSet *lts, long blocknum)
269
303
}
270
304
271
305
/*
272
- * Insert blocknum into array, preserving decreasing order (so that
273
- * ltsGetFreeBlock returns the lowest available block number). This could
274
- * get fairly slow if there were many free blocks, but we don't expect
275
- * there to be very many at one time.
306
+ * Add blocknum to array, and mark the array unsorted if it's no longer
307
+ * in decreasing order.
276
308
*/
277
309
ndx = lts -> nFreeBlocks ++ ;
278
- ptr = lts -> freeBlocks + ndx ;
279
- while (ndx > 0 && ptr [-1 ] < blocknum )
280
- {
281
- ptr [0 ] = ptr [-1 ];
282
- ndx -- , ptr -- ;
283
- }
284
- ptr [0 ] = blocknum ;
310
+ lts -> freeBlocks [ndx ] = blocknum ;
311
+ if (ndx > 0 && lts -> freeBlocks [ndx - 1 ] < blocknum )
312
+ lts -> blocksSorted = false;
285
313
}
286
314
287
315
/*
@@ -503,6 +531,7 @@ LogicalTapeSetCreate(int ntapes)
503
531
lts -> pfile = BufFileCreateTemp (false);
504
532
lts -> nFileBlocks = 0L ;
505
533
lts -> forgetFreeSpace = false;
534
+ lts -> blocksSorted = true; /* a zero-length array is sorted ... */
506
535
lts -> freeBlocksLen = 32 ; /* reasonable initial guess */
507
536
lts -> freeBlocks = (long * ) palloc (lts -> freeBlocksLen * sizeof (long ));
508
537
lts -> nFreeBlocks = 0 ;
0 commit comments