axeld:
* Implemented a way to do asynchronous pre-fetching when mapping files. * There are slight code duplications in some places that could benefit from cleaning up, but nothing too bad. * Implementing smarter ways to trigger prefetching and more analysis of the situations in the kernel would be nice. Currently up to 10 MB of every mapped file are pre-fetched without further analysis. * The speed improvement is nice for certain operations. On our test system (real hardware), Firefox took 9 seconds from being launched to display a window. Now it takes 5 seconds. Both measurements right after booting. The same system took 35 seconds from launching Haiku in the GRUB menu to displaying the Tracker desktop background image. Now it takes 27 seconds. * We didn't have the chance to check out the effects of this on the CD boot, but potentially, they could speed it up a lot. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30465 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
f77b0a6b83
commit
eb2bd0e8e3
@ -286,6 +286,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
status_t vfs_asynchronous_read_pages(struct vnode* vnode, void* cookie,
|
||||||
|
off_t pos, const iovec* vecs, size_t count, size_t numBytes,
|
||||||
|
uint32 flags, AsyncIOCallback* callback);
|
||||||
|
|
||||||
status_t vfs_asynchronous_write_pages(struct vnode* vnode, void* cookie,
|
status_t vfs_asynchronous_write_pages(struct vnode* vnode, void* cookie,
|
||||||
off_t pos, const iovec* vecs, size_t count, size_t numBytes,
|
off_t pos, const iovec* vecs, size_t count, size_t numBytes,
|
||||||
uint32 flags, AsyncIOCallback* callback);
|
uint32 flags, AsyncIOCallback* callback);
|
||||||
|
207
src/system/kernel/cache/file_cache.cpp
vendored
207
src/system/kernel/cache/file_cache.cpp
vendored
@ -69,10 +69,37 @@ struct file_cache_ref {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PrecacheIO : public AsyncIOCallback {
|
||||||
|
public:
|
||||||
|
PrecacheIO(file_cache_ref* ref, off_t offset,
|
||||||
|
size_t size);
|
||||||
|
~PrecacheIO();
|
||||||
|
|
||||||
|
status_t Init();
|
||||||
|
status_t Start();
|
||||||
|
|
||||||
|
virtual void IOFinished(status_t status,
|
||||||
|
bool partialTransfer,
|
||||||
|
size_t bytesTransferred);
|
||||||
|
|
||||||
|
private:
|
||||||
|
file_cache_ref* fRef;
|
||||||
|
VMCache* fCache;
|
||||||
|
vm_page** fPages;
|
||||||
|
size_t fPageCount;
|
||||||
|
ConditionVariable* fBusyConditions;
|
||||||
|
iovec* fVecs;
|
||||||
|
off_t fOffset;
|
||||||
|
size_t fSize;
|
||||||
|
};
|
||||||
|
|
||||||
typedef status_t (*cache_func)(file_cache_ref *ref, void *cookie, off_t offset,
|
typedef status_t (*cache_func)(file_cache_ref *ref, void *cookie, off_t offset,
|
||||||
int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
|
int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
|
||||||
size_t lastReservedPages, size_t reservePages);
|
size_t lastReservedPages, size_t reservePages);
|
||||||
|
|
||||||
|
static void add_to_iovec(iovec *vecs, uint32 &index, uint32 max, addr_t address,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
|
||||||
static struct cache_module_info *sCacheModule;
|
static struct cache_module_info *sCacheModule;
|
||||||
static const uint8 kZeroBuffer[4096] = {};
|
static const uint8 kZeroBuffer[4096] = {};
|
||||||
@ -81,8 +108,119 @@ static const uint8 kZeroBuffer[4096] = {};
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
PrecacheIO::PrecacheIO(file_cache_ref* ref, off_t offset, size_t size)
|
||||||
|
:
|
||||||
|
fRef(ref),
|
||||||
|
fCache(ref->cache),
|
||||||
|
fPages(NULL),
|
||||||
|
fBusyConditions(NULL),
|
||||||
|
fVecs(NULL),
|
||||||
|
fOffset(offset),
|
||||||
|
fSize(size)
|
||||||
|
{
|
||||||
|
fPageCount = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
|
||||||
|
fCache->AcquireRefLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PrecacheIO::~PrecacheIO()
|
||||||
|
{
|
||||||
|
delete[] fPages;
|
||||||
|
delete[] fBusyConditions;
|
||||||
|
delete[] fVecs;
|
||||||
|
fCache->ReleaseRefLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
PrecacheIO::Init()
|
||||||
|
{
|
||||||
|
if (fPageCount == 0)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
fPages = new(std::nothrow) vm_page*[fPageCount];
|
||||||
|
if (fPages == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
fBusyConditions = new(std::nothrow) ConditionVariable[fPageCount];
|
||||||
|
if (fBusyConditions == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
fVecs = new(std::nothrow) iovec[fPageCount];
|
||||||
|
if (fVecs == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Cache has to be locked when calling this method.
|
||||||
|
status_t
|
||||||
|
PrecacheIO::Start()
|
||||||
|
{
|
||||||
|
// allocate pages for the cache and mark them busy
|
||||||
|
uint32 vecCount = 0;
|
||||||
|
uint32 i = 0;
|
||||||
|
for (size_t pos = 0; pos < fSize; pos += B_PAGE_SIZE) {
|
||||||
|
vm_page* page = fPages[i++] = vm_page_allocate_page(
|
||||||
|
PAGE_STATE_FREE, true);
|
||||||
|
if (page == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
fBusyConditions[i - 1].Publish(page, "page");
|
||||||
|
|
||||||
|
fCache->InsertPage(page, fOffset + pos);
|
||||||
|
|
||||||
|
add_to_iovec(fVecs, vecCount, fPageCount,
|
||||||
|
page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != fPageCount) {
|
||||||
|
// allocating pages failed
|
||||||
|
while (i-- > 0) {
|
||||||
|
fBusyConditions[i].Unpublish();
|
||||||
|
fCache->RemovePage(fPages[i]);
|
||||||
|
vm_page_set_state(fPages[i], PAGE_STATE_FREE);
|
||||||
|
}
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vfs_asynchronous_read_pages(fRef->vnode, NULL, fOffset, fVecs,
|
||||||
|
vecCount, fSize, B_PHYSICAL_IO_REQUEST, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PrecacheIO::IOFinished(status_t status, bool partialTransfer,
|
||||||
|
size_t bytesTransferred)
|
||||||
|
{
|
||||||
|
AutoLocker<VMCache> locker(fCache);
|
||||||
|
|
||||||
|
// Make successfully loaded pages accessible again (partially
|
||||||
|
// transferred pages are considered failed)
|
||||||
|
size_t pagesTransferred = bytesTransferred >> PAGE_SHIFT;
|
||||||
|
for (uint32 i = 0; i < pagesTransferred; i++) {
|
||||||
|
fPages[i]->state = PAGE_STATE_ACTIVE;
|
||||||
|
fBusyConditions[i].Unpublish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free pages after failed I/O
|
||||||
|
for (uint32 i = pagesTransferred; i < fPageCount; i++) {
|
||||||
|
fBusyConditions[i].Unpublish();
|
||||||
|
fCache->RemovePage(fPages[i]);
|
||||||
|
vm_page_set_state(fPages[i], PAGE_STATE_FREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size)
|
add_to_iovec(iovec *vecs, uint32 &index, uint32 max, addr_t address,
|
||||||
|
size_t size)
|
||||||
{
|
{
|
||||||
if (index > 0 && (addr_t)vecs[index - 1].iov_base
|
if (index > 0 && (addr_t)vecs[index - 1].iov_base
|
||||||
+ vecs[index - 1].iov_len == address) {
|
+ vecs[index - 1].iov_len == address) {
|
||||||
@ -194,7 +332,7 @@ read_into_cache(file_cache_ref *ref, void *cookie, off_t offset,
|
|||||||
// TODO: We're using way too much stack! Rather allocate a sufficiently
|
// TODO: We're using way too much stack! Rather allocate a sufficiently
|
||||||
// large chunk on the heap.
|
// large chunk on the heap.
|
||||||
iovec vecs[MAX_IO_VECS];
|
iovec vecs[MAX_IO_VECS];
|
||||||
int32 vecCount = 0;
|
uint32 vecCount = 0;
|
||||||
|
|
||||||
size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
|
size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
|
||||||
vm_page *pages[MAX_IO_VECS];
|
vm_page *pages[MAX_IO_VECS];
|
||||||
@ -314,7 +452,7 @@ write_to_cache(file_cache_ref *ref, void *cookie, off_t offset,
|
|||||||
// TODO: We're using way too much stack! Rather allocate a sufficiently
|
// TODO: We're using way too much stack! Rather allocate a sufficiently
|
||||||
// large chunk on the heap.
|
// large chunk on the heap.
|
||||||
iovec vecs[MAX_IO_VECS];
|
iovec vecs[MAX_IO_VECS];
|
||||||
int32 vecCount = 0;
|
uint32 vecCount = 0;
|
||||||
size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
|
size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
|
||||||
vm_page *pages[MAX_IO_VECS];
|
vm_page *pages[MAX_IO_VECS];
|
||||||
int32 pageIndex = 0;
|
int32 pageIndex = 0;
|
||||||
@ -391,7 +529,7 @@ write_to_cache(file_cache_ref *ref, void *cookie, off_t offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < vecCount; i++) {
|
for (uint32 i = 0; i < vecCount; i++) {
|
||||||
addr_t base = (addr_t)vecs[i].iov_base;
|
addr_t base = (addr_t)vecs[i].iov_base;
|
||||||
size_t bytes = min_c(bufferSize,
|
size_t bytes = min_c(bufferSize,
|
||||||
size_t(vecs[i].iov_len - pageOffset));
|
size_t(vecs[i].iov_len - pageOffset));
|
||||||
@ -757,6 +895,11 @@ file_cache_control(const char *subsystem, uint32 function, void *buffer,
|
|||||||
extern "C" void
|
extern "C" void
|
||||||
cache_prefetch_vnode(struct vnode *vnode, off_t offset, size_t size)
|
cache_prefetch_vnode(struct vnode *vnode, off_t offset, size_t size)
|
||||||
{
|
{
|
||||||
|
if (low_resource_state(B_KERNEL_RESOURCE_PAGES) != B_NO_LOW_RESOURCE) {
|
||||||
|
// don't do anything if we don't have the resources left
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vm_cache *cache;
|
vm_cache *cache;
|
||||||
if (vfs_get_vnode_cache(vnode, &cache, false) != B_OK)
|
if (vfs_get_vnode_cache(vnode, &cache, false) != B_OK)
|
||||||
return;
|
return;
|
||||||
@ -764,16 +907,56 @@ cache_prefetch_vnode(struct vnode *vnode, off_t offset, size_t size)
|
|||||||
file_cache_ref *ref = ((VMVnodeCache*)cache)->FileCacheRef();
|
file_cache_ref *ref = ((VMVnodeCache*)cache)->FileCacheRef();
|
||||||
off_t fileSize = cache->virtual_end;
|
off_t fileSize = cache->virtual_end;
|
||||||
|
|
||||||
if (size > fileSize)
|
if (offset >= fileSize) {
|
||||||
size = fileSize;
|
cache->ReleaseRef();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (offset + size > fileSize)
|
||||||
|
size = offset - fileSize;
|
||||||
|
|
||||||
// we never fetch more than 4 MB at once
|
// "offset" and "size" are always aligned to B_PAGE_SIZE,
|
||||||
if (size > 4 * 1024 * 1024)
|
offset &= ~(B_PAGE_SIZE - 1);
|
||||||
size = 4 * 1024 * 1024;
|
size = ROUNDUP(size, B_PAGE_SIZE);
|
||||||
|
|
||||||
cache_io(ref, NULL, offset, 0, &size, false);
|
size_t bytesToRead = 0;
|
||||||
cache->Lock();
|
off_t lastOffset = offset;
|
||||||
cache->ReleaseRefAndUnlock();
|
|
||||||
|
AutoLocker<VMCache> locker(cache);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// check if this page is already in memory
|
||||||
|
if (size > 0) {
|
||||||
|
vm_page* page = cache->LookupPage(offset);
|
||||||
|
|
||||||
|
offset += B_PAGE_SIZE;
|
||||||
|
size -= B_PAGE_SIZE;
|
||||||
|
|
||||||
|
if (page == NULL) {
|
||||||
|
bytesToRead += B_PAGE_SIZE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bytesToRead != 0) {
|
||||||
|
// read the part before the current page (or the end of the request)
|
||||||
|
PrecacheIO* io
|
||||||
|
= new(std::nothrow) PrecacheIO(ref, lastOffset, bytesToRead);
|
||||||
|
if (io == NULL || io->Init() != B_OK || io->Start() != B_OK) {
|
||||||
|
delete io;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesToRead = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
// we have reached the end of the request
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastOffset = offset + B_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache->ReleaseRefLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -426,6 +426,32 @@ vfs_synchronous_io(io_request* request,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
vfs_asynchronous_read_pages(struct vnode* vnode, void* cookie, off_t pos,
|
||||||
|
const iovec* vecs, size_t count, size_t numBytes, uint32 flags,
|
||||||
|
AsyncIOCallback* callback)
|
||||||
|
{
|
||||||
|
IORequest* request = IORequest::Create((flags & B_VIP_IO_REQUEST) != 0);
|
||||||
|
if (request == NULL) {
|
||||||
|
callback->IOFinished(B_NO_MEMORY, true, 0);
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t status = request->Init(pos, vecs, count, numBytes, false,
|
||||||
|
flags | B_DELETE_IO_REQUEST);
|
||||||
|
if (status != B_OK) {
|
||||||
|
delete request;
|
||||||
|
callback->IOFinished(status, true, 0);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
request->SetFinishedCallback(&AsyncIOCallback::IORequestCallback,
|
||||||
|
callback);
|
||||||
|
|
||||||
|
return vfs_vnode_io(vnode, cookie, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
vfs_asynchronous_write_pages(struct vnode* vnode, void* cookie, off_t pos,
|
vfs_asynchronous_write_pages(struct vnode* vnode, void* cookie, off_t pos,
|
||||||
const iovec* vecs, size_t count, size_t numBytes, uint32 flags,
|
const iovec* vecs, size_t count, size_t numBytes, uint32 flags,
|
||||||
|
@ -2256,9 +2256,9 @@ pre_map_area_pages(vm_area* area, VMCache* cache)
|
|||||||
\a offset and \a size arguments have to be page aligned.
|
\a offset and \a size arguments have to be page aligned.
|
||||||
*/
|
*/
|
||||||
static area_id
|
static area_id
|
||||||
_vm_map_file(team_id team, const char* name, void** _address, uint32 addressSpec,
|
_vm_map_file(team_id team, const char* name, void** _address,
|
||||||
size_t size, uint32 protection, uint32 mapping, int fd, off_t offset,
|
uint32 addressSpec, size_t size, uint32 protection, uint32 mapping, int fd,
|
||||||
bool kernel)
|
off_t offset, bool kernel)
|
||||||
{
|
{
|
||||||
// TODO: for binary files, we want to make sure that they get the
|
// TODO: for binary files, we want to make sure that they get the
|
||||||
// copy of a file at a given time, ie. later changes should not
|
// copy of a file at a given time, ie. later changes should not
|
||||||
@ -2352,7 +2352,7 @@ _vm_map_file(team_id team, const char* name, void** _address, uint32 addressSpec
|
|||||||
offset, size, addressSpec, 0, protection, mapping, &area, name,
|
offset, size, addressSpec, 0, protection, mapping, &area, name,
|
||||||
addressSpec == B_EXACT_ADDRESS, kernel);
|
addressSpec == B_EXACT_ADDRESS, kernel);
|
||||||
|
|
||||||
if (status < B_OK || mapping == REGION_PRIVATE_MAP) {
|
if (status != B_OK || mapping == REGION_PRIVATE_MAP) {
|
||||||
// map_backing_store() cannot know we no longer need the ref
|
// map_backing_store() cannot know we no longer need the ref
|
||||||
cache->ReleaseRefLocked();
|
cache->ReleaseRefLocked();
|
||||||
}
|
}
|
||||||
@ -2362,7 +2362,14 @@ _vm_map_file(team_id team, const char* name, void** _address, uint32 addressSpec
|
|||||||
|
|
||||||
cache->Unlock();
|
cache->Unlock();
|
||||||
|
|
||||||
if (status < B_OK)
|
if (status == B_OK) {
|
||||||
|
// TODO: this probably deserves a smarter solution, ie. don't always
|
||||||
|
// prefetch stuff
|
||||||
|
cache_prefetch_vnode(vnode, offset, min_c(size, 10LL * 1024 * 1024));
|
||||||
|
// prefetches at max 10 MB starting from "offset"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
area->cache_type = CACHE_TYPE_VNODE;
|
area->cache_type = CACHE_TYPE_VNODE;
|
||||||
|
Loading…
Reference in New Issue
Block a user