New ImmutableCString string class (#9291)
ImmutableCString is a string class similar to CString except it's
immutable. ImmutableCString occupies 16 bytes instead of 8 for CString.
However, ImmutableCString is able to avoid memory allocation when
constructed from a string literal, and in that way it us similar
to StaticString.
ImmutableCString can be auto converted from StaticString.
The backend tag tracking is updated to use ImmutableCString and
the FrameGraph resource manager us updated to use StaticString.
Together these changes significantly cut down heap allocations due to
internal tagging.
We also add optional tracking to {Immutable}CString.
This commit is contained in:
@@ -17,23 +17,49 @@
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace utils {
|
||||
|
||||
namespace {
|
||||
struct CStringStats {
|
||||
std::atomic_int32_t alive = { 0 };
|
||||
std::atomic_int32_t ctor = { 0 };
|
||||
};
|
||||
|
||||
CStringStats gCStringStats{};
|
||||
constexpr size_t CSTRING_LOG_INTERVAL = 10000;
|
||||
}
|
||||
|
||||
void CString::do_tracking(bool ctor) {
|
||||
if (ctor) {
|
||||
gCStringStats.ctor.fetch_add(1, std::memory_order_relaxed);
|
||||
gCStringStats.alive.fetch_add(1, std::memory_order_relaxed);
|
||||
} else {
|
||||
gCStringStats.alive.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
static std::atomic_int32_t sCtorSinceLastLog = { 0 };
|
||||
if (UTILS_UNLIKELY(sCtorSinceLastLog.fetch_add(1, std::memory_order_relaxed) == CSTRING_LOG_INTERVAL)) {
|
||||
LOG(INFO) << "CString stats: "
|
||||
<< gCStringStats.alive.load(std::memory_order_relaxed) << " alive, "
|
||||
<< gCStringStats.ctor.load(std::memory_order_relaxed) << " ctor";
|
||||
sCtorSinceLastLog.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
CString::CString(const char* cstr, size_t const length) {
|
||||
track(true);
|
||||
if (length && cstr) {
|
||||
|
||||
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
|
||||
p->length = size_type(length);
|
||||
mCStr = reinterpret_cast<value_type*>(p + 1);
|
||||
@@ -44,6 +70,7 @@ CString::CString(const char* cstr, size_t const length) {
|
||||
}
|
||||
|
||||
CString::CString(size_t const length) {
|
||||
track(true);
|
||||
if (length) {
|
||||
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
|
||||
p->length = size_type(length);
|
||||
@@ -59,14 +86,13 @@ CString::CString(const CString& rhs)
|
||||
|
||||
CString& CString::operator=(const CString& rhs) {
|
||||
if (this != &rhs) {
|
||||
auto *const p = mData ? mData - 1 : nullptr;
|
||||
new(this) CString(rhs);
|
||||
std::free(p);
|
||||
CString(rhs).swap(*this);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CString::~CString() noexcept {
|
||||
track(false);
|
||||
if (mData) {
|
||||
std::free(mData - 1);
|
||||
}
|
||||
@@ -75,6 +101,10 @@ CString::~CString() noexcept {
|
||||
CString& CString::replace(size_type const pos, size_type len, char const* str, size_t const l) & noexcept {
|
||||
assert(pos <= size());
|
||||
|
||||
if (UTILS_UNLIKELY(!l && !len)) { // nothing to do
|
||||
return *this;
|
||||
}
|
||||
|
||||
len = std::min(len, size() - pos);
|
||||
|
||||
const size_type newSize = size() - len + l;
|
||||
|
||||
Reference in New Issue
Block a user