mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Make ctl::set use 30% less memory than libcxx
This commit is contained in:
parent
6dbc3fba18
commit
135d538b1d
3 changed files with 225 additions and 112 deletions
244
ctl/set.h
244
ctl/set.h
|
@ -13,18 +13,34 @@ class set
|
|||
{
|
||||
struct rbtree
|
||||
{
|
||||
rbtree* left;
|
||||
uintptr_t left_;
|
||||
rbtree* right;
|
||||
rbtree* parent;
|
||||
bool is_red;
|
||||
Key value;
|
||||
|
||||
rbtree* left() const
|
||||
{
|
||||
return (rbtree*)(left_ & -2);
|
||||
}
|
||||
|
||||
void left(rbtree* val)
|
||||
{
|
||||
left_ = (uintptr_t)val | (left_ & 1);
|
||||
}
|
||||
|
||||
bool is_red() const
|
||||
{
|
||||
return left_ & 1;
|
||||
}
|
||||
|
||||
void is_red(bool val)
|
||||
{
|
||||
left_ &= -2;
|
||||
left_ |= val;
|
||||
}
|
||||
|
||||
rbtree(const Key& val)
|
||||
: left(nullptr)
|
||||
, right(nullptr)
|
||||
, parent(nullptr)
|
||||
, is_red(true)
|
||||
, value(val)
|
||||
: left_(1), right(nullptr), parent(nullptr), value(val)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -85,14 +101,14 @@ class set
|
|||
{
|
||||
if (node_ == nullptr)
|
||||
__builtin_trap();
|
||||
if (node_->left) {
|
||||
node_ = rightmost(node_->left);
|
||||
if (node_->left()) {
|
||||
node_ = rightmost(node_->left());
|
||||
} else {
|
||||
node_type* parent = node_->parent;
|
||||
for (;;) {
|
||||
if (parent == nullptr)
|
||||
break;
|
||||
if (node_ == parent->left) {
|
||||
if (node_ == parent->left()) {
|
||||
node_ = parent;
|
||||
parent = parent->parent;
|
||||
} else {
|
||||
|
@ -161,11 +177,11 @@ class set
|
|||
|
||||
reverse_iterator& operator++()
|
||||
{
|
||||
if (node_->left) {
|
||||
node_ = rightmost(node_->left);
|
||||
if (node_->left()) {
|
||||
node_ = rightmost(node_->left());
|
||||
} else {
|
||||
node_type* parent = node_->parent;
|
||||
while (parent && node_ == parent->left) {
|
||||
while (parent && node_ == parent->left()) {
|
||||
node_ = parent;
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
@ -508,7 +524,7 @@ class set
|
|||
{
|
||||
size_type count = 0;
|
||||
if (root_ != nullptr) {
|
||||
if (root_->is_red)
|
||||
if (root_->is_red())
|
||||
// ILLEGAL TREE: root node must be black
|
||||
__builtin_trap();
|
||||
int black_height = -1;
|
||||
|
@ -523,8 +539,8 @@ class set
|
|||
private:
|
||||
static node_type* leftmost(node_type* node) noexcept
|
||||
{
|
||||
while (node && node->left)
|
||||
node = node->left;
|
||||
while (node && node->left())
|
||||
node = node->left();
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -535,35 +551,35 @@ class set
|
|||
return node;
|
||||
}
|
||||
|
||||
static void clearer(node_type* node) noexcept
|
||||
static optimizesize void clearer(node_type* node) noexcept
|
||||
{
|
||||
node_type* right;
|
||||
for (; node; node = right) {
|
||||
right = node->right;
|
||||
clearer(node->left);
|
||||
clearer(node->left());
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
static node_type* copier(const node_type* node)
|
||||
static optimizesize node_type* copier(const node_type* node)
|
||||
{
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
node_type* new_node = new node_type(node->value);
|
||||
new_node->left = copier(node->left);
|
||||
new_node->left(copier(node->left()));
|
||||
new_node->right = copier(node->right);
|
||||
if (new_node->left)
|
||||
new_node->left->parent = new_node;
|
||||
if (new_node->left())
|
||||
new_node->left()->parent = new_node;
|
||||
if (new_node->right)
|
||||
new_node->right->parent = new_node;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
static size_type tally(const node_type* node)
|
||||
static optimizesize size_type tally(const node_type* node)
|
||||
{
|
||||
if (node == nullptr)
|
||||
return 0;
|
||||
return 1 + tally(node->left) + tally(node->right);
|
||||
return 1 + tally(node->left()) + tally(node->right);
|
||||
}
|
||||
|
||||
template<typename K>
|
||||
|
@ -572,7 +588,7 @@ class set
|
|||
node_type* current = root_;
|
||||
while (current != nullptr) {
|
||||
if (comp_(key, current->value)) {
|
||||
current = current->left;
|
||||
current = current->left();
|
||||
} else if (comp_(current->value, key)) {
|
||||
current = current->right;
|
||||
} else {
|
||||
|
@ -590,7 +606,7 @@ class set
|
|||
while (current != nullptr) {
|
||||
if (!comp_(current->value, key)) {
|
||||
result = current;
|
||||
current = current->left;
|
||||
current = current->left();
|
||||
} else {
|
||||
current = current->right;
|
||||
}
|
||||
|
@ -606,7 +622,7 @@ class set
|
|||
while (current != nullptr) {
|
||||
if (comp_(key, current->value)) {
|
||||
result = current;
|
||||
current = current->left;
|
||||
current = current->left();
|
||||
} else {
|
||||
current = current->right;
|
||||
}
|
||||
|
@ -614,11 +630,11 @@ class set
|
|||
return result;
|
||||
}
|
||||
|
||||
ctl::pair<iterator, bool> insert_node(node_type* node)
|
||||
optimizesize ctl::pair<iterator, bool> insert_node(node_type* node)
|
||||
{
|
||||
if (root_ == nullptr) {
|
||||
root_ = node;
|
||||
root_->is_red = false;
|
||||
root_->is_red(false);
|
||||
size_++;
|
||||
return { iterator(root_), true };
|
||||
}
|
||||
|
@ -627,7 +643,7 @@ class set
|
|||
while (current != nullptr) {
|
||||
parent = current;
|
||||
if (comp_(node->value, current->value)) {
|
||||
current = current->left;
|
||||
current = current->left();
|
||||
} else if (comp_(current->value, node->value)) {
|
||||
current = current->right;
|
||||
} else {
|
||||
|
@ -636,7 +652,7 @@ class set
|
|||
}
|
||||
}
|
||||
if (comp_(node->value, parent->value)) {
|
||||
parent->left = node;
|
||||
parent->left(node);
|
||||
} else {
|
||||
parent->right = node;
|
||||
}
|
||||
|
@ -646,23 +662,23 @@ class set
|
|||
return { iterator(node), true };
|
||||
}
|
||||
|
||||
void erase_node(node_type* node)
|
||||
optimizesize void erase_node(node_type* node)
|
||||
{
|
||||
node_type* y = node;
|
||||
node_type* x = nullptr;
|
||||
node_type* x_parent = nullptr;
|
||||
bool y_original_color = y->is_red;
|
||||
if (node->left == nullptr) {
|
||||
bool y_original_color = y->is_red();
|
||||
if (node->left() == nullptr) {
|
||||
x = node->right;
|
||||
transplant(node, node->right);
|
||||
x_parent = node->parent;
|
||||
} else if (node->right == nullptr) {
|
||||
x = node->left;
|
||||
transplant(node, node->left);
|
||||
x = node->left();
|
||||
transplant(node, node->left());
|
||||
x_parent = node->parent;
|
||||
} else {
|
||||
y = leftmost(node->right);
|
||||
y_original_color = y->is_red;
|
||||
y_original_color = y->is_red();
|
||||
x = y->right;
|
||||
if (y->parent == node) {
|
||||
if (x)
|
||||
|
@ -675,9 +691,9 @@ class set
|
|||
x_parent = y->parent;
|
||||
}
|
||||
transplant(node, y);
|
||||
y->left = node->left;
|
||||
y->left->parent = y;
|
||||
y->is_red = node->is_red;
|
||||
y->left(node->left());
|
||||
y->left()->parent = y;
|
||||
y->is_red(node->is_red());
|
||||
}
|
||||
if (!y_original_color)
|
||||
rebalance_after_erase(x, x_parent);
|
||||
|
@ -685,28 +701,28 @@ class set
|
|||
--size_;
|
||||
}
|
||||
|
||||
void left_rotate(node_type* x)
|
||||
optimizesize void left_rotate(node_type* x)
|
||||
{
|
||||
node_type* y = x->right;
|
||||
x->right = y->left;
|
||||
if (y->left != nullptr)
|
||||
y->left->parent = x;
|
||||
x->right = y->left();
|
||||
if (y->left() != nullptr)
|
||||
y->left()->parent = x;
|
||||
y->parent = x->parent;
|
||||
if (x->parent == nullptr) {
|
||||
root_ = y;
|
||||
} else if (x == x->parent->left) {
|
||||
x->parent->left = y;
|
||||
} else if (x == x->parent->left()) {
|
||||
x->parent->left(y);
|
||||
} else {
|
||||
x->parent->right = y;
|
||||
}
|
||||
y->left = x;
|
||||
y->left(x);
|
||||
x->parent = y;
|
||||
}
|
||||
|
||||
void right_rotate(node_type* y)
|
||||
optimizesize void right_rotate(node_type* y)
|
||||
{
|
||||
node_type* x = y->left;
|
||||
y->left = x->right;
|
||||
node_type* x = y->left();
|
||||
y->left(x->right);
|
||||
if (x->right != nullptr)
|
||||
x->right->parent = y;
|
||||
x->parent = y->parent;
|
||||
|
@ -715,18 +731,18 @@ class set
|
|||
} else if (y == y->parent->right) {
|
||||
y->parent->right = x;
|
||||
} else {
|
||||
y->parent->left = x;
|
||||
y->parent->left(x);
|
||||
}
|
||||
x->right = y;
|
||||
y->parent = x;
|
||||
}
|
||||
|
||||
void transplant(node_type* u, node_type* v)
|
||||
optimizesize void transplant(node_type* u, node_type* v)
|
||||
{
|
||||
if (u->parent == nullptr) {
|
||||
root_ = v;
|
||||
} else if (u == u->parent->left) {
|
||||
u->parent->left = v;
|
||||
} else if (u == u->parent->left()) {
|
||||
u->parent->left(v);
|
||||
} else {
|
||||
u->parent->right = v;
|
||||
}
|
||||
|
@ -734,10 +750,10 @@ class set
|
|||
v->parent = u->parent;
|
||||
}
|
||||
|
||||
void checker(const node_type* node,
|
||||
const node_type* parent,
|
||||
int black_count,
|
||||
int& black_height) const
|
||||
optimizesize void checker(const node_type* node,
|
||||
const node_type* parent,
|
||||
int black_count,
|
||||
int& black_height) const
|
||||
{
|
||||
if (node == nullptr) {
|
||||
// Leaf nodes are considered black
|
||||
|
@ -753,117 +769,121 @@ class set
|
|||
// ILLEGAL TREE: Parent link is incorrect
|
||||
__builtin_trap();
|
||||
if (parent) {
|
||||
if (parent->left == node && !comp_(node->value, parent->value))
|
||||
if (parent->left() == node && !comp_(node->value, parent->value))
|
||||
// ILLEGAL TREE: Binary search property violated on left child
|
||||
__builtin_trap();
|
||||
if (parent->right == node && !comp_(parent->value, node->value))
|
||||
// ILLEGAL TREE: Binary search property violated on right child
|
||||
__builtin_trap();
|
||||
}
|
||||
if (!node->is_red) {
|
||||
if (!node->is_red()) {
|
||||
black_count++;
|
||||
} else if (parent != nullptr && parent->is_red) {
|
||||
} else if (parent != nullptr && parent->is_red()) {
|
||||
// ILLEGAL TREE: Red node has red child
|
||||
__builtin_trap();
|
||||
}
|
||||
checker(node->left, node, black_count, black_height);
|
||||
checker(node->left(), node, black_count, black_height);
|
||||
checker(node->right, node, black_count, black_height);
|
||||
}
|
||||
|
||||
void rebalance_after_insert(node_type* node)
|
||||
optimizesize void rebalance_after_insert(node_type* node)
|
||||
{
|
||||
node->is_red = true;
|
||||
while (node != root_ && node->parent->is_red) {
|
||||
if (node->parent == node->parent->parent->left) {
|
||||
node->is_red(true);
|
||||
while (node != root_ && node->parent->is_red()) {
|
||||
if (node->parent == node->parent->parent->left()) {
|
||||
node_type* uncle = node->parent->parent->right;
|
||||
if (uncle && uncle->is_red) {
|
||||
node->parent->is_red = false;
|
||||
uncle->is_red = false;
|
||||
node->parent->parent->is_red = true;
|
||||
if (uncle && uncle->is_red()) {
|
||||
node->parent->is_red(false);
|
||||
uncle->is_red(false);
|
||||
node->parent->parent->is_red(true);
|
||||
node = node->parent->parent;
|
||||
} else {
|
||||
if (node == node->parent->right) {
|
||||
node = node->parent;
|
||||
left_rotate(node);
|
||||
}
|
||||
node->parent->is_red = false;
|
||||
node->parent->parent->is_red = true;
|
||||
node->parent->is_red(false);
|
||||
node->parent->parent->is_red(true);
|
||||
right_rotate(node->parent->parent);
|
||||
}
|
||||
} else {
|
||||
node_type* uncle = node->parent->parent->left;
|
||||
if (uncle && uncle->is_red) {
|
||||
node->parent->is_red = false;
|
||||
uncle->is_red = false;
|
||||
node->parent->parent->is_red = true;
|
||||
node_type* uncle = node->parent->parent->left();
|
||||
if (uncle && uncle->is_red()) {
|
||||
node->parent->is_red(false);
|
||||
uncle->is_red(false);
|
||||
node->parent->parent->is_red(true);
|
||||
node = node->parent->parent;
|
||||
} else {
|
||||
if (node == node->parent->left) {
|
||||
if (node == node->parent->left()) {
|
||||
node = node->parent;
|
||||
right_rotate(node);
|
||||
}
|
||||
node->parent->is_red = false;
|
||||
node->parent->parent->is_red = true;
|
||||
node->parent->is_red(false);
|
||||
node->parent->parent->is_red(true);
|
||||
left_rotate(node->parent->parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
root_->is_red = false;
|
||||
root_->is_red(false);
|
||||
}
|
||||
|
||||
void rebalance_after_erase(node_type* node, node_type* parent)
|
||||
optimizesize void rebalance_after_erase(node_type* node, node_type* parent)
|
||||
{
|
||||
while (node != root_ && (node == nullptr || !node->is_red)) {
|
||||
if (node == parent->left) {
|
||||
while (node != root_ && (node == nullptr || !node->is_red())) {
|
||||
if (node == parent->left()) {
|
||||
node_type* sibling = parent->right;
|
||||
if (sibling->is_red) {
|
||||
sibling->is_red = false;
|
||||
parent->is_red = true;
|
||||
if (sibling->is_red()) {
|
||||
sibling->is_red(false);
|
||||
parent->is_red(true);
|
||||
left_rotate(parent);
|
||||
sibling = parent->right;
|
||||
}
|
||||
if ((sibling->left == nullptr || !sibling->left->is_red) &&
|
||||
(sibling->right == nullptr || !sibling->right->is_red)) {
|
||||
sibling->is_red = true;
|
||||
if ((sibling->left() == nullptr ||
|
||||
!sibling->left()->is_red()) &&
|
||||
(sibling->right == nullptr || !sibling->right->is_red())) {
|
||||
sibling->is_red(true);
|
||||
node = parent;
|
||||
parent = node->parent;
|
||||
} else {
|
||||
if (sibling->right == nullptr || !sibling->right->is_red) {
|
||||
sibling->left->is_red = false;
|
||||
sibling->is_red = true;
|
||||
if (sibling->right == nullptr ||
|
||||
!sibling->right->is_red()) {
|
||||
sibling->left()->is_red(false);
|
||||
sibling->is_red(true);
|
||||
right_rotate(sibling);
|
||||
sibling = parent->right;
|
||||
}
|
||||
sibling->is_red = parent->is_red;
|
||||
parent->is_red = false;
|
||||
sibling->right->is_red = false;
|
||||
sibling->is_red(parent->is_red());
|
||||
parent->is_red(false);
|
||||
sibling->right->is_red(false);
|
||||
left_rotate(parent);
|
||||
node = root_;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
node_type* sibling = parent->left;
|
||||
if (sibling->is_red) {
|
||||
sibling->is_red = false;
|
||||
parent->is_red = true;
|
||||
node_type* sibling = parent->left();
|
||||
if (sibling->is_red()) {
|
||||
sibling->is_red(false);
|
||||
parent->is_red(true);
|
||||
right_rotate(parent);
|
||||
sibling = parent->left;
|
||||
sibling = parent->left();
|
||||
}
|
||||
if ((sibling->right == nullptr || !sibling->right->is_red) &&
|
||||
(sibling->left == nullptr || !sibling->left->is_red)) {
|
||||
sibling->is_red = true;
|
||||
if ((sibling->right == nullptr || !sibling->right->is_red()) &&
|
||||
(sibling->left() == nullptr ||
|
||||
!sibling->left()->is_red())) {
|
||||
sibling->is_red(true);
|
||||
node = parent;
|
||||
parent = node->parent;
|
||||
} else {
|
||||
if (sibling->left == nullptr || !sibling->left->is_red) {
|
||||
sibling->right->is_red = false;
|
||||
sibling->is_red = true;
|
||||
if (sibling->left() == nullptr ||
|
||||
!sibling->left()->is_red()) {
|
||||
sibling->right->is_red(false);
|
||||
sibling->is_red(true);
|
||||
left_rotate(sibling);
|
||||
sibling = parent->left;
|
||||
sibling = parent->left();
|
||||
}
|
||||
sibling->is_red = parent->is_red;
|
||||
parent->is_red = false;
|
||||
sibling->left->is_red = false;
|
||||
sibling->is_red(parent->is_red());
|
||||
parent->is_red(false);
|
||||
sibling->left()->is_red(false);
|
||||
right_rotate(parent);
|
||||
node = root_;
|
||||
break;
|
||||
|
@ -871,7 +891,7 @@ class set
|
|||
}
|
||||
}
|
||||
if (node != nullptr)
|
||||
node->is_red = false;
|
||||
node->is_red(false);
|
||||
}
|
||||
|
||||
node_type* root_;
|
||||
|
|
|
@ -18,6 +18,7 @@ TEST_CTL_DIRECTDEPS = \
|
|||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_PROC \
|
||||
LIBC_STDIO \
|
||||
LIBC_STDIO \
|
||||
LIBC_THREAD \
|
||||
|
|
92
test/ctl/set_bench.cc
Normal file
92
test/ctl/set_bench.cc
Normal file
|
@ -0,0 +1,92 @@
|
|||
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
|
||||
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
||||
//
|
||||
// Copyright 2024 Justine Alexandra Roberts Tunney
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for
|
||||
// any purpose with or without fee is hereby granted, provided that the
|
||||
// above copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include "ctl/set.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/mem/leaks.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
|
||||
// #include <set>
|
||||
// #define ctl std
|
||||
// #define check() size()
|
||||
|
||||
#define BENCH(ITERATIONS, WORK_PER_RUN, CODE) \
|
||||
do { \
|
||||
struct timespec start = timespec_real(); \
|
||||
for (int __i = 0; __i < ITERATIONS; ++__i) { \
|
||||
asm volatile("" ::: "memory"); \
|
||||
CODE; \
|
||||
} \
|
||||
long long work = (WORK_PER_RUN) * (ITERATIONS); \
|
||||
double nanos = \
|
||||
(timespec_tonanos(timespec_sub(timespec_real(), start)) + work - \
|
||||
1) / \
|
||||
(double)work; \
|
||||
printf("%10g ns %2dx %s\n", nanos, (ITERATIONS), #CODE); \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
rand32(void)
|
||||
{
|
||||
/* Knuth, D.E., "The Art of Computer Programming," Vol 2,
|
||||
Seminumerical Algorithms, Third Edition, Addison-Wesley, 1998,
|
||||
p. 106 (line 26) & p. 108 */
|
||||
static unsigned long long lcg = 1;
|
||||
lcg *= 6364136223846793005;
|
||||
lcg += 1442695040888963407;
|
||||
return lcg >> 32;
|
||||
}
|
||||
|
||||
void
|
||||
eat(int x)
|
||||
{
|
||||
}
|
||||
|
||||
void (*pEat)(int) = eat;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
|
||||
{
|
||||
long x = 0;
|
||||
ctl::set<long> s;
|
||||
BENCH(1000000, 1, s.insert(rand32() % 1000000));
|
||||
// s.check();
|
||||
BENCH(1000000, 1, {
|
||||
auto i = s.find(rand32() % 1000000);
|
||||
if (i != s.end())
|
||||
x += *i;
|
||||
});
|
||||
BENCH(1000000, 1, {
|
||||
auto i = s.lower_bound(rand32() % 1000000);
|
||||
if (i != s.end())
|
||||
x += *i;
|
||||
});
|
||||
BENCH(1000000, 1, s.erase(rand32() % 1000000));
|
||||
eat(x);
|
||||
}
|
||||
|
||||
struct rusage ru;
|
||||
getrusage(RUSAGE_SELF, &ru);
|
||||
printf("%,10d kb peak rss\n", ru.ru_maxrss);
|
||||
|
||||
CheckForMemoryLeaks();
|
||||
}
|
Loading…
Reference in a new issue