Replace std::shared_ptr with homemade, intrusive, ref_ptr
That saves a lot of memory as sizeof(SharedString) is now one pointer less.
This commit is contained in:
parent
ef26b77aa7
commit
9b057896d4
65
src/ref_ptr.hh
Normal file
65
src/ref_ptr.hh
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef ref_ptr_hh_INCLUDED
|
||||
#define ref_ptr_hh_INCLUDED
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct ref_ptr
|
||||
{
|
||||
ref_ptr() = default;
|
||||
ref_ptr(T* ptr) : m_ptr(ptr) { acquire(); }
|
||||
~ref_ptr() { release(); }
|
||||
ref_ptr(const ref_ptr& other) : m_ptr(other.m_ptr) { acquire(); }
|
||||
ref_ptr(ref_ptr&& other) : m_ptr(other.m_ptr) { other.m_ptr = nullptr; }
|
||||
|
||||
ref_ptr& operator=(const ref_ptr& other)
|
||||
{
|
||||
release();
|
||||
m_ptr = other.m_ptr;
|
||||
acquire();
|
||||
return *this;
|
||||
}
|
||||
ref_ptr& operator=(ref_ptr&& other)
|
||||
{
|
||||
release();
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* operator->() const { return m_ptr; }
|
||||
T& operator*() const { return *m_ptr; }
|
||||
|
||||
T* get() const { return m_ptr; }
|
||||
|
||||
explicit operator bool() { return m_ptr; }
|
||||
|
||||
friend bool operator==(const ref_ptr& lhs, const ref_ptr& rhs)
|
||||
{
|
||||
return lhs.m_ptr == rhs.m_ptr;
|
||||
}
|
||||
friend bool operator!=(const ref_ptr& lhs, const ref_ptr& rhs)
|
||||
{
|
||||
return lhs.m_ptr != rhs.m_ptr;
|
||||
}
|
||||
private:
|
||||
T* m_ptr = nullptr;
|
||||
|
||||
void acquire()
|
||||
{
|
||||
if (m_ptr)
|
||||
inc_ref_count(m_ptr);
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (m_ptr)
|
||||
dec_ref_count(m_ptr);
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ref_ptr_hh_INCLUDED
|
|
@ -10,16 +10,16 @@ SharedString StringRegistry::intern(StringView str)
|
|||
if (it == m_strings.end())
|
||||
{
|
||||
SharedString shared_str = str;
|
||||
it = m_strings.emplace(StringView{shared_str}, shared_str.m_storage).first;
|
||||
it = m_strings.emplace(shared_str, shared_str.m_storage).first;
|
||||
}
|
||||
return {*it->second, it->second};
|
||||
return {it->second->strview(), it->second};
|
||||
}
|
||||
|
||||
void StringRegistry::purge_unused()
|
||||
{
|
||||
for (auto it = m_strings.begin(); it != m_strings.end(); )
|
||||
{
|
||||
if (it->second.unique())
|
||||
if (it->second->refcount == 1)
|
||||
it = m_strings.erase(it);
|
||||
else
|
||||
++it;
|
||||
|
@ -28,15 +28,14 @@ void StringRegistry::purge_unused()
|
|||
|
||||
void StringRegistry::debug_stats() const
|
||||
{
|
||||
|
||||
write_debug("Shared Strings stats:");
|
||||
size_t total_refcount = 0;
|
||||
size_t total_size = 0;
|
||||
size_t count = m_strings.size();
|
||||
for (auto& st : m_strings)
|
||||
{
|
||||
total_refcount += st.second.use_count() - 1;
|
||||
total_size += (int)st.second->length();
|
||||
total_refcount += st.second->refcount - 1;
|
||||
total_size += (int)st.second->content.size();
|
||||
}
|
||||
write_debug(" data size: " + to_string(total_size) + ", mean: " + to_string((float)total_size/count));
|
||||
write_debug(" refcounts: " + to_string(total_refcount) + ", mean: " + to_string((float)total_refcount/count));
|
||||
|
|
|
@ -2,26 +2,40 @@
|
|||
#define shared_string_hh_INCLUDED
|
||||
|
||||
#include "string.hh"
|
||||
#include "ref_ptr.hh"
|
||||
#include "utils.hh"
|
||||
#include "unordered_map.hh"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
|
||||
class SharedString : public StringView
|
||||
{
|
||||
public:
|
||||
using Storage = std::basic_string<char, std::char_traits<char>,
|
||||
Allocator<char, MemoryDomain::SharedString>>;
|
||||
struct Storage
|
||||
{
|
||||
int refcount = 0;
|
||||
Vector<char, MemoryDomain::SharedString> content;
|
||||
|
||||
Storage(StringView str)
|
||||
{
|
||||
content.reserve((int)str.length() + 1);
|
||||
content.assign(str.begin(), str.end());
|
||||
content.push_back('\0');
|
||||
}
|
||||
StringView strview() const { return {&content.front(), &content.back()}; }
|
||||
|
||||
friend void inc_ref_count(Storage* s) { ++s->refcount; }
|
||||
friend void dec_ref_count(Storage* s) { if (--s->refcount == 0) delete s; }
|
||||
};
|
||||
|
||||
SharedString() = default;
|
||||
SharedString(StringView str)
|
||||
{
|
||||
if (not str.empty())
|
||||
{
|
||||
m_storage = std::make_shared<Storage>(str.begin(), str.end());
|
||||
StringView::operator=(*m_storage);
|
||||
m_storage = new Storage{str};
|
||||
StringView::operator=(m_storage->strview());
|
||||
}
|
||||
}
|
||||
struct NoCopy{};
|
||||
|
@ -39,11 +53,11 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
SharedString(StringView str, std::shared_ptr<Storage> storage)
|
||||
SharedString(StringView str, ref_ptr<Storage> storage)
|
||||
: StringView{str}, m_storage(std::move(storage)) {}
|
||||
|
||||
friend class StringRegistry;
|
||||
std::shared_ptr<Storage> m_storage;
|
||||
ref_ptr<Storage> m_storage;
|
||||
};
|
||||
|
||||
inline size_t hash_value(const SharedString& str)
|
||||
|
@ -59,7 +73,7 @@ public:
|
|||
void purge_unused();
|
||||
|
||||
private:
|
||||
UnorderedMap<StringView, std::shared_ptr<SharedString::Storage>, MemoryDomain::SharedString> m_strings;
|
||||
UnorderedMap<StringView, ref_ptr<SharedString::Storage>, MemoryDomain::SharedString> m_strings;
|
||||
};
|
||||
|
||||
inline SharedString intern(StringView str)
|
||||
|
|
Loading…
Reference in New Issue
Block a user