1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

celldb in-memory mode (--celldb-in-memory option)

This commit is contained in:
birydrad 2024-09-09 18:08:15 +02:00
parent 420029b056
commit 1723562748
48 changed files with 1966 additions and 201 deletions

View file

@ -19,6 +19,7 @@
#pragma once
#include "common/refcnt.hpp"
#include "common/bitstring.h"
#include "td/utils/HashSet.h"
#include "vm/cells/CellHash.h"
#include "vm/cells/CellTraits.h"
@ -86,4 +87,31 @@ class Cell : public CellTraits {
};
std::ostream& operator<<(std::ostream& os, const Cell& c);
using is_transparent = void; // Pred to use
inline vm::CellHash as_cell_hash(const Ref<Cell>& cell) {
return cell->get_hash();
}
inline vm::CellHash as_cell_hash(td::Slice hash) {
return vm::CellHash::from_slice(hash);
}
inline vm::CellHash as_cell_hash(vm::CellHash hash) {
return hash;
}
struct CellEqF {
using is_transparent = void; // Pred to use
template <class A, class B>
bool operator()(const A& a, const B& b) const {
return as_cell_hash(a) == as_cell_hash(b);
}
};
struct CellHashF {
using is_transparent = void; // Pred to use
using transparent_key_equal = CellEqF;
template <class T>
size_t operator()(const T& value) const {
return cell_hash_slice_hash(as_cell_hash(value).as_slice());
}
};
using CellHashSet = td::HashSet<td::Ref<Cell>, CellHashF, CellEqF>;
} // namespace vm

View file

@ -617,7 +617,7 @@ std::string CellBuilder::to_hex() const {
int len = serialize(buff, sizeof(buff));
char hex_buff[Cell::max_serialized_bytes * 2 + 1];
for (int i = 0; i < len; i++) {
sprintf(hex_buff + 2 * i, "%02x", buff[i]);
snprintf(hex_buff + 2 * i, sizeof(hex_buff) - 2 * i, "%02x", buff[i]);
}
return hex_buff;
}

View file

@ -74,13 +74,17 @@ struct CellHash {
};
} // namespace vm
inline size_t cell_hash_slice_hash(td::Slice hash) {
// use offset 8, because in db keys are grouped by first bytes.
return td::as<size_t>(hash.ubegin() + 8);
}
namespace std {
template <>
struct hash<vm::CellHash> {
typedef vm::CellHash argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const noexcept {
return td::as<size_t>(s.as_slice().ubegin());
return cell_hash_slice_hash(s.as_slice());
}
};
} // namespace std

View file

