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:
parent
420029b056
commit
1723562748
48 changed files with 1966 additions and 201 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)";
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue