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:
Augustin Cavalier
2023-03-24 10:53:52 -04:00
parent e48af9e4ec
commit c650846d9e
7 changed files with 71 additions and 73 deletions
+32 -21
View File
@@ -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;
};