vm: Replace the VMAreas OpenHashTable with an AVLTree.
Since we used a hash table with a fixed size (1024), collisions were obviously inevitable, meaning that while insertions would always be fast, lookups and deletions would take linear time to search the linked-list for the area in question. For recently-created areas, this would be fast; for less-recently-created areas, it would get slower and slower and slower. A particularly pathological case was the "mmap/24-1" test from the Open POSIX Testsuite, which creates millions of areas until it hits ENOMEM; it then simply exits, at which point it would run for minutes and minutes in the kernel team deletion routines; how long I don't know, as I rebooted before it finished. This change fixes that problem, among others, at the cost of increased area creation time, by using an AVL tree instead of a hash. For comparison, mmap'ing 2 million areas with the "24-1" test before this change took around 0m2.706s of real time, while afterwards it takes about 0m3.118s, or around a 15% increase (1.152x). On the other hand, the total test runtime for 2 million areas went from around 2m11.050s to 0m4.035s, or around a 97% decrease (0.031x); in other words, with this new code, it is *32 times faster.* Area insertion will no longer be O(1), however, so the time increase may go up with the number of areas present on the system; but if it's only around 3 seconds to create 2 million areas, or about 1.56 us per area, vs. 1.35 us before, I don't think that's worth worrying about. My nonscientific "compile HaikuDepot with 2 cores in VM" benchmark seems to be within the realm of "noise", anyway, with most results both before and after this change coming in around 47s real time. Change-Id: I230e17de4f80304d082152af83db8bd5abe7b831
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
#include <lock.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/SinglyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
#include <util/AVLTree.h>
|
||||
#include <vm/vm_types.h>
|
||||
|
||||
|
||||
@@ -88,7 +88,12 @@ struct VMPageWiringInfo {
|
||||
};
|
||||
|
||||
|
||||
struct VMArea {
|
||||
struct VMAreasTreeNode {
|
||||
AVLTreeNode tree_node;
|
||||
};
|
||||
|
||||
|
||||
struct VMArea : private VMAreasTreeNode {
|
||||
public:
|
||||
enum {
|
||||
// AddWaiterIfWired() flags
|
||||
@@ -117,7 +122,6 @@ public:
|
||||
struct VMAddressSpace* address_space;
|
||||
struct VMArea* cache_next;
|
||||
struct VMArea* cache_prev;
|
||||
struct VMArea* hash_next;
|
||||
|
||||
addr_t Base() const { return fBase; }
|
||||
size_t Size() const { return fSize; }
|
||||
@@ -149,7 +153,7 @@ protected:
|
||||
status_t Init(const char* name, uint32 allocationFlags);
|
||||
|
||||
protected:
|
||||
friend struct VMAddressSpace;
|
||||
friend struct VMAreasTreeDefinition;
|
||||
friend struct VMKernelAddressSpace;
|
||||
friend struct VMUserAddressSpace;
|
||||
|
||||
@@ -164,35 +168,42 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
struct VMAreaHashDefinition {
|
||||
typedef area_id KeyType;
|
||||
typedef VMArea ValueType;
|
||||
struct VMAreasTreeDefinition {
|
||||
typedef area_id Key;
|
||||
typedef VMArea Value;
|
||||
|
||||
size_t HashKey(area_id key) const
|
||||
AVLTreeNode* GetAVLTreeNode(VMArea* value) const
|
||||
{
|
||||
return key;
|
||||
return &value->tree_node;
|
||||
}
|
||||
|
||||
size_t Hash(const VMArea* value) const
|
||||
VMArea* GetValue(AVLTreeNode* node) const
|
||||
{
|
||||
return HashKey(value->id);
|
||||
const addr_t vmTreeNodeAddr = (addr_t)node
|
||||
- offsetof(VMAreasTreeNode, tree_node);
|
||||
VMAreasTreeNode* vmTreeNode =
|
||||
reinterpret_cast<VMAreasTreeNode*>(vmTreeNodeAddr);
|
||||
return static_cast<VMArea*>(vmTreeNode);
|
||||
}
|
||||
|
||||
bool Compare(area_id key, const VMArea* value) const
|
||||
int Compare(area_id key, const VMArea* value) const
|
||||
{
|
||||
return value->id == key;
|
||||
const area_id valueId = value->id;
|
||||
if (valueId == key)
|
||||
return 0;
|
||||
return key < valueId ? -1 : 1;
|
||||
}
|
||||
|
||||
VMArea*& GetLink(VMArea* value) const
|
||||
int Compare(const VMArea* a, const VMArea* b) const
|
||||
{
|
||||
return value->hash_next;
|
||||
return Compare(a->id, b);
|
||||
}
|
||||
};
|
||||
|
||||
typedef BOpenHashTable<VMAreaHashDefinition> VMAreaHashTable;
|
||||
typedef AVLTree<VMAreasTreeDefinition> VMAreasTree;
|
||||
|
||||
|
||||
struct VMAreaHash {
|
||||
struct VMAreas {
|
||||
static status_t Init();
|
||||
|
||||
static status_t ReadLock()
|
||||
@@ -205,18 +216,18 @@ struct VMAreaHash {
|
||||
{ rw_lock_write_unlock(&sLock); }
|
||||
|
||||
static VMArea* LookupLocked(area_id id)
|
||||
{ return sTable.Lookup(id); }
|
||||
{ return sTree.Find(id); }
|
||||
static VMArea* Lookup(area_id id);
|
||||
static area_id Find(const char* name);
|
||||
static void Insert(VMArea* area);
|
||||
static void Remove(VMArea* area);
|
||||
|
||||
static VMAreaHashTable::Iterator GetIterator()
|
||||
{ return sTable.GetIterator(); }
|
||||
static VMAreasTree::Iterator GetIterator()
|
||||
{ return sTree.GetIterator(); }
|
||||
|
||||
private:
|
||||
static rw_lock sLock;
|
||||
static VMAreaHashTable sTable;
|
||||
static VMAreasTree sTree;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user