To understand the LFS performance, we must
examine the operation of the cleaner and its
interaction with the benchmark. In the steady state,
each segment dirtied by LFS requires that the cleaner
produce one clean segment. If we were insensitive to
response time and wished only to clean most
efficiently, we would run the benchmark until the disk
filled, then clean, and then restart the benchmark. This
should produce the best possible throughput in the
presence of the cleaner.
As discussed earlier, LFS fills segments at a rate
of 115 transactions per 700 KB or 168 transactions
per segment. For simplicity, call the database 256 MB
and the disk system 512 MB. This requires 256
segments, 43,000 transactions, or 1000 seconds at the
“no-cleaning” LFS rate. After the 1000 seconds have
elapsed, LFS must clean. If we wish to clean the
entire disk, we must read all 512 segments and write
256 new ones. Let us assume, optimistically, that we
can read segments at the full bus bandwidth (2.3
MB/sec) and write them at two-thirds of the disk
bandwidth (1.7 MB/sec), missing a rotation between
every 64 KB transfer. The cleaning process will take
223 seconds to read and 151 seconds to write for a
total of 374 seconds. Therefore, at 50% utilization our
best case throughput in the presence of the cleaner is
31.3 transactions per second. This is within 15% of
our measured performance.
Unfortunately, LFS cannot clean at the optimal
rate described above. First, the transaction response
would be unacceptably slow while the cleaner
stopped for six minutes to clean. Secondly, the
calculations above assumed that the disk is read
sequentially. Since the selection of segments is based
on Rosenblum’s cost-benefit algorithm [9], there is no
guarantee that collections of segments being cleaned
will be contiguous on disk. Thirdly, the history file in
the benchmark grows by 50 bytes per transaction, so
file system utilization increases during the test run.
Finally, cleaning requires that multiple segments be
cached in memory for processing, so we must limit
the number of segments cleaned simultaneously.
Since LFS cannot clean at its maximal rate, it
should clean at a rate that permits it to perform its
segment reads and writes at near-optimal speed. At
50% utilization, we should be able to read two dirty
segments and produce one clean segment. Reading
one megabyte requires a random seek (9.5 ms) one-
half rotation (5.5 ms) and a 1 MB transfer (435 ms)
for a total of 450 ms per segment read. Rewriting the
segment requires the same seek and rotation, but the
transfer requires 588 ms for a total of 603 ms for the
write or 1.5 seconds to clean the segment. In the
steady state, this cleaning must be done for each 168
transactions. Our throughput without the cleaner is 41
transactions per second, so it takes 4.1 seconds to
execute 168 transactions and 1.5 seconds to clean,
yielding 5.6 seconds or 30.0 TPS. This is within 10%
of our measured number.
It can be argued that LFS loses performance
because it writes indirect blocks too frequently
(approximately once every three seconds in our
benchmarking environment). The current BSD-LFS
write policy assumes that when the number of dirty
buffers in the cache exceeds the write threshold (the
point at which the kernel triggers a segment write),
generating clean buffers is essential and it uses an
aggressive policy of writing all the dirty buffers to
disk. If the dirty indirect blocks were cached during
this benchmark, the number of dirty data blocks that
are allowed to accumulate in the cache would be
reduced and segment writes would occur more
frequently. While suboptimal for this benchmark, we
believe that flushing indirect blocks with their data
blocks is the correct default policy.
5 Effects of Free Space Fragmentation
on FFS Performance
Both LFS and FFS rely on the allocation of contigu-
ous disk blocks to achieve high levels of performance.
Because the results in Section 3 were obtained from
newly-created, empty file systems, there was no short-
age of contiguous extents of free space. On real sys-
tems, in use for extended periods of time (months or
years), the file system cannot expect to find such an
ideal arrangement of free space. LFS and FFS deal
with this reality in two different ways, both of which
cause performance overhead for the respective file
systems.
LFS relies on the cleaner process to garbage
collect old segments, creating large regions of free
space—clean segments. The cleaner imposes
overhead on the overall performance of LFS. Section
4 discusses this overhead in the context of a
transaction processing workload.
In contrast, FFS makes no assumptions about the
layout of free space on the file system. FFS uses
whatever free space is available on the disk,
contiguous or not. In fact, the block allocation policy
of FFS remained unchanged when clustering was
added [7]. FFS may not allocate contiguous blocks to
a file, even when contiguous free space is available.
As with the LFS cleaner, this may adversely effect
performance.
The fragmentation of free space in an FFS may
increase with time or with file system utilization. This
fragmentation can degrade performance as an FFS