Buffer chunks is a "feature" inherited from the original IBX Borland source code. IBX Buffer Management is something I have yet to improve - but it is on my "to do list". Here's the background:
The Delphi TDataset model allows for either uni-directional or bi-directional movement through the rows of the underlying dataset (a single table or the result of an SQL Query). On the other hand, most database engines only provide uni-directional movement through a dataset - from top to bottom - and return each row through successive calls to a "fetch" function. This difference has to be resolved by the actual implementation of TDataset (e.g. that provided by IBX - by subclassing TDataset).
The TDataset model also implements a sliding window approach. It expects the TDataset implementation to provide pointers to a fixed number of buffers when represent those rows in the "window". For example, the rows shown in a TDBGrid. How those buffers are provided is a matter for the TDataset subclass. TDataset allows for both uni-directional (forward only) and bidirectional movement of the sliding window. This mode is selected when the dataset is opened.
For most of its history, Firebird has only provided uni-directional movement - although Firebird 4 has introduced the idea of scrollable cursors. IBX does provide access to scrollable cursors through its low level Firebird Pascal Interface, while I have yet to investigate any possible use of this feature in support of TDataset. As far as IBX datasets are concerned, only Firebird uni-directional cursors are used.
In IBX there are currently three ways in which the underlying buffers are managed in support of TDataset uni and bidirectional modes:
1. Uni-directional.
In uni-direction mode, the sliding window can only be advanced and cam never be reversed. Only a small number of buffers need be allocated given that as the window moves forwards those left behind can be reused for the most recently fetched rows. This is the most efficient mode of use and has a minimal memory footprint. This is the best choice if you only scroll forwards through the dataset. The BufferChunks parameter is ignored in this mode.
2. Bi-directional.
In this mode, IBX allocates a buffer pool from a single memory allocation. Each buffer has a fixed size which makes random access easy given that each buffer can be accessed using a readily computable offset from the start of the buffer pool. Successive buffers are used to save the dataset rows as they are fetched on demand as the user scrolls forwards. The user can scroll backwards at any time as the required row will always be in a buffer.
The problem with this approach is that the size of the buffer pool will always be finite and can only accommodate a limited number of buffers/rows. As the number of rows is not known when the dataset is opened, the approach used is to create a buffer pool for a fixed number of buffers and if that proves to be too small when the buffer pool needs to be expanded, the underlying memory is reallocated as a larger size providing for an increased number of buffers.
Depending on the underlying memory manager, when the buffer pool size is increased, a new block of contiguous memory is allocated for the resized memory block and the contents of the original memory block copied to it, before the original memory block is deallocated.
The BufferChunks parameter is used to determine both the initial size of the original memory block and each successive increase in size. The initial block is computed as "buffer size required" * BufferChunks. Each reallocation increases the size of the memory block by the same amount.
If BufferChunks is set too high them memory use is inefficient as there are always many unused buffers. If it is too low then there are frequent reallocations each taking a finite time to perform and performance suffers. There is no perfect setting unless you know in advance how big the dataset will be. Sometimes it may even be worth querying the database to find out how many rows are present before the dataset is opened, and setting BufferChunks appropriately.
3. Bidirectional with cached updates.
This is the same as bidirectional mode but requires a second buffer pool to hold a copy of each buffer prior to it being edited. This is to allow for the cancellation of cached updates.
Future Steps
What I would like to do is to move away from the single contiguous memory block approach used for the buffer pool and instead allow for each increase to use a separate memory block. This will avoid the need to copy the buffer pool contents each time the underlying memory block has to be increased, reducing the performance overhead and allowing for smaller initial allocations and hence improved memory utilisation.