Return more than a single dirent at a time in bfs_read_dir().

Return as many dirents as fit into the provided buffer. The readdir()
implementation in libroot tries to get many dirents and caches them to
reduce the amount of syscalls and the associated overhead.

This reduces the syscall count dramatically for use cases where a lot
of directories are enumerated. A "git status" on a Haiku repository for
example goes from over 50k read dir syscalls down to just under 7k.
The performance gain is only around a moderate 5% in that use case
though.
This commit is contained in:
Michael Lotz 2013-04-28 13:36:20 +02:00
parent 91b4626a78
commit a01dd96346

View File

@ -1634,25 +1634,44 @@ bfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
FUNCTION();
TreeIterator* iterator = (TreeIterator*)_cookie;
uint16 length;
ino_t id;
status_t status = iterator->GetNextEntry(dirent->d_name, &length,
bufferSize, &id);
if (status == B_ENTRY_NOT_FOUND) {
*_num = 0;
return B_OK;
} else if (status != B_OK)
RETURN_ERROR(status);
Volume* volume = (Volume*)_volume->private_volume;
dirent->d_dev = volume->ID();
dirent->d_ino = id;
uint32 maxCount = *_num;
uint32 count = 0;
dirent->d_reclen = sizeof(struct dirent) + length;
while (count < maxCount && bufferSize > sizeof(struct dirent)) {
ino_t id;
uint16 length;
size_t nameBufferSize = bufferSize - sizeof(struct dirent) + 1;
*_num = 1;
status_t status = iterator->GetNextEntry(dirent->d_name, &length,
nameBufferSize, &id);
if (status == B_ENTRY_NOT_FOUND)
break;
if (status == B_BUFFER_OVERFLOW) {
// the remaining name buffer length was too small
if (count == 0)
RETURN_ERROR(B_BUFFER_OVERFLOW);
break;
}
if (status != B_OK)
RETURN_ERROR(status);
ASSERT(length < nameBufferSize);
dirent->d_dev = volume->ID();
dirent->d_ino = id;
dirent->d_reclen = sizeof(struct dirent) + length;
bufferSize -= dirent->d_reclen;
dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
count++;
}
*_num = count;
return B_OK;
}