Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions benchmark/benchmark_native_object.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "benchmark/benchmark.h"
#include "fakelua.h"

#include <string>

using namespace fakelua;

// NativeObject 热路径基准:字段读写、对象创建、父子绑定与深树叶子写入。
// 这些用例只使用长期存在的公开 API,因此同一份基准可分别跑在加版本号前后的构建上。

static void BM_NativeObjectSetInt(benchmark::State &state) {
auto *s = FakeluaNewState();
auto &mgr = GetNativeObjectManager(s);
int64_t gid = mgr.CreateGroup();
auto *obj = mgr.Create(gid, "bench", 1);
for (auto _: state) {
obj->SetInt("hp", 42);
benchmark::DoNotOptimize(obj);
}
mgr.Clear();
FakeluaDeleteState(s);
}

static void BM_NativeObjectGetInt(benchmark::State &state) {
auto *s = FakeluaNewState();
auto &mgr = GetNativeObjectManager(s);
int64_t gid = mgr.CreateGroup();
auto *obj = mgr.Create(gid, "bench", 1);
obj->SetInt("hp", 42);
for (auto _: state) {
benchmark::DoNotOptimize(obj->GetInt("hp"));
}
mgr.Clear();
FakeluaDeleteState(s);
}

static void BM_NativeObjectSetString(benchmark::State &state) {
auto *s = FakeluaNewState();
auto &mgr = GetNativeObjectManager(s);
int64_t gid = mgr.CreateGroup();
auto *obj = mgr.Create(gid, "bench", 1);
const std::string value = "benchmark-value";
for (auto _: state) {
obj->SetString("name", value);
benchmark::DoNotOptimize(obj);
}
mgr.Clear();
FakeluaDeleteState(s);
}

static void BM_NativeObjectCreateDestroy(benchmark::State &state) {
auto *s = FakeluaNewState();
auto &mgr = GetNativeObjectManager(s);
int64_t gid = mgr.CreateGroup();
int64_t id = 0;
for (auto _: state) {
auto *obj = mgr.Create(gid, "bench", ++id);
benchmark::DoNotOptimize(obj);
mgr.DestroySingle("bench", id);
}
mgr.Clear();
FakeluaDeleteState(s);
}

static void BM_NativeObjectSetObject(benchmark::State &state) {
auto *s = FakeluaNewState();
auto &mgr = GetNativeObjectManager(s);
int64_t gid = mgr.CreateGroup();
auto *parent = mgr.Create(gid, "bench_parent", 1);
auto *child = mgr.Create(gid, "bench_child", 2);
for (auto _: state) {
parent->SetObject("child", child);
}
mgr.Clear();
FakeluaDeleteState(s);
}

// 8 层父子链上写叶子:用于观察深层对象上的写入开销(简化后应与浅层写入一致)。
static void BM_NativeObjectDeepLeafWrite(benchmark::State &state) {
auto *s = FakeluaNewState();
auto &mgr = GetNativeObjectManager(s);
int64_t gid = mgr.CreateGroup();
NativeObject *cur = mgr.Create(gid, "bench_node", 1);
for (int i = 0; i < 8; ++i) {
auto *child = mgr.Create(gid, "bench_node", i + 2);
cur->SetObject("child", child);
cur = child;
}
for (auto _: state) {
cur->SetInt("leaf", 1);
benchmark::DoNotOptimize(cur);
}
mgr.Clear();
FakeluaDeleteState(s);
}

