Giter Club home page Giter Club logo

Comments (18)

rpatters1 avatar rpatters1 commented on May 20, 2024 1

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.

rpatters1 avatar rpatters1 commented on May 20, 2024 1

This issue seems like it could be closed, as far as I am concerned.

from refl-cpp.

rpatters1 avatar rpatters1 commented on May 20, 2024 1

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.

veselink1 avatar veselink1 commented on May 20, 2024

What the code does

Just to make sure we're on the same page:

  1. is_writable - does member have a setter-like signature (Whatever MyClass::$member(Input&);), this is orthogonal to the property feature.
  2. 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;).
  3. 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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

veselink1 avatar veselink1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

veselink1 avatar veselink1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

veselink1 avatar veselink1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

veselink1 avatar veselink1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

veselink1 avatar veselink1 commented on May 20, 2024

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.

rpatters1 avatar rpatters1 commented on May 20, 2024

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.

tommitytom avatar tommitytom commented on May 20, 2024

@rpatters1 Sorry to task, but is your refl-cpp -> LuaBridge code public?

from refl-cpp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.