Boost logo

Boost Users :

Subject: Re: [Boost-users] Flyweight: wrapping shared_ptr
From: Akim Demaille (akim_at_[hidden])
Date: 2014-10-10 09:12:05


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 <joaquin_at_[hidden]> a écrit :

>> 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.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net