BENCHMARK(BM_NativeObjectSetInt);
BENCHMARK(BM_NativeObjectGetInt);
BENCHMARK(BM_NativeObjectSetString);
BENCHMARK(BM_NativeObjectCreateDestroy);
BENCHMARK(BM_NativeObjectSetObject);
BENCHMARK(BM_NativeObjectDeepLeafWrite);
46 changes: 46 additions & 0 deletions include/fakelua.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <algorithm>
#include <cstdint>
#include <format>
#include <functional>
#include <memory>
Expand Down Expand Up @@ -812,6 +813,36 @@ class NativeObject {
void Del(std::string_view key);
void Clear();

// ---- 对象级变更版本号(增量存档 / C++ 镜像同步 / UI 缓存失效)----
// 语义边界:只描述「这个对象自身」,不包含它的子树。
// * 本对象自身的写入(字段、方法、id/group、finalizer、Clear、直接子对象的挂载/卸载)
// 会让本对象的版本号单调变大;
// * 子对象内部的写入只改变子对象自己的版本号,不会改变本对象;
// * 所有读操作都不改变版本号。
// 返回的是全局单调时间戳,不是计数器;只应与同一对象自己上一次记录的版本号比较,
// 不应跨对象比较大小。
// 调用方:记下 v = obj->GetVersion();之后用 obj->IsChangedSince(v) 做 O(1) 判断。
// 需要「整棵子树」粒度时,调用方自行遍历对象图、记录读到的每个对象的版本集合,
// 再在上层聚合判断(遍历只读指针,成本远低于字段序列化)。
// 版本号不写入存档、不改变既有数据格式;对象销毁后仍保留最后一次版本号。
[[nodiscard]] uint64_t GetVersion() const;
[[nodiscard]] bool IsChangedSince(uint64_t version) const;

// 显式标记本对象(仅本对象,不含父对象/子对象)已变更。
// 用于通过字段写入无法表达的副作用修改,例如 native/container 内部容器内容、
// 外部资源状态(io/socket 等)改变。
void MarkModified();

// 调试自检(编译期开关):仅当库以 FAKELUA_NATIVE_OBJECT_DEBUG_HASH 编译时,
// 每次写入才维护内容哈希;SelfCheck 重新计算本对象字段/方法的内容哈希并与记录值
// 比对,用于交叉验证是否有写入点漏标版本。
// 默认构建不包含哈希字段与维护逻辑:SetVersionDebugEnabled 为空操作,
// SelfCheck 恒返回 true(不做任何检查)。
// 不做子树递归:版本号是对象自身粒度,且不再维护父子边,
// recursive 参数为兼容保留、当前被忽略。
static void SetVersionDebugEnabled(bool enabled);
[[nodiscard]] static bool SelfCheck(const NativeObject *obj, bool recursive = true);

// 销毁回调
// 设置 NativeObject 被 Destroy 时(含管理器 Clear / DestroyGroup)的回调,
// 用于释放该对象拥有的 C++ 资源(如 socket)。回调内不应再操作 Lua 状态。
Expand All @@ -832,11 +863,26 @@ class NativeObject {
private:
struct Impl;
Impl *impl_;
// 对象级版本戳保存在 NativeObject 本身(不随 Impl 释放),
// 因此 Destroy 之后仍能查到最后一次版本,保持单调性。
uint64_t version_ = 0;

explicit NativeObject(std::string type_name);
~NativeObject();

// 版本号内部维护:TouchVersion 只提升本对象自身(O(1),无任何父子边簿记)。
void TouchVersion();
#if defined(FAKELUA_NATIVE_OBJECT_DEBUG_HASH)
// 调试哈希维护(仅调试构建存在)。
void RefreshDebugHash();
[[nodiscard]] static uint64_t ComputeContentHash(const NativeObject *obj);
#endif

// 内部申请与释放(只能由 NativeObjectManager 管理调配)
// 约束:销毁前必须先从所有父对象的字段中移除该对象(Del/SetNil/Clear 或覆盖字段)。
// 实现不维护父子边索引,无法自动通知或清空父对象字段;销毁仍被引用/嵌套的对象
// 属于调用方错误(父对象字段会保持悬空指针)。整组销毁(DestroyGroup/Clear)不受此限,
// 因为组内对象同时销毁。
static NativeObject *Create(int64_t group_id, std::string type_name, int64_t id = 0);
static void Destroy(NativeObject *obj);

Expand Down
7 changes: 7 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ if (USE_COV)
target_compile_definitions(fakelua PRIVATE "FAKELUA_JIT_COV_DIR=\"${CMAKE_BINARY_DIR}/jit_cov\"")
endif ()

# NativeObject 版本号调试自检:开启后每个对象额外维护内容哈希(仅调试/排障用,默认关闭,
# 默认构建不占这些字段)。配合 NativeObject::SetVersionDebugEnabled / SelfCheck 使用。
option(FAKELUA_NATIVE_OBJECT_DEBUG_HASH "Enable NativeObject version content-hash self-check" OFF)
if (FAKELUA_NATIVE_OBJECT_DEBUG_HASH)
target_compile_definitions(fakelua PRIVATE FAKELUA_NATIVE_OBJECT_DEBUG_HASH=1)
endif ()

find_package(Threads REQUIRED)

if (WIN32)
Expand Down
55 changes: 44 additions & 11 deletions src/native/container/native_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ CVar ClearOf(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
c->set.clear();
break;
}
// 容器内容存放在 NativeObject 之外的 C++ 结构中,字段写入无法感知,需要显式标记版本变更。
self->MarkModified();
return inter::NativeToFakeluaNil(s);
}

Expand All @@ -257,12 +259,14 @@ CVar ClearOf(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
CVar DequePushBack(NativeObject *self, State *s, CVar *args, int n) {
if (n < 1) ThrowBadArgument(1, "deque:push_back", "value expected");
Require(self)->deque.push_back(PersistValue(inter::GetNativeArg(s, args, n, 0), "deque:push_back"));
self->MarkModified();
return inter::NativeToFakeluaNil(s);
}

CVar DequePushFront(NativeObject *self, State *s, CVar *args, int n) {
if (n < 1) ThrowBadArgument(1, "deque:push_front", "value expected");
Require(self)->deque.push_front(PersistValue(inter::GetNativeArg(s, args, n, 0), "deque:push_front"));
self->MarkModified();
return inter::NativeToFakeluaNil(s);
}

Expand All @@ -271,6 +275,7 @@ CVar DequePopBack(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
if (d.empty()) return inter::NativeToFakeluaNil(s);
NativeField v = std::move(d.back());
d.pop_back();
self->MarkModified();
return FieldToLua(v, s);
}

Expand All @@ -279,6 +284,7 @@ CVar DequePopFront(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
if (d.empty()) return inter::NativeToFakeluaNil(s);
NativeField v = std::move(d.front());
d.pop_front();
self->MarkModified();
return FieldToLua(v, s);
}

Expand Down Expand Up @@ -310,6 +316,7 @@ CVar DequeSet(NativeObject *self, State *s, CVar *args, int n) {
ThrowBadArgument(1, "deque:set", "index out of range");
}
d[static_cast<size_t>(i - 1)] = PersistValue(inter::GetNativeArg(s, args, n, 1), "deque:set");
self->MarkModified();
return inter::NativeToFakeluaNil(s);
}

Expand All @@ -329,6 +336,7 @@ CVar MapSet(NativeObject *self, State *s, CVar *args, int n) {
auto &m = Require(self)->map;
ContainerKey k = ContainerKey::FromCVar(inter::GetNativeArg(s, args, n, 0));
m[std::move(k)] = PersistValue(inter::GetNativeArg(s, args, n, 1), "map:set");
self->MarkModified();
return inter::NativeToFakeluaNil(s);
}

Expand All @@ -350,6 +358,7 @@ CVar MapErase(NativeObject *self, State *s, CVar *args, int n) {
if (n < 1) ThrowBadArgument(1, "map:erase", "key expected");
auto &m = Require(self)->map;
bool ok = m.erase(ContainerKey::FromCVar(inter::GetNativeArg(s, args, n, 0))) > 0;
self->MarkModified();
return inter::NativeToFakeluaBool(s, ok);
}

Expand Down Expand Up @@ -378,6 +387,7 @@ CVar SetInsert(NativeObject *self, State *s, CVar *args, int n) {
if (n < 1) ThrowBadArgument(1, "set:insert", "value expected");
auto &st = Require(self)->set;
auto r = st.insert(ContainerKey::FromCVar(inter::GetNativeArg(s, args, n, 0)));
self->MarkModified();
return inter::NativeToFakeluaBool(s, r.second);
}

Expand All @@ -391,6 +401,7 @@ CVar SetErase(NativeObject *self, State *s, CVar *args, int n) {
if (n < 1) ThrowBadArgument(1, "set:erase", "value expected");
auto &st = Require(self)->set;
bool ok = st.erase(ContainerKey::FromCVar(inter::GetNativeArg(s, args, n, 0))) > 0;
self->MarkModified();
return inter::NativeToFakeluaBool(s, ok);
}

Expand Down Expand Up @@ -506,10 +517,14 @@ boost::container::list<NativeField> &ListOf(NativeObject *self) {
}

CVar VectorPushBack(NativeObject *self, State *s, CVar *args, int n) {
return SeqPushBack(VecOf(self), s, args, n, "vector:push_back");
auto r = SeqPushBack(VecOf(self), s, args, n, "vector:push_back");
self->MarkModified();
return r;
}
CVar VectorPopBack(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqPopBack(VecOf(self), s);
auto r = SeqPopBack(VecOf(self), s);
self->MarkModified();
return r;
}
CVar VectorFront(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqFront(VecOf(self), s);
Expand All @@ -521,17 +536,23 @@ CVar VectorAt(NativeObject *self, State *s, CVar *args, int n) {
return SeqAt(VecOf(self), s, args, n, "vector:at");
}
CVar VectorSet(NativeObject *self, State *s, CVar *args, int n) {
return SeqSet(VecOf(self), s, args, n, "vector:set");
auto r = SeqSet(VecOf(self), s, args, n, "vector:set");
self->MarkModified();
return r;
}
CVar VectorToTable(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqToTable(VecOf(self), s);
}

CVar SmallPushBack(NativeObject *self, State *s, CVar *args, int n) {
return SeqPushBack(SmallOf(self), s, args, n, "small_vector:push_back");
auto r = SeqPushBack(SmallOf(self), s, args, n, "small_vector:push_back");
self->MarkModified();
return r;
}
CVar SmallPopBack(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqPopBack(SmallOf(self), s);
auto r = SeqPopBack(SmallOf(self), s);
self->MarkModified();
return r;
}
CVar SmallFront(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqFront(SmallOf(self), s);
Expand All @@ -543,23 +564,33 @@ CVar SmallAt(NativeObject *self, State *s, CVar *args, int n) {
return SeqAt(SmallOf(self), s, args, n, "small_vector:at");
}
CVar SmallSet(NativeObject *self, State *s, CVar *args, int n) {
return SeqSet(SmallOf(self), s, args, n, "small_vector:set");
auto r = SeqSet(SmallOf(self), s, args, n, "small_vector:set");
self->MarkModified();
return r;
}
CVar SmallToTable(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqToTable(SmallOf(self), s);
}

CVar ListPushBack(NativeObject *self, State *s, CVar *args, int n) {
return SeqPushBack(ListOf(self), s, args, n, "list:push_back");
auto r = SeqPushBack(ListOf(self), s, args, n, "list:push_back");
self->MarkModified();
return r;
}
CVar ListPushFront(NativeObject *self, State *s, CVar *args, int n) {
return SeqPushFront(ListOf(self), s, args, n, "list:push_front");
auto r = SeqPushFront(ListOf(self), s, args, n, "list:push_front");
self->MarkModified();
return r;
}
CVar ListPopBack(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqPopBack(ListOf(self), s);
auto r = SeqPopBack(ListOf(self), s);
self->MarkModified();
return r;
}
CVar ListPopFront(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqPopFront(ListOf(self), s);
auto r = SeqPopFront(ListOf(self), s);
self->MarkModified();
return r;
}
CVar ListFront(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqFront(ListOf(self), s);
Expand All @@ -571,7 +602,9 @@ CVar ListAt(NativeObject *self, State *s, CVar *args, int n) {
return SeqAt(ListOf(self), s, args, n, "list:at");
}
CVar ListSet(NativeObject *self, State *s, CVar *args, int n) {
return SeqSet(ListOf(self), s, args, n, "list:set");
auto r = SeqSet(ListOf(self), s, args, n, "list:set");
self->MarkModified();
return r;
}
CVar ListToTable(NativeObject *self, State *s, CVar * /*args*/, int /*n*/) {
return SeqToTable(ListOf(self), s);
Expand Down
Loading
Loading