We are in the process of switching from a c-style-type-switch to loki::visitor.
Our class hierarchy has 54 classes at the moment.
I have them grouped like this:
It works well, and compared to the former approach, is a pleasure to work with.
But recently I discovered, that the linker almost uses up all the available RAM.
By looking closer, I discovered that some obj files increased the size tenfold e.g. form 2MB to 20MB.
Analyzing with dumpbin.exe I found that loki indeed contributes a good fraction of the symbols found in the obj.
Especially the deeply nested typelists.
Is there a way to prevent this bloat?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Solved it with the following actions:
1) instead of
typedef Loki::CyclicVisitor<void, TlObGlEntityClasses> ObGlVisitor;
I'm using
class __declspec(dllexport) ObGlVisitor : public Loki::CyclicVisitor<void, TlObGlEntityClasses> { };
which allows me to forward declare ObGlVisitor.
2) Instead of using the LOKI_DEFINE_CYCLIC_VISITABLE macro, I have split the definition and implementation in the header and cpp files.
3) Moved the definition of the concrete visitor into a separate header file, thus the client code that only uses the functionality only gets a few function definitions while only the files that are concerned with the implementation of the functionality get to know the visitor.
4) Using explicit template instantiation declaration and -definition for ObGlVisitor as described at: http://gameangst.com/?p=246
The combined effect is that the summed size of the obj files is now below what it was before applying the visitor.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks for posting that tip for others. I like how C++ allows forward referencing to reduce the number of header files developers must include. As you show here, forward referencing also reduces code-bloat when working with complex templated typedefs.
Cheers,
Rich
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In the meantime we had to solve another problem.
For fast dispatching, we use the cyclic visitor. And at dispatching it really is fast, but a co-worker discovered, that the visitor is slow in construction. The disassembly revealed that the constructor of the visitor builds a huge vtable for all the 54 incarnations of the recursive template. I would expect the compiler to be able to optimise these base classes away as they only contain a pure abstract function definition. But MSVC8 doesn't, others might?
So I put the visitor into a singleton which can only be accessed by an accompanying RAII manager class. The manager class sets the member variables of the visitor, and makes sure only one caller uses the visitor singleton at a time. As our code is mainly single threaded, that's no problem. Otherwise it might be good to have one singleton instance per thread if it's a finite thread pool.
After all, the whole mechanism is not as simple anymore as I would like it to be, but now it seems to perform well.
Rgds
Richard
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Wouldn't a cached factory will be just what you need ?
Instead on creating the visitor on the stack\heap just create it with a Loki cached factory.
this solution is very scalable for later MT and very easy to implement using Loki.
Shaul
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
We are in the process of switching from a c-style-type-switch to loki::visitor.
Our class hierarchy has 54 classes at the moment.
I have them grouped like this:
It works well, and compared to the former approach, is a pleasure to work with.
But recently I discovered, that the linker almost uses up all the available RAM.
By looking closer, I discovered that some obj files increased the size tenfold e.g. form 2MB to 20MB.
Analyzing with dumpbin.exe I found that loki indeed contributes a good fraction of the symbols found in the obj.
Especially the deeply nested typelists.
Is there a way to prevent this bloat?
Solved it with the following actions:
1) instead of
typedef Loki::CyclicVisitor<void, TlObGlEntityClasses> ObGlVisitor;
I'm using
class __declspec(dllexport) ObGlVisitor : public Loki::CyclicVisitor<void, TlObGlEntityClasses> { };
which allows me to forward declare ObGlVisitor.
2) Instead of using the LOKI_DEFINE_CYCLIC_VISITABLE macro, I have split the definition and implementation in the header and cpp files.
3) Moved the definition of the concrete visitor into a separate header file, thus the client code that only uses the functionality only gets a few function definitions while only the files that are concerned with the implementation of the functionality get to know the visitor.
4) Using explicit template instantiation declaration and -definition for ObGlVisitor as described at:
http://gameangst.com/?p=246
The combined effect is that the summed size of the obj files is now below what it was before applying the visitor.
Thanks for posting that tip for others. I like how C++ allows forward referencing to reduce the number of header files developers must include. As you show here, forward referencing also reduces code-bloat when working with complex templated typedefs.
Cheers,
Rich
In the meantime we had to solve another problem.
For fast dispatching, we use the cyclic visitor. And at dispatching it really is fast, but a co-worker discovered, that the visitor is slow in construction. The disassembly revealed that the constructor of the visitor builds a huge vtable for all the 54 incarnations of the recursive template. I would expect the compiler to be able to optimise these base classes away as they only contain a pure abstract function definition. But MSVC8 doesn't, others might?
So I put the visitor into a singleton which can only be accessed by an accompanying RAII manager class. The manager class sets the member variables of the visitor, and makes sure only one caller uses the visitor singleton at a time. As our code is mainly single threaded, that's no problem. Otherwise it might be good to have one singleton instance per thread if it's a finite thread pool.
After all, the whole mechanism is not as simple anymore as I would like it to be, but now it seems to perform well.
Rgds
Richard
Hi,
Wouldn't a cached factory will be just what you need ?
Instead on creating the visitor on the stack\heap just create it with a Loki cached factory.
this solution is very scalable for later MT and very easy to implement using Loki.
Shaul