@ -976,7 +976,7 @@ void CellSlice::dump(std::ostream& os, int level, bool endl) const {
os << "; refs: " << refs_st << ".." << refs_en;
if (level > 2) {
char tmp[64];
std::sprintf(tmp, "; ptr=data+%ld; z=%016llx",
std::snprintf(tmp, sizeof(tmp), "; ptr=data+%ld; z=%016llx",
static_cast<long>(ptr && cell.not_null() ? ptr - cell->get_data() : -1), static_cast<long long>(z));
os << tmp << " (have " << size() << " bits; " << zd << " preloaded)";
}

View file

@ -20,6 +20,15 @@
namespace vm {
namespace detail {
template <class CellT>
struct DefaultAllocator {
template <class T, class... ArgsT>
std::unique_ptr<CellT> make_unique(ArgsT&&... args) {
return std::make_unique<T>(std::forward<ArgsT>(args)...);
}
};
template <class CellT, size_t Size = 0>
class CellWithArrayStorage : public CellT {
public:
@ -29,14 +38,14 @@ class CellWithArrayStorage : public CellT {
~CellWithArrayStorage() {
CellT::destroy_storage(get_storage());
}
template <class... ArgsT>
static std::unique_ptr<CellT> create(size_t storage_size, ArgsT&&... args) {
template <class Allocator, class... ArgsT>
static auto create(Allocator allocator, size_t storage_size, ArgsT&&... args) {
static_assert(CellT::max_storage_size <= 40 * 8, "");
//size = 128 + 32 + 8;
auto size = (storage_size + 7) / 8;
#define CASE(size) \
case (size): \
return std::make_unique<CellWithArrayStorage<CellT, (size)*8>>(std::forward<ArgsT>(args)...);
return allocator. template make_unique<CellWithArrayStorage<CellT, (size) * 8>>(std::forward<ArgsT>(args)...);
#define CASE2(offset) CASE(offset) CASE(offset + 1)
#define CASE8(offset) CASE2(offset) CASE2(offset + 2) CASE2(offset + 4) CASE2(offset + 6)
#define CASE32(offset) CASE8(offset) CASE8(offset + 8) CASE8(offset + 16) CASE8(offset + 24)
@ -48,6 +57,10 @@ class CellWithArrayStorage : public CellT {
LOG(FATAL) << "TOO BIG " << storage_size;
UNREACHABLE();
}
template <class... ArgsT>
static std::unique_ptr<CellT> create(size_t storage_size, ArgsT&&... args) {
return create(DefaultAllocator<CellT>{}, storage_size, std::forward<ArgsT>(args)...);
}
private:
alignas(alignof(void*)) char storage_[Size];

View file

@ -25,7 +25,44 @@
#include "vm/cells/CellWithStorage.h"
namespace vm {
thread_local bool DataCell::use_arena = false;
namespace {
template <class CellT>
struct ArenaAllocator {
template <class T, class... ArgsT>
std::unique_ptr<CellT> make_unique(ArgsT&&... args) {
auto* ptr = fast_alloc(sizeof(T));
T* obj = new (ptr) T(std::forward<ArgsT>(args)...);
return std::unique_ptr<T>(obj);
}
private:
td::MutableSlice alloc_batch() {
size_t batch_size = 1 << 20;
auto batch = std::make_unique<char[]>(batch_size);
return td::MutableSlice(batch.release(), batch_size);
}
char* fast_alloc(size_t size) {
thread_local td::MutableSlice batch;
auto aligned_size = (size + 7) / 8 * 8;
if (batch.size() < size) {
batch = alloc_batch();
}
auto res = batch.begin();
batch.remove_prefix(aligned_size);
return res;
}
};
}
std::unique_ptr<DataCell> DataCell::create_empty_data_cell(Info info) {
if (use_arena) {
ArenaAllocator<DataCell> allocator;
auto res = detail::CellWithArrayStorage<DataCell>::create(allocator, info.get_storage_size(), info);
// this is dangerous
Ref<DataCell>(res.get()).release();
return res;
}
return detail::CellWithUniquePtrStorage<DataCell>::create(info.get_storage_size(), info);
}
@ -359,7 +396,7 @@ std::string DataCell::to_hex() const {
int len = serialize(buff, sizeof(buff));
char hex_buff[max_serialized_bytes * 2 + 1];
for (int i = 0; i < len; i++) {
sprintf(hex_buff + 2 * i, "%02x", buff[i]);
snprintf(hex_buff + 2 * i, sizeof(hex_buff) - 2 * i, "%02x", buff[i]);
}
return hex_buff;
}

View file

@ -27,6 +27,9 @@ namespace vm {
class DataCell : public Cell {
public:
// NB: cells created with use_arena=true are never freed
static thread_local bool use_arena;
DataCell(const DataCell& other) = delete;
~DataCell() override;
@ -121,10 +124,6 @@ class DataCell : public Cell {
void destroy_storage(char* storage);
explicit DataCell(Info info);
Cell* get_ref_raw_ptr(unsigned idx) const {
DCHECK(idx < get_refs_cnt());
return info_.get_refs(get_storage())[idx];
}
public:
td::Result<LoadedCell> load_cell() const override {
@ -152,6 +151,20 @@ class DataCell : public Cell {
return Ref<Cell>(get_ref_raw_ptr(idx));
}
Cell* get_ref_raw_ptr(unsigned idx) const {
DCHECK(idx < get_refs_cnt());
return info_.get_refs(get_storage())[idx];
}
Ref<Cell> reset_ref_unsafe(unsigned idx, Ref<Cell> ref, bool check_hash = true) {
CHECK(idx < get_refs_cnt());
auto refs = info_.get_refs(get_storage());
CHECK(!check_hash || refs[idx]->get_hash() == ref->get_hash());
auto res = Ref<Cell>(refs[idx], Ref<Cell>::acquire_t{}); // call destructor
refs[idx] = ref.release();
return res;
}
td::uint32 get_virtualization() const override {
return info_.virtualization_;
}
@ -173,6 +186,9 @@ class DataCell : public Cell {
return ((get_bits() + 23) >> 3) +
(with_hashes ? get_level_mask().get_hashes_count() * (hash_bytes + depth_bytes) : 0);
}
size_t get_storage_size() const {
return info_.get_storage_size();
}
int serialize(unsigned char* buff, int buff_size, bool with_hashes = false) const;
std::string serialize() const;
std::string to_hex() const;
@ -207,6 +223,9 @@ class DataCell : public Cell {
};
std::ostream& operator<<(std::ostream& os, const DataCell& c);
inline CellHash as_cell_hash(const Ref<DataCell>& cell) {
return cell->get_hash();
}
} // namespace vm

View file

@ -30,18 +30,27 @@ struct PrunnedCellInfo {
template <class ExtraT>
class PrunnedCell : public Cell {
public:
ExtraT& get_extra() {
return extra_;
}
const ExtraT& get_extra() const {
return extra_;
}
static td::Result<Ref<PrunnedCell<ExtraT>>> create(const PrunnedCellInfo& prunned_cell_info, ExtraT&& extra) {
return create(detail::DefaultAllocator<PrunnedCell<ExtraT>>(), prunned_cell_info, std::forward<ExtraT>(extra));
}
template <class AllocatorT>
static td::Result<Ref<PrunnedCell<ExtraT>>> create(AllocatorT allocator, const PrunnedCellInfo& prunned_cell_info,
ExtraT&& extra) {
auto level_mask = prunned_cell_info.level_mask;
if (level_mask.get_level() > max_level) {
return td::Status::Error("Level is too big");
}
Info info(level_mask);
auto prunned_cell =
detail::CellWithUniquePtrStorage<PrunnedCell<ExtraT>>::create(info.get_storage_size(), info, std::move(extra));
detail::CellWithArrayStorage<PrunnedCell<ExtraT>>::create(allocator, info.get_storage_size(), info, std::move(extra));
TRY_STATUS(prunned_cell->init(prunned_cell_info));
return Ref<PrunnedCell<ExtraT>>(prunned_cell.release(), typename Ref<PrunnedCell<ExtraT>>::acquire_t{});
}
@ -51,6 +60,7 @@ class PrunnedCell : public Cell {
}
protected:
static constexpr auto max_storage_size = (max_level + 1) * (hash_bytes + sizeof(td::uint16));
struct Info {
Info(LevelMask level_mask) {
level_mask_ = level_mask.get_mask() & 7;