-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite reflection and merging #426
Conversation
f037ada
to
f7644b7
Compare
@RalphSteinhagen Is there any chance that you'd want to merge this? The compile-time numbers on GitHub Actions seem like a minor improvement, but nothing substantial. The other important measure would be memory usage by the compiler. I could set up an experiment for that here and add those results (but I'd be happy if someone beats me to it). If you are interested, there's still another pass of code cleanup and then clang-format-back-to-long-lines to do. But I don't want to waste my time if this PR has no chance of getting merged. |
7ec0b22
to
4bb332d
Compare
@mattkretz I ran the difference with Clang's TL;DR:
Before (full_analysis): Analyzing build trace from 'qa_Converter_before.bin'...
**** Time summary:
Compilation (2 times):
Parsing (frontend): 194.5 s
Codegen & opts (backend): 777.5 s
**** Files that took longest to parse (compiler frontend):
141459 ms: ./blocks/basic/test/CMakeFiles/qa_Converter.dir//qa_Converter.cpp.o
53041 ms: ./blocks/basic/test/CMakeFiles/qa_Converter.dir//qa_Converter.cpp.o
**** Files that took longest to codegen (compiler backend):
777491 ms: ./blocks/basic/test/CMakeFiles/qa_Converter.dir//qa_Converter.cpp.o
**** Templates that took longest to instantiate:
2097 ms: std::apply<(lambda at /home/steinhagen/git/gnuradio4/core/include/gnuradio-4.0/Block.hpp:2109:9), std::tuple<unsigned char, unsigned short, unsigned int, unsigned long, signed char, short, int, long, float, double>> (2 times, avg 1048 ms)
2096 ms: std::__apply_tuple_impl<(lambda at /home/steinhagen/git/gnuradio4/core/include/gnuradio-4.0/Block.hpp:2109:9), std::tuple<unsigned char, unsigned short, unsigned int, unsigned long, signed char, short, int, long, float, double>, 0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL, 8UL, 9UL> (2 times, avg 1048 ms)
[..] After (full analysis): Analyzing build trace from 'qa_Converter_after.bin'...
**** Time summary:
Compilation (2 times):
Parsing (frontend): 193.2 s
Codegen & opts (backend): 755.1 s
**** Files that took longest to parse (compiler frontend):
145068 ms: ./blocks/basic/test/CMakeFiles/qa_Converter.dir//qa_Converter.cpp.o
48090 ms: ./blocks/basic/test/CMakeFiles/qa_Converter.dir//qa_Converter.cpp.o
**** Files that took longest to codegen (compiler backend):
755062 ms: ./blocks/basic/test/CMakeFiles/qa_Converter.dir//qa_Converter.cpp.o
**** Templates that took longest to instantiate:
7969 ms: vir::refl::for_each_data_member_index((lambda at /home/steinhagen/git/gnuradio4/core/include/gnuradio-4.0/Settings.hpp:330:59) &&)::(anonymous class)::operator()<0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL, 8UL> (144 times, avg 55 ms)
6443 ms: vir::refl::for_each_data_member_index((lambda at /home/steinhagen/git/gnuradio4/core/include/gnuradio-4.0/Settings.hpp:330:59) &&)::(anonymous class)::operator()<0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL, 8UL, 9UL> (108 times, avg 59 ms)
[..] Tentative Summary:
While there is a slight improvement in compile time and a more significant (~20%) reduction in binary size, the compile-time gains are modest. Introducing the new reflection library and SIMD code doesn't substantially reduce compile times but does affect where the time is spent during compilation. I like the more expressive/intuitive new compile-time macro but this doesn't solve the initial problem of excessive compile-times. Maybe others could have a look/express their thoughts and ideas? @ivan-cukic any ideas? |
d8eda0c
to
44c5d1e
Compare
ff25e1c
to
fbb3864
Compare
Use GR_MAKE_REFLECTABLE macro instead of ENABLE_REFLECTION... Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
839de9a
to
3286409
Compare
- remove ports defined as template arguments (MergedGraph) => drop Block inheritance from std::tuple - split Port into Port and PortDescriptor, where the latter is used in the type lists returned from block reflection - remove fixed_string name template parameter from Port - remove ability to programmatically set a Port name; only the reflected member name is ever used — and then as string_view instead of string (because the name can never be set by users / points to compile-time strings). - flatten tuples of Ports and arrays of Ports when reflecting on block members, as if they were direct members of the block. - reflect vectors of Ports as dynamically sized Ports instead of `vector<PortDescriptor>` - `reflFirstTypeName` is not needed with the way `refl::type_name` presents the names. If it would be, then it's a bug to fix in `refl::type_name`. Therefore, replace `reflFirstTypeName` calls with `refl::type_name`. Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
1. A simple `registerBlock` overload for a list of complete block types was missing. If the given type is a specialized template, then the `type_name` is transformed into a template argument string and passed to `addBlockType`. 2. Generalize the `registerBlock` function for a block template with type template arguments to an arbitrary number of parameters/arguments. 3. Generalize the `registerBlockTT` function to an arbitrary number of type lists (and corresponding number of parameters for the template template parameter). The relatively expensive use of std::apply is replaced by an `outer_product` over all given typelists. 4. Add a comment on how to improve the remaining `registerBlock` overloads. 5. Add meta::outer_product (lifted from mattkretz/virtest). Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
3286409
to
8e6be5f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @mattkretz looks good. Will merge this as-is.
Improvements in this PR
Port
intoPort
: the type used as Block data memberPortDescriptor
: the type used when reflecting over port members in Blocks. This type is never meant to be used for objects. It's a plain compile-time facility.MergedGraph
ports: BeforeMergedGraph
reflected over the existing ports in the Left and Right blocks, removed the internally connected ports and constructed the remaining ports as data members (viastd::tuple
inheritance). Additionally it created data members of the Left and Right blocks, which themselves store Port data members. The latter were unused and potentially a source for error since multiple Port objects seemed to describe the same port. Now,PortDescriptor
has a simple static methodgetPortObject(Block&)
which allows to go from a given Block object and corresponding port reflection to the Port data member. (If that Port data member is astd::tuple
orstd::array
then this will lead to the individual Port object, not the tuple/array. Thus tuple/array Ports appear as if they were directly declared as data members — except that their name is disambiguated.) ThisgetPortObject
method is the customization point forMergedGraph
to abstract how the port object is found.MergedGraph
replaces the standardPortDescriptor
with a slightly modified type that knows to look into Left or Right for the port object. This recurses through an arbitrarily merged set of blocks into the one-and-only Port object. Thus no data member bloat is needed forMergedGraph
anymore, and any potential for confusing Port objects is gone.own PR? (last commit): Refactor
gr::registerBlock
(Please excuse my opinionated boy-scouting, but this was easier to propose in code than in words 😜)
registerBlock
overload for a list of complete block types was missing. If the given type is a specialized template, then thetype_name
is transformed into a template argument string and passed toaddBlockType
.registerBlock
function for a block template with type template arguments to an arbitrary number of parameters/arguments.registerBlockTT
function to an arbitrary number of type lists (and corresponding number of parameters for the template template parameter). The relatively expensive use of std::apply is replaced by anouter_product
over all given typelists.Open questions,
TODO
s,FIXME
sMergedGraph
specialization for port reflection could be generalized into a customization point. But we probably want to think of a better name thanAllPorts
for triggering that customization.PortDescription
traits: They are currently calledis_…
. Better namedescribes_…
?std::tuple
/std::array
of Ports, the index is directly appended to the string. I.e.std::array<PortIn<T>, 3> in;
becomes"in0", "in1", "in2"
. Should name and number be separated by a#
orconnect<"in", 0UZ>
allow connection to"in0"
?PortReflectable
concept go. Any idea for a better name? Should this be merged with theBlockLike
concept? Thesame_as<T, T::derived_t>
bit has led me to find two of the bug reports I triggered over the weekend. IOW it seems quite valueable.MergedGraph
be adjusted? Should port names be concatenated? e.g."out0-scaled"
and"out1-scaled"
instead of two ports with the name"scaled"
? The rule would be:Right
block, prepend the name of theLeft
output that is internally connected.Left
block, append the name of theRight
input that is internally connected.Questions raised while reading, unrelated to this PR
unique_id
already is a member of the Block base class, this is shadowing that member with a different value. No other block does this.)RTLD_LOCAL
and notRTLD_GLOBAL
is used here. (RTLD_LOCAL
breaks RTTI/dynamic_cast
across plugin boundaries. Note that RTTI can be very helpful in the debugger.)void*
to function-pointer is UB in C++. Yes "… 'dlsym' is not C++ and therefore we can do whateever …". But we don't need to. Simply have a single 'extern "C"' symbol in the plugin which is an object storing two function pointers. Then we need a single cast from the 'dlsym' result to an aggregate type and can then extract the two function pointers from it. That's simpler and more likely to be conforming C++.enum class PortType
FIXME: can we still rename this to e.g. PortFlavor? "type" has a very specific meaning in C++ already. And since we're doing a lot of reflection on ports there's ambiguity all over the place.template<bool UseIoThread = true> struct BlockingIO
TODO: replacebool
by an enum or tag type:BlockingIO<false>
is very misleadingUncertainValue
hidden friends or members to reduce compile time (simplifies overload resolution)Results
Binary size when compiled with
-O2 -g0 -march=native -D_GLIBCXX_HARDEN=3
is down:Compile time and space of
qa_Converter
GCC 15
-O2 -g0
without block registration
with block registration
Clang 18 Release
without block registration
with block registration
GCC 14 Release
without block registration
with block registration
GCC 13 Release
without block registration
with block registration