Hi Joaquín,
You are a tough one, I'm surprised (and very pleased) you still answer
to this thread :)
Le 10 oct. 2014 à 14:00, Joaquin M Lopez Munoz
a. a single factory based on Flyweight, and use derived_poly_flyweight
b. one factory made by hand on top of map or unordered_map
c. likewise, but with Flyweight and key_value
As for c, you can have something more or less along that line by using your latest attempt (http://coliru.stacked-crooked.com/a/2b768fa26574adea ), declaring the flyweights with the no_tracking policy and having them store a weak_ptr (which boild down to your b hand-made scenario.)
Well, I couldn't get this work. In fact b neither. There are several issues. One is that the key-value approach, while appealing (you don't build at all if the object of this type was already built) keeps value alive because they are keys. Consider Bin('+', Num(42), Num(51)). To build it, I have used the key <'+', Exp(Num(42)), Exp(Num(51))>, to map it to a weak pointer. But when I kill the pointer, the key remains, and therefore 42 and 51 are still alive and are not reclaimed. So I tried a set of weak_pointer, but couldn't have it work. In fact, my problem is that I would like to get a call-back from the shared_ptr at use_count == 1, not == 0 (to erase the value from the factory). But I could not find a means to have that. I have also tried to store real objects in the factory, and use shared_from_this to create the shared_ptr, but this is illegal: shared_ptr can only be called when there is already a shared_ptr somewhere.
Well, this is not so clear to me, as the expressions I store have often a strong regularity, with many elements of the same kind in a row.
Of course, benchmarking is the best way to go.
I agree.
I'd be interested in knowing your outcomes.
Well, you won: the single store is roughly 20% faster in my experiment. I am surprised, I really expected the in-place allocation in the per-type factories to be much cheaper that the pointer and malloc agitation in the case of the flyweight of single-pointer type. Maybe my attempt to have a polymorphic wrapper around flyweight is slow, I don't know. But after all, it is true that most of the time these values are manipulated via the base type, which is cheap (well, gratis) in the single-factory case, while it requires quite some allocations in the per-type-factory case. So after all, it might make some sense anyway. The bench was as follows: for (unsigned i = 0; i < 500U * 1000U; ++i) { Num n = num(1); Bin e = bin('*', bin('+', num(1), num(1)), bin('+', num(1), num(1))); // Duplicates large parts of e. Exp f = bin('/', bin('*', bin('+', num(1), num(1)), bin('+', num(1), num(1))), bin('*', bin('+', num(1), num(1)), bin('+', num(1), num(1)))); assert(f->eval() == 1); } rm 7 9 && make 7 9 && time ./7 && time ./9 clang++-mp-3.5 -std=c++11 -isystem /opt/local/include -ggdb -O3 7.cc -o 7 clang++-mp-3.5 -std=c++11 -isystem /opt/local/include -ggdb -O3 9.cc -o 9 real 0m4.550s This is single factory user 0m4.540s sys 0m0.003s real 0m5.261s This is per-type factory user 0m5.256s sys 0m0.003s The corresponding files are here: single: http://coliru.stacked-crooked.com/a/d7028e28fcc86217 per-type: http://coliru.stacked-crooked.com/a/4566f6e2e7dd6d81 Again, thanks for your feedback.