Comments (18)
Without even trying for_each_unordered
it solved my stack usage issue. But I'm going to try the unordered approach to see if it gives me any more speed.
from refl-cpp.
This issue seems like it could be closed, as far as I am concerned.
from refl-cpp.
Hello. The complete code is proprietary, but I can share the reflection routines:
struct needs_classname : refl::attr::usage::type {};
template <typename Func>
struct ctor_ptr : refl::attr::usage::type
{
using RT = std::remove_pointer_t<decltype(std::declval<Func>()(nullptr, nullptr))>;
static_assert(std::is_same_v<Func, RT*(*)(void*, lua_State*)>, "Func must be a function pointer with signature RT*(*)(void*, lua_State*)");
Func func;
constexpr ctor_ptr(Func func) : func(func) {}
};
template <typename Func>
struct close_func_ptr : refl::attr::usage::type
{
static_assert(std::is_member_function_pointer_v<std::remove_pointer_t<Func>> || std::is_function_v<std::remove_pointer_t<Func>>,
"Func must be a function pointer or method pointer");
Func func;
constexpr close_func_ptr(Func func) : func(func) {}
};
template <typename Func>
struct func_ptr : refl::attr::usage::function
{
static_assert(std::is_member_function_pointer_v<std::remove_pointer_t<Func>> || std::is_function_v<std::remove_pointer_t<Func>>,
"Func must be a function pointer or method pointer");
Func func;
constexpr func_ptr(Func func) : func(func) {}
};
struct static_func : refl::attr::usage::function {};
struct setter_is_next : refl::attr::usage::function {};
#define USE_LUABRIDGE_HACK_FOR_REFLECTION 0
#if USE_LUABRIDGE_HACK_FOR_REFLECTION
// This code depends on the following changes to luabridge::Namespace::Class<T> that will never be merged back into the main fork:
// 1. make luabridge::Namespace::Class<T> public
// 2. make assertStackState public with 'using ClassBase::assertStackState'
// 3. add public state() method to expose the lua_State in luabridge::Namespace::Class<T>
// The main reason not to delete this code is that if we get into a situation where we need fast iteration of debug and build
// this code shaves about 20% off the build time compared to the fastest method that does not require the hack.
template<class T, class TS>
luabridge::Namespace::Class<T>& AddPropertySetter(luabridge::Namespace::Class<T> &bridgeClass, char const* name, void (T::*set)(TS))
{
bridgeClass.assertStackState(); // Stack: const table (co), class table (cl), static table (st)
typedef void (T::*set_t)(TS);
new (lua_newuserdata(bridgeClass.state(), sizeof(set_t)))
set_t(set); // Stack: co, cl, st, function ptr
lua_pushcclosure(bridgeClass.state(), &luabridge::detail::CFunc::CallMember<set_t>::f, 1); // Stack: co, cl, st, setter
luabridge::detail::CFunc::addSetter(bridgeClass.state(), name, -3); // Stack: co, cl, st
return bridgeClass;
}
#endif
// use refl::util namespace to force errors if refl-cpp ever officially adds this
namespace refl
{
namespace util
{
template <typename F, typename... Ts>
constexpr void for_each_unordered(type_list<Ts...>, F&& f)
{
refl::util::ignore((f(Ts{}), 0)...);
}
}
}
template<bool IS_STATIC, typename BC, typename Func, typename NC>
void AddMemberFunction(BC& bridgeClass, Func&& funcPtr, NC&& nameStr, _reflListMember& staticMethodList, _reflListMember& methodList)
{
if constexpr (IS_STATIC)
{
bridgeClass.addStaticFunction(nameStr.c_str(), funcPtr);
if (g_buildReflTables)
__AddToReflList(staticMethodList, nameStr.c_str());
}
else
{
bridgeClass.addFunction(nameStr.c_str(), funcPtr);
if (g_buildReflTables)
__AddToReflList(methodList, nameStr.c_str());
}
}
template<class T, typename TN>
void AddClassMembers(TN& bridgeClass, const char* className)
{
constexpr auto type = refl::reflect<T>();
constexpr auto members = get_members(type);
_reflListMember &staticMethodList = g_staticMethods[className];
_reflListMember &methodList = g_methods[className];
_reflListMember &propGetList = g_propGets[className];
_reflListMember &propSetList = g_propSets[className];
_reflListMember &parentList = g_parents[className];
if constexpr (refl::descriptor::has_attribute<needs_classname>(type))
{
std::string nameStr(className);
AddMemberFunction<false>(
bridgeClass,
std::function<const char*(const T*)>([nameStr](const T*){ return nameStr.c_str(); } ),
std::string("ClassName"),
staticMethodList,
methodList);
}
#if LUA_VERSION_NUM >= 504
if constexpr (refl::descriptor::has_attribute<close_func_ptr>(type))
{
// direct call to addFunction avoids adding "__close" to reflection list
bridgeClass.addFunction("__close", refl::descriptor::get_attribute<close_func_ptr>(type).func);
}
#endif
// WARNING: This for loop is quite difficult to refactor. If it works, I'd leave it alone.
for_each_unordered(members, [&](auto member)
{
constexpr bool isStaticFunc = refl::descriptor::has_attribute<static_func>(member);
if constexpr (refl::descriptor::has_attribute<special_name>(member))
{
if constexpr(refl::descriptor::has_attribute<func_ptr>(member))
AddMemberFunction<isStaticFunc>(
bridgeClass,
refl::descriptor::get_attribute<func_ptr>(member).func,
REFL_MAKE_CONST_STRING(*refl::descriptor::get_attribute<special_name>(member).alternate_name),
staticMethodList,
methodList);
else
AddMemberFunction<isStaticFunc>(
bridgeClass,
refl::descriptor::get_pointer(member),
REFL_MAKE_CONST_STRING(*refl::descriptor::get_attribute<special_name>(member).alternate_name),
staticMethodList,
methodList);
}
else
{
if constexpr(refl::descriptor::has_attribute<func_ptr>(member))
AddMemberFunction<isStaticFunc>(
bridgeClass,
refl::descriptor::get_attribute<func_ptr>(member).func,
refl::descriptor::get_name(member),
staticMethodList,
methodList);
else
AddMemberFunction<isStaticFunc>(
bridgeClass,
refl::descriptor::get_pointer(member),
refl::descriptor::get_name(member),
staticMethodList,
methodList);
}
if constexpr (refl::descriptor::is_property(member))
{
if constexpr (! refl::descriptor::is_writable(member))
{
if constexpr (refl::descriptor::has_attribute<setter_is_next>(member))
{
using member_types = typename refl::type_descriptor<typename decltype(member)::declaring_type>::declared_member_types;
constexpr auto member_index = refl::descriptor::detail::get_member_index(member);
auto writer = refl::trait::get_t<member_index + 1, member_types>{};
bridgeClass.addProperty(
refl::descriptor::get_display_name(member),
refl::descriptor::get_pointer(member),
refl::descriptor::get_pointer(writer));
if (g_buildReflTables)
__AddToReflList(propSetList, refl::descriptor::get_display_name(member));
if (g_buildReflTables)
__AddToReflList(propGetList, refl::descriptor::get_display_name(member));
}
else
{
if constexpr (refl::descriptor::has_writer(member))
{
bridgeClass.addProperty(
refl::descriptor::get_display_name(member),
refl::descriptor::get_pointer(member),
refl::descriptor::get_pointer(refl::descriptor::get_writer(member)));
if (g_buildReflTables)
__AddToReflList(propSetList, refl::descriptor::get_display_name(member));
}
else
{
bridgeClass.addProperty(refl::descriptor::get_display_name(member), refl::descriptor::get_pointer(member));
}
if (g_buildReflTables)
__AddToReflList(propGetList, refl::descriptor::get_display_name(member));
}
}
}
});
//add the properties every time
bridgeClass.addStaticProperty(__METHOD_TABLE, &methodList , false);
bridgeClass.addStaticProperty(__STATIC_METHOD_TABLE, &staticMethodList, false);
bridgeClass.addStaticProperty(__PROPGET_TABLE, &propGetList, false);
bridgeClass.addStaticProperty(__PROPSET_TABLE, &propSetList, false);
bridgeClass.addStaticProperty(__PARENT_TABLE, &parentList, false);
}
template<class T>
static constexpr auto GetClassName_()
{
constexpr auto type = refl::reflect<T>();
if constexpr (refl::descriptor::has_attribute<special_name>(type))
return REFL_MAKE_CONST_STRING(*refl::descriptor::get_attribute<special_name>(type).alternate_name);
else
return refl::descriptor::get_name(type);
}
using lbOptions = LB2(uint32_t) LB3(luabridge::Options);
template<class T>
void AddClass(const std::string& nameSpace, lua_State *l, LB2([[maybe_unused]])lbOptions options)
{
constexpr auto name = GetClassName_<T>();
auto bridgeClass = luabridge::getGlobalNamespace(l)
.beginNamespace (nameSpace.c_str())
.beginClass<T>(name.c_str() LB3_PARM(options));
#if USE_PROXY_CONSTRUCTORS
constexpr auto type = refl::reflect<T>();
if constexpr (refl::descriptor::has_attribute<ctor_ptr>(type))
bridgeClass.addConstructor(refl::descriptor::get_attribute<ctor_ptr>(type).func);
#endif
AddClassMembers<T>(bridgeClass, name.c_str());
bridgeClass.endClass()
.endNamespace();
}
template<class T, typename ConstructorArgs>
void AddClass(const std::string& nameSpace, lua_State *l, LB2([[maybe_unused]])lbOptions options)
{
constexpr auto name = GetClassName_<T>();
auto bridgeClass = luabridge::getGlobalNamespace(l)
.beginNamespace (nameSpace.c_str())
.beginClass<T>(name.c_str() LB3_PARM(options))
.template addConstructor<ConstructorArgs>();
AddClassMembers<T>(bridgeClass, name.c_str());
bridgeClass.endClass()
.endNamespace();
}
// for some reason, refl-cpp is generating compile-errors for its base_types attribute, so don't use it
// (the error occurs on addFunction, which may mean there is a conflict between refl-cpp and LuaBridge)
template<class T, class Baseclass>
void AddDerivedClass(const std::string& nameSpace, lua_State *l, LB2([[maybe_unused]])lbOptions options)
{
constexpr auto name = GetClassName_<T>();
auto bridgeClass = luabridge::getGlobalNamespace(l)
.beginNamespace (nameSpace.c_str())
.deriveClass<T, Baseclass>(name.c_str() LB3_PARM(options));
#if USE_PROXY_CONSTRUCTORS
constexpr auto type = refl::reflect<T>();
if constexpr (refl::descriptor::has_attribute<ctor_ptr>(type))
bridgeClass.addConstructor(refl::descriptor::get_attribute<ctor_ptr>(type).func);
#endif
AddClassMembers<T>(bridgeClass, name.c_str());
bridgeClass.endClass()
.endNamespace();
if (g_buildReflTables)
__AddToReflList(g_parents[name.c_str()], GetClassName_<Baseclass>().c_str(), "table");
}
// for some reason, refl-cpp is generating compile-errors for its base_types attribute, so don't use it
// (the error occurs on addFunction, which may mean there is a conflict between refl-cpp and LuaBridge)
template<class T, class Baseclass, typename ConstructorArgs>
void AddDerivedClass(const std::string& nameSpace, lua_State *l, LB2([[maybe_unused]])lbOptions options)
{
constexpr auto name = GetClassName_<T>();
auto bridgeClass = luabridge::getGlobalNamespace(l)
.beginNamespace (nameSpace.c_str())
.deriveClass<T, Baseclass>(name.c_str() LB3_PARM(options))
.template addConstructor<ConstructorArgs>();
AddClassMembers<T>(bridgeClass, name.c_str());
bridgeClass.endClass()
.endNamespace();
if (g_buildReflTables)
__AddToReflList(g_parents[name.c_str()], GetClassName_<Baseclass>().c_str(), "table");
}
from refl-cpp.
What the code does
Just to make sure we're on the same page:
is_writable
- doesmember
have a setter-like signature (Whatever MyClass::$member(Input&);
), this is orthogonal to the property feature.get_reader
- searches the list of members for a property with the same display name and with a getter-like signature (Output& MyClass::$member() const;
).has_writer
- searches the list of members for a property with the same display name and with a setter-like signature; returns boolean
Why this is slow
Because there is no additional infrastructure to bind getters and setters, other than matching by property name, this lookup is always necessary. The situation is not as grim as it seems, because the compiler should only do code-gen once per template instantiation (calling has_writer twice with the same member should not result in twice the code-gen).
Possible things to try
I'm guessing the above situation could be improved marginally, by using a custom "property" attribute that can also store a flag indicating a read-only, write-only, or read-write property. In that case, you wouldn't need to do the has_writer/reader
invocation, but this is only a constant-factor improvement.
What might not work
It might seem intuitive that by enumerating the members in advance and building a list of tuples of indices binding the getter and setter and by using get_t, you could improve the compilation time, but actually, you would still need to do the same number of calls to get_reader/get_writer/has_reader/has_writer (at least in the naive approach I could come up with).
Proxies and an undocumented hack
There is a feature in refl-cpp called proxies. The reason I am mentioning is it is a heavy (in terms of compilation time) feature to support, and disabling it will likely help somewhat. I would be curious to see by how much.
#undef REFL_DETAIL_MEMBER_PROXY
#define REFL_DETAIL_MEMBER_PROXY(...)
More information needed
Could you maybe share some more details of the compiler you are using, number of reflected classes (via macros), number of properties, number of classes for which ProcessClass
is invoked (can be rough estimates). Maybe also include the output of the compilation with timed compilation phases (clang has -ftime-report
). Thanks.
from refl-cpp.
Suggestion Results
As to "are we on the same page," based on your criteria, we are.
I actually timed the difference this time. The get_reader
approach takes a little over 2.5x the LuaBridge
-hack approach.
Adding
#undef REFL_DETAIL_MEMBER_PROXY
#define REFL_DETAIL_MEMBER_PROXY(...)
made no appreciable difference, as far as I could tell.
A Possible Way Out
If there were a way, in a single call, to use get_writer
to both get the writer if it exists or find out it didn't if it didn't, I could refactor my code to search for getters and simply call get_writer
for them. That would roughly halve the number of calls. I don't like this approach because then I would not find stranded writers, but perhaps I could run the current version periodically to look for them. Unfortunately it looks as if refl-cpp
currently throws a static_assert
if I call get_writer
and it doesn't exist.
My Environment
I am using XCode out-of-the-box. So Apple clang I guess. And Visual Studio 2022 out-of-the-box. So MSC.
I am processing approximately 200 classes. The number of members varies between 2 and 195. Unfortunately, the very biggest ones are nearly all property setters and getters.
The Big One
My class with the largest number of members has 195 members, nearly all property setters and getters. This largest class is in its own separate cpp
file in an attempt to placate MSC.
(Right now it runs MSC out of heap memory if I use the get_reader
/has_writer
approach, but I think that is because of the weak Windows dev environment I currently am using: a VM with somewhat limited memory. I will be addressing this soon.)
This one class in its own separate cpp
file consumes a substantial amount of the build time. The cpp
file lists all the properties in REFL_FUNC
macros, then contains a callable function with a single line:
void ProcessMyClass()
{
ProcessClass<MyClass>();
}
I compiled it on XCode with the option you suggested. Here is the result:
===-------------------------------------------------------------------------===
Miscellaneous Ungrouped Timers
===-------------------------------------------------------------------------===
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
8.3525 ( 67.8%) 0.6323 ( 38.3%) 8.9847 ( 64.3%) 9.1533 ( 64.5%) LLVM IR Generation Time
3.9736 ( 32.2%) 1.0167 ( 61.7%) 4.9903 ( 35.7%) 5.0349 ( 35.5%) Code Generation Time
12.3260 (100.0%) 1.6490 (100.0%) 13.9750 (100.0%) 14.1883 (100.0%) Total
===-------------------------------------------------------------------------===
Instruction Selection and Scheduling
===-------------------------------------------------------------------------===
Total Execution Time: 0.5066 seconds (0.5104 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
0.1811 ( 37.8%) 0.0062 ( 22.0%) 0.1873 ( 37.0%) 0.1882 ( 36.9%) Instruction Scheduling
0.0816 ( 17.1%) 0.0027 ( 9.5%) 0.0843 ( 16.6%) 0.0848 ( 16.6%) Instruction Selection
0.0668 ( 14.0%) 0.0075 ( 26.4%) 0.0743 ( 14.7%) 0.0765 ( 15.0%) DAG Combining 1
0.0560 ( 11.7%) 0.0023 ( 8.2%) 0.0583 ( 11.5%) 0.0583 ( 11.4%) DAG Combining 2
0.0448 ( 9.4%) 0.0028 ( 9.8%) 0.0475 ( 9.4%) 0.0476 ( 9.3%) Instruction Creation
0.0199 ( 4.2%) 0.0018 ( 6.3%) 0.0217 ( 4.3%) 0.0218 ( 4.3%) DAG Legalization
0.0130 ( 2.7%) 0.0018 ( 6.3%) 0.0148 ( 2.9%) 0.0147 ( 2.9%) Type Legalization
0.0111 ( 2.3%) 0.0017 ( 6.0%) 0.0128 ( 2.5%) 0.0129 ( 2.5%) Instruction Scheduling Cleanup
0.0039 ( 0.8%) 0.0015 ( 5.4%) 0.0054 ( 1.1%) 0.0054 ( 1.0%) Vector Legalization
0.0003 ( 0.1%) 0.0000 ( 0.1%) 0.0003 ( 0.1%) 0.0003 ( 0.1%) DAG Combining after legalize types
0.4784 (100.0%) 0.0282 (100.0%) 0.5066 (100.0%) 0.5104 (100.0%) Total
===-------------------------------------------------------------------------===
DWARF Emission
===-------------------------------------------------------------------------===
Total Execution Time: 1.0137 seconds (1.0259 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
0.4938 ( 70.9%) 0.1780 ( 56.0%) 0.6717 ( 66.3%) 0.6797 ( 66.3%) Debug Info Emission
0.1815 ( 26.1%) 0.1354 ( 42.6%) 0.3169 ( 31.3%) 0.3209 ( 31.3%) DWARF Exception Writer
0.0207 ( 3.0%) 0.0044 ( 1.4%) 0.0251 ( 2.5%) 0.0253 ( 2.5%) DWARF Debug Writer
0.6960 (100.0%) 0.3177 (100.0%) 1.0137 (100.0%) 1.0259 (100.0%) Total
===-------------------------------------------------------------------------===
... Pass execution timing report ...
===-------------------------------------------------------------------------===
Total Execution Time: 4.0898 seconds (4.1161 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
1.3500 ( 41.3%) 0.5923 ( 72.5%) 1.9424 ( 47.5%) 1.9567 ( 47.5%) X86 Assembly Printer
1.0163 ( 31.1%) 0.0612 ( 7.5%) 1.0775 ( 26.3%) 1.0846 ( 26.4%) X86 DAG->DAG Instruction Selection
0.2027 ( 6.2%) 0.0075 ( 0.9%) 0.2102 ( 5.1%) 0.2112 ( 5.1%) Live DEBUG_VALUE analysis
0.1043 ( 3.2%) 0.0092 ( 1.1%) 0.1134 ( 2.8%) 0.1139 ( 2.8%) Fast Register Allocator
0.1004 ( 3.1%) 0.0039 ( 0.5%) 0.1043 ( 2.5%) 0.1046 ( 2.5%) Prologue/Epilogue Insertion & Frame Finalization
0.0520 ( 1.6%) 0.0030 ( 0.4%) 0.0550 ( 1.3%) 0.0552 ( 1.3%) Inliner for always_inline functions
0.0455 ( 1.4%) 0.0029 ( 0.4%) 0.0485 ( 1.2%) 0.0489 ( 1.2%) Insert stack protectors
0.0433 ( 1.3%) 0.0029 ( 0.4%) 0.0461 ( 1.1%) 0.0463 ( 1.1%) Free MachineFunction
0.0347 ( 1.1%) 0.0035 ( 0.4%) 0.0382 ( 0.9%) 0.0380 ( 0.9%) Two-Address instruction pass
0.0195 ( 0.6%) 0.0029 ( 0.4%) 0.0224 ( 0.5%) 0.0225 ( 0.5%) MachineDominator Tree Construction
0.0193 ( 0.6%) 0.0027 ( 0.3%) 0.0220 ( 0.5%) 0.0221 ( 0.5%) Lower the matrix intrinsics
0.0185 ( 0.6%) 0.0010 ( 0.1%) 0.0195 ( 0.5%) 0.0195 ( 0.5%) CallGraph Construction
0.0135 ( 0.4%) 0.0025 ( 0.3%) 0.0159 ( 0.4%) 0.0159 ( 0.4%) Expand Atomic instructions
0.0133 ( 0.4%) 0.0025 ( 0.3%) 0.0158 ( 0.4%) 0.0158 ( 0.4%) Dominator Tree Construction
0.0116 ( 0.4%) 0.0025 ( 0.3%) 0.0141 ( 0.3%) 0.0143 ( 0.3%) Finalize ISel and expand pseudo-instructions
0.0107 ( 0.3%) 0.0025 ( 0.3%) 0.0132 ( 0.3%) 0.0133 ( 0.3%) X86 EFLAGS copy lowering
0.0104 ( 0.3%) 0.0025 ( 0.3%) 0.0129 ( 0.3%) 0.0130 ( 0.3%) Exception handling preparation
0.0102 ( 0.3%) 0.0024 ( 0.3%) 0.0127 ( 0.3%) 0.0126 ( 0.3%) Lower constant intrinsics
0.0100 ( 0.3%) 0.0024 ( 0.3%) 0.0124 ( 0.3%) 0.0123 ( 0.3%) Post-RA pseudo instruction expansion pass
0.0083 ( 0.3%) 0.0031 ( 0.4%) 0.0114 ( 0.3%) 0.0113 ( 0.3%) Basic Alias Analysis (stateless AA impl)
0.0078 ( 0.2%) 0.0024 ( 0.3%) 0.0102 ( 0.3%) 0.0102 ( 0.2%) Scalarize Masked Memory Intrinsics
0.0074 ( 0.2%) 0.0023 ( 0.3%) 0.0098 ( 0.2%) 0.0098 ( 0.2%) Function Alias Analysis Results
0.0073 ( 0.2%) 0.0023 ( 0.3%) 0.0096 ( 0.2%) 0.0097 ( 0.2%) X86 pseudo instruction expansion pass
0.0065 ( 0.2%) 0.0025 ( 0.3%) 0.0090 ( 0.2%) 0.0091 ( 0.2%) Natural Loop Information
0.0066 ( 0.2%) 0.0023 ( 0.3%) 0.0090 ( 0.2%) 0.0090 ( 0.2%) Lazy Branch Probability Analysis
0.0065 ( 0.2%) 0.0024 ( 0.3%) 0.0089 ( 0.2%) 0.0089 ( 0.2%) Expand reduction intrinsics
0.0060 ( 0.2%) 0.0024 ( 0.3%) 0.0084 ( 0.2%) 0.0083 ( 0.2%) Remove unreachable blocks from the CFG
0.0054 ( 0.2%) 0.0024 ( 0.3%) 0.0078 ( 0.2%) 0.0080 ( 0.2%) Eliminate PHI nodes for register allocation
0.0054 ( 0.2%) 0.0024 ( 0.3%) 0.0078 ( 0.2%) 0.0079 ( 0.2%) X86 Indirect Branch Tracking
0.0050 ( 0.2%) 0.0023 ( 0.3%) 0.0074 ( 0.2%) 0.0075 ( 0.2%) Machine Optimization Remark Emitter #2
0.0047 ( 0.1%) 0.0023 ( 0.3%) 0.0070 ( 0.2%) 0.0072 ( 0.2%) Machine Optimization Remark Emitter
0.0047 ( 0.1%) 0.0024 ( 0.3%) 0.0071 ( 0.2%) 0.0071 ( 0.2%) Bundle Machine CFG Edges
0.0045 ( 0.1%) 0.0024 ( 0.3%) 0.0069 ( 0.2%) 0.0069 ( 0.2%) Expand indirectbr instructions
0.0040 ( 0.1%) 0.0024 ( 0.3%) 0.0064 ( 0.2%) 0.0065 ( 0.2%) Insert XRay ops
0.0040 ( 0.1%) 0.0024 ( 0.3%) 0.0064 ( 0.2%) 0.0064 ( 0.2%) X86 PIC Global Base Reg Initialization
0.0035 ( 0.1%) 0.0024 ( 0.3%) 0.0059 ( 0.1%) 0.0062 ( 0.1%) X86 Darwin Stack Probe Emitter
0.0038 ( 0.1%) 0.0023 ( 0.3%) 0.0061 ( 0.1%) 0.0061 ( 0.1%) Insert fentry calls
0.0036 ( 0.1%) 0.0023 ( 0.3%) 0.0059 ( 0.1%) 0.0060 ( 0.1%) Optimization Remark Emitter
0.0036 ( 0.1%) 0.0023 ( 0.3%) 0.0059 ( 0.1%) 0.0059 ( 0.1%) X86 Speculative Execution Side Effect Suppression
0.0037 ( 0.1%) 0.0023 ( 0.3%) 0.0059 ( 0.1%) 0.0059 ( 0.1%) X86 Indirect Thunks
0.0036 ( 0.1%) 0.0023 ( 0.3%) 0.0059 ( 0.1%) 0.0059 ( 0.1%) Local Stack Slot Allocation
0.0032 ( 0.1%) 0.0025 ( 0.3%) 0.0057 ( 0.1%) 0.0057 ( 0.1%) Lazy Machine Block Frequency Analysis
0.0036 ( 0.1%) 0.0022 ( 0.3%) 0.0058 ( 0.1%) 0.0057 ( 0.1%) Implement the 'patchable-function' attribute
0.0034 ( 0.1%) 0.0024 ( 0.3%) 0.0058 ( 0.1%) 0.0057 ( 0.1%) Contiguously Lay Out Funclets
0.0032 ( 0.1%) 0.0023 ( 0.3%) 0.0055 ( 0.1%) 0.0057 ( 0.1%) X86 FP Stackifier
0.0032 ( 0.1%) 0.0024 ( 0.3%) 0.0056 ( 0.1%) 0.0056 ( 0.1%) X86 Insert Cache Prefetches
0.0032 ( 0.1%) 0.0023 ( 0.3%) 0.0055 ( 0.1%) 0.0056 ( 0.1%) Lazy Machine Block Frequency Analysis #2
0.0030 ( 0.1%) 0.0023 ( 0.3%) 0.0053 ( 0.1%) 0.0056 ( 0.1%) X86 insert wait instruction
0.0030 ( 0.1%) 0.0026 ( 0.3%) 0.0056 ( 0.1%) 0.0055 ( 0.1%) Instrument function entry/exit with calls to e.g. mcount() (pre inlining)
0.0033 ( 0.1%) 0.0023 ( 0.3%) 0.0056 ( 0.1%) 0.0055 ( 0.1%) Fixup Statepoint Caller Saved
0.0031 ( 0.1%) 0.0023 ( 0.3%) 0.0055 ( 0.1%) 0.0055 ( 0.1%) StackMap Liveness Analysis
0.0031 ( 0.1%) 0.0024 ( 0.3%) 0.0055 ( 0.1%) 0.0055 ( 0.1%) Analyze Machine Code For Garbage Collection
0.0031 ( 0.1%) 0.0024 ( 0.3%) 0.0055 ( 0.1%) 0.0055 ( 0.1%) Instrument function entry/exit with calls to e.g. mcount() (post inlining)
0.0029 ( 0.1%) 0.0023 ( 0.3%) 0.0053 ( 0.1%) 0.0054 ( 0.1%) X86 DynAlloca Expander
0.0029 ( 0.1%) 0.0025 ( 0.3%) 0.0054 ( 0.1%) 0.0053 ( 0.1%) Annotation Remarks
0.0028 ( 0.1%) 0.0023 ( 0.3%) 0.0051 ( 0.1%) 0.0053 ( 0.1%) X86 vzeroupper inserter
0.0029 ( 0.1%) 0.0023 ( 0.3%) 0.0051 ( 0.1%) 0.0052 ( 0.1%) X86 speculative load hardening
0.0028 ( 0.1%) 0.0023 ( 0.3%) 0.0052 ( 0.1%) 0.0052 ( 0.1%) X86 Load Value Injection (LVI) Ret-Hardening
0.0027 ( 0.1%) 0.0023 ( 0.3%) 0.0051 ( 0.1%) 0.0051 ( 0.1%) X86 Discriminate Memory Operands
0.0028 ( 0.1%) 0.0023 ( 0.3%) 0.0051 ( 0.1%) 0.0051 ( 0.1%) Safe Stack instrumentation pass
0.0028 ( 0.1%) 0.0023 ( 0.3%) 0.0051 ( 0.1%) 0.0051 ( 0.1%) Lazy Block Frequency Analysis
0.0027 ( 0.1%) 0.0023 ( 0.3%) 0.0050 ( 0.1%) 0.0051 ( 0.1%) Compressing EVEX instrs to VEX encoding when possible
0.0026 ( 0.1%) 0.0023 ( 0.3%) 0.0049 ( 0.1%) 0.0050 ( 0.1%) Lower Garbage Collection Instructions
0.0025 ( 0.1%) 0.0024 ( 0.3%) 0.0048 ( 0.1%) 0.0049 ( 0.1%) Shadow Stack GC Lowering
0.0010 ( 0.0%) 0.0000 ( 0.0%) 0.0010 ( 0.0%) 0.0010 ( 0.0%) Assumption Cache Tracker
0.0005 ( 0.0%) 0.0000 ( 0.0%) 0.0005 ( 0.0%) 0.0005 ( 0.0%) Pre-ISel Intrinsic Lowering
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Profile summary info
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Annotation2Metadata
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Rewrite Symbols
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Force set function attributes
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Machine Branch Probability Analysis
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Target Library Information
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Machine Module Information
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Assumption Cache Tracker #2
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Target Pass Configuration
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Target Library Information #2
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Create Garbage Collector Module Metadata
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Profile summary info #2
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Target Transform Information #2
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) Target Transform Information
3.2728 (100.0%) 0.8170 (100.0%) 4.0898 (100.0%) 4.1161 (100.0%) Total
===-------------------------------------------------------------------------===
Clang front-end time report
===-------------------------------------------------------------------------===
Total Execution Time: 169.2407 seconds (172.4141 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
163.1288 (100.0%) 6.1119 (100.0%) 169.2407 (100.0%) 172.4141 (100.0%) Clang front-end timer
163.1288 (100.0%) 6.1119 (100.0%) 169.2407 (100.0%) 172.4141 (100.0%) Total
from refl-cpp.
On further reflection, I realized that calling get_writer
might not be that much more expensive after calling has_writer
, based on your comments above about how the compiler only does code-gen once. I refactored my code as described above, and it shaved a fair amount off. The build time dropped from ~11:15 minutes to ~8:10. (The hack version takes ~4 minutes.) Obviously, the main context I worry about these build times is in the iterative dev process of build->debug->fix->...
Unfortunately, the refactored version still crashes MSC.
The new approach also had a hidden benefit I didn't expect. It exposed a class in which I had included the same setter twice. (LuaBridge
lets you add the same property again and again: you end up with the last one you loaded.)
from refl-cpp.
Happy New Year! I have made some progress on reducing the compilation times for the example you provided.
One of the heuristics that I have added is to check if the getter and setter are reflected on consecutive lines. This skips the expensive linear search that happens currently.
It would be really great if you could verify that those work for your use case by compiling against the faster-properties branch.
from refl-cpp.
I will certainly try it. Unfortunately my code base has getters and setters reflected fairly randomly, so this excellent idea may not make that much difference for me. (I realize I could move them around, but that's quite a big job. But maybe it makes sense for a couple of the biggest classes.)
from refl-cpp.
I have good news and bad. The good news is that my class with the largest number of reflections compiles much faster than the master
branch. (This is with the original version that uses has_writer
on non-writable members and get_reader
on writable members.) On macOS
it was 0:45 seconds on faster-properties
vs. 2:20 on the master
branch. (This compares to ~0:15 with the LuaBridge
hack that adds getters and setters separately.) So a vast improvement, without any additional changes to my code.
The refactored version that uses has_writer
and get_writer
on only non-writable members takes 1:37 on master
and 0:28 on faster-properties
.
Even better news comes on Windows, where now even my weak-ass build platform running on a virtual machine can compile the most complex class without running out of heap space, at least with the refactored version.
You've definitely made a big improvement. Tremendous props and thanks. A Happy New Year indeed.
Now for the (hopefully minor) bad news. It appears that the faster-properties
branch can't handle read-only properties. I got compiler errors on a class reflected as follows. (special_name
is an attribute I added.)
REFL_AUTO
(
type(FCRawText),
func(ClassName),
func(GetText),
func(SetText),
func(SaveNew),
func(Load),
func(GetItemNo, property()),
func(CreateString_GC, special_name("CreateString"))
)
Here is the error log:
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:17:
.../refl-cpp/include/refl.hpp:3336:24: error: no matching function for call to 'contains'
return contains(writable_properties, display_name_equals_p<ReadableMember>);
^~~~~~~~
.../refl-cpp/include/refl.hpp:3441:40: note: in instantiation of function template specialization 'refl::descriptor::detail::has_writer_search<refl::descriptor::function_descriptor<FCRawText, 5>>' requested here
return detail::has_writer_search(member);
^
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:253:49: note: in instantiation of function template specialization 'refl::descriptor::has_writer<refl::descriptor::function_descriptor<FCRawText, 5>>' requested here
else if constexpr (! refl::descriptor::has_writer(member))
^
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:17:
.../refl-cpp/include/refl.hpp:1557:89: note: in instantiation of function template specialization 'AddClassMembers(luabridge::Namespace::Class<FCRawText> &)::(anonymous class)::operator()<refl::descriptor::function_descriptor<FCRawText, 5>>' requested here
constexpr auto invoke_optional_index(F&& f, T&& t, size_t, ...) -> decltype(f(std::forward<T>(t)))
^
.../refl-cpp/include/refl.hpp:1637:17: note: while substituting deduced template arguments into function template 'invoke_optional_index' [with F = (lambda at .../-Source/RGPLuaReflection.h:208:22) &, T = refl::descriptor::function_descriptor<FCRawText, 5>]
detail::invoke_optional_index(f, std::forward<decltype(val)>(val), idx, 0);
^
.../refl-cpp/include/refl.hpp:1551:93: note: in instantiation of function template specialization 'refl::util::for_each(type_list<refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>>, (lambda at .../-Source/RGPLuaReflection.h:208:22) &&)::(anonymous class)::operator()<refl::descriptor::function_descriptor<FCRawText, 5>>' requested here
constexpr auto invoke_optional_index(F&& f, T&& t, size_t idx, int) -> decltype(f(std::forward<T>(t), idx))
^
.../refl-cpp/include/refl.hpp:1577:33: note: (skipping 6 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
auto&& result = invoke_optional_index(f, T{}, I, 0);
^
.../refl-cpp/include/refl.hpp:1602:28: note: in instantiation of function template specialization 'refl::util::detail::eval_in_order_to_tuple<(lambda at .../refl-cpp/include/refl.hpp:1635:32), refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>, 0, 1, 2, 3, 4, 5, 6>' requested here
return detail::eval_in_order_to_tuple(list, std::make_index_sequence<sizeof...(Ts)>{}, std::forward<F>(f));
^
.../refl-cpp/include/refl.hpp:1635:13: note: in instantiation of function template specialization 'refl::util::map_to_tuple<(lambda at .../refl-cpp/include/refl.hpp:1635:32), refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>>' requested here
map_to_tuple(list, [&](auto&& val, size_t idx)
^
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:208:4: note: in instantiation of function template specialization 'refl::util::for_each<(lambda at .../-Source/RGPLuaReflection.h:208:22), refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>>' requested here
for_each(members, [&](auto member)
^
.../-Source/RGPLuaReflection.h:348:4: note: in instantiation of function template specialization 'AddClassMembers<FCRawText, luabridge::Namespace::Class<FCRawText>>' requested here
AddClassMembers<T>(bridgeClass);
^
.../-Source/RGPLuaConnectorText.cpp:72:4: note: in instantiation of function template specialization 'AddDerivedClass<FCRawText, __FCBaseData>' requested here
AddDerivedClass<FCRawText, __FCBaseData>(nameSpace, l);
^
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:17:
.../refl-cpp/include/refl.hpp:1751:24: note: candidate template ignored: failed template argument deduction
constexpr bool contains(type_list<T, Ts...> list, F&& f)
^
.../refl-cpp/include/refl.hpp:1762:24: note: candidate function template not viable: requires 1 argument, but 2 were provided
constexpr bool contains(type_list<Ts...>)
^
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:253:29: error: constexpr if condition is not a constant expression
else if constexpr (! refl::descriptor::has_writer(member))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:17:
.../refl-cpp/include/refl.hpp:1557:89: note: in instantiation of function template specialization 'AddClassMembers(luabridge::Namespace::Class<FCRawText> &)::(anonymous class)::operator()<refl::descriptor::function_descriptor<FCRawText, 5>>' requested here
constexpr auto invoke_optional_index(F&& f, T&& t, size_t, ...) -> decltype(f(std::forward<T>(t)))
^
.../refl-cpp/include/refl.hpp:1637:17: note: while substituting deduced template arguments into function template 'invoke_optional_index' [with F = (lambda at .../-Source/RGPLuaReflection.h:208:22) &, T = refl::descriptor::function_descriptor<FCRawText, 5>]
detail::invoke_optional_index(f, std::forward<decltype(val)>(val), idx, 0);
^
.../refl-cpp/include/refl.hpp:1551:93: note: in instantiation of function template specialization 'refl::util::for_each(type_list<refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>>, (lambda at .../-Source/RGPLuaReflection.h:208:22) &&)::(anonymous class)::operator()<refl::descriptor::function_descriptor<FCRawText, 5>>' requested here
constexpr auto invoke_optional_index(F&& f, T&& t, size_t idx, int) -> decltype(f(std::forward<T>(t), idx))
^
.../refl-cpp/include/refl.hpp:1577:33: note: while substituting deduced template arguments into function template 'invoke_optional_index' [with F = (lambda at .../refl-cpp/include/refl.hpp:1635:32) &, T = refl::descriptor::function_descriptor<FCRawText, 5>]
auto&& result = invoke_optional_index(f, T{}, I, 0);
^
.../refl-cpp/include/refl.hpp:1578:24: note: in instantiation of function template specialization 'refl::util::detail::eval_in_order_to_tuple<(lambda at .../refl-cpp/include/refl.hpp:1635:32), refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>, 5, 6, int, int, int, int, int>' requested here
return eval_in_order_to_tuple(
^
.../refl-cpp/include/refl.hpp:1578:24: note: (skipping 4 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
.../refl-cpp/include/refl.hpp:1602:28: note: in instantiation of function template specialization 'refl::util::detail::eval_in_order_to_tuple<(lambda at .../refl-cpp/include/refl.hpp:1635:32), refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>, 0, 1, 2, 3, 4, 5, 6>' requested here
return detail::eval_in_order_to_tuple(list, std::make_index_sequence<sizeof...(Ts)>{}, std::forward<F>(f));
^
.../refl-cpp/include/refl.hpp:1635:13: note: in instantiation of function template specialization 'refl::util::map_to_tuple<(lambda at .../refl-cpp/include/refl.hpp:1635:32), refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>>' requested here
map_to_tuple(list, [&](auto&& val, size_t idx)
^
.../-Source/RGPLuaConnectorText.cpp:10:
.../-Source/RGPLuaReflection.h:208:4: note: in instantiation of function template specialization 'refl::util::for_each<(lambda at .../-Source/RGPLuaReflection.h:208:22), refl::descriptor::function_descriptor<FCRawText, 0>, refl::descriptor::function_descriptor<FCRawText, 1>, refl::descriptor::function_descriptor<FCRawText, 2>, refl::descriptor::function_descriptor<FCRawText, 3>, refl::descriptor::function_descriptor<FCRawText, 4>, refl::descriptor::function_descriptor<FCRawText, 5>, refl::descriptor::function_descriptor<FCRawText, 6>>' requested here
for_each(members, [&](auto member)
^
.../-Source/RGPLuaReflection.h:348:4: note: in instantiation of function template specialization 'AddClassMembers<FCRawText, luabridge::Namespace::Class<FCRawText>>' requested here
AddClassMembers<T>(bridgeClass);
^
.../-Source/RGPLuaConnectorText.cpp:72:4: note: in instantiation of function template specialization 'AddDerivedClass<FCRawText, __FCBaseData>' requested here
AddDerivedClass<FCRawText, __FCBaseData>(nameSpace, l);
^
2 errors generated.
from refl-cpp.
Great to hear that you are seeing an improvement.
I believe this is fixed and even faster to compile with the latest commit on faster-properties, but I will need to double check later to confirm that is the case.
from refl-cpp.
It is slightly faster. (It shaved about 5 seconds off the worst case class, so ~10% improvement.)
Unfortunately it is still getting the same compiler errors. (Or similar enough that they seem to be the same.)
from refl-cpp.
After iterating on the faster-properties branch a bit more, I am now generally happy with the code and the results. Everything should be working now. I will likely wait for a couple of days at the most to see if you got the chance to test and will likely merge this in and bump up to 0.12.2.
A small but very effective patch was made to for_each and is already merged in the latest master, so the whole loop should be somewhat faster as well.
My benchmarks: #60 (comment)
from refl-cpp.
This version of faster-properities
works excellently for me. It has cut themacOS
build time for my project from over 11 minutes to 6:10, using the most intensive approach. (Compared to 4:00 for the least intensive approach using a LuaBridge
hack.) Even better, the intermediate approach (calling has_writer
and get_writer
only on non-writable property members) reduces the build time to 4:44, which is close enough to the hacked version that I can abandon the hacked version.
I added pull request #61 to the faster-properties
branch to eliminate an unused-parameter warning.
I do have a question about runtime. I was originally surprised that my ProcessClass
code seemed to generate heavy use of the stack at run time. On Windows I actually had to spin off a separate thread with a multi-MB stack to process all my classes. I would have thought that the recursion in for_each
would have happened at compile time and ended up just spitting out a series of LuaBridge
calls at run time. I am wondering if these for_each
revisions will have an effect on that. (Also, the stack issue may not be caused by refl-cpp
but rather another reflection package I am using for enums, magic-enum
, so this is just an informational question.)
from refl-cpp.
Thanks for PR #61! I forgot to use -Wall
.
I am wondering if these for_each revisions will have an effect on that.
They definitely will, but I do not know how large the effect will be. The for_each implementation is still recursive, though I'd bet that you will have considerably less stack usage.
I am considering adding a non-recursive implementation which for 1.13.0, whenever that happens. The main reason why the implementation is recursive is that is the only way to preserve the correct order of invocations to the passed lambda.
If you are okay with that and don't care about the optional member index parameter, this should be sufficient.
template <typename F, typename... Ts>
constexpr void for_each_unordered(type_list<Ts...>, F&& f)
{
refl::util::ignore((f(Ts{}), 0)...);
}
from refl-cpp.
Is that the full implementation of for_each_unordered
? Because the compiler is complaining that the list
parameter is unused, which seems like it's not right.
from refl-cpp.
I believe it is. I have edited out the name of the parameter (not necessary), and made it so that your lambdas can still return void.
from refl-cpp.
Okay, I'm a little surprised by the timing outcome of for_each_unordered
. It shaved 50 seconds off the version of my code that uses has_writer
or has_reader
on every property member, and it shaved about half that off the version that only calls has_writer
for non-writable property members.
But it made no appreciable difference in the timing of the version that doesn't use has/get_writer
or has/get_reader
. I would have thought it would make an overall difference. weird.
But I'm definitely going to incorporate it.
from refl-cpp.
@rpatters1 Sorry to task, but is your refl-cpp -> LuaBridge code public?
from refl-cpp.
Related Issues (20)
- GetDateFormat getter vs. the Win32 API HOT 2
- Q: make REFL_AUTO, REFL_TYPE etc. callable from nested namespace HOT 7
- Port to vcpkg HOT 3
- Fix warnings for strict variadic macro checks (stronger C++20 enforcement)
- refl::util::detail::filter(..) inverts order of member list HOT 2
- Is there a way to reflect private fields? HOT 5
- Q: how to reflect on member functions' parameters and return types? HOT 5
- Is there a possibility of adding features like Boost.PFR? HOT 3
- Ability to break class declarations up HOT 9
- How to implement my case with this library? HOT 1
- Question. a generic getter function for reflected type HOT 1
- Enum reflection HOT 2
- Access member descriptor from pointer-to-member HOT 4
- Field offsetof HOT 4
- Member list empty when using reflect<T>(), but works fine with reflect(const T&) HOT 1
- How to resolve the non-const signature of a class method. HOT 2
- Q: Implementing a member attribute for "Default value" to use in ctors
- (unknown)**ย that is all i get for typename HOT 1
- Deleted HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from refl-cpp.