0

I'm having an issue solving the problem of dynamic allocation... I have a function reduce which accumulates values, and in this case I am iterating over boost::tuple<double*,double*>'s. My issue is here:

//executes this code in chunks, asynchronously
boost::tuple<double*,double*> res = hpx::parallel::reduce(hpx::parallel::par,
    iter, iter+4,                                                         
    boost::make_tuple<double*,double*>(g,h), [](reference a, reference b) { 
        double *res= new double;  //dynamic allocation! I don't have anyway to delete this memory afterwards
        *res = *boost::get<1>(b) * *boost::get<0>(b);
        return boost::make_tuple<double*,double*>(res,res); });

the function parameters are

template< typename ExPolicy, typename Iter, typename T, typename Func>
T reduce(ExPolicy execution_policy, Iter being, Iter end, T && start, Func && op)

How can I avoid possibly creating memory leaking by dynamically allocating variable, but still be able to fill a tuple with two pointers?

hkaiser
  • 11,403
  • 1
  • 30
  • 35
Syntactic Fructose
  • 18,936
  • 23
  • 91
  • 177

1 Answers1

2

I'd much rather just see this as a tuple of doubles (or array thereof); it removes all the guesswork that a tuple of pointers to doubles has created.

Instead of thinking of this as a potential leak, think of it in terms of ownership. When you declare a local variable of type double, the stack owns it and it will be cleaned up. When you create one of type double*, the stack owns the pointer but does not own the value, so the value may not be cleaned up. Clearly just declaring a double is simpler and easier, so when that's an option, prefer it.

Now consider other potential owners. A tuple containing POD types such as std::tuple<double, double> would own and then clean up the doubles, but a tuple containing a pointer type isn't clear. Cleaning up a std::tuple<double*, double*> won't clean up the doubles. Clearly just using a tuple of doubles is simpler and easier, so if that's an option, prefer it.

But, I'm assuming for various reasons you have to use a tuple of pointers to doubles.

Consider then what lifetime you need for your underlying data. How can you get this lifetime? Can you give ownership of the doubles to something else that will get cleaned up at the right time, and store their addresses in the tuple's non-owning pointers to doubles? For instance have your lambda capture an external container by reference, and put your doubles in it, and store just their addresses in the tuples.

Here's an attempt to show what I mean...but be wary doing this with the vector as I show it. I know nothing of hpx::parallel::reduce, but I assume that its parallel nature will render this simplified version completely unsafe. Two interleaved calls to push_back and back will result in incorrect tuples being created; two overlapping calls to push_back could easily corrupt the vector. A more complex version could synchronize its use of the container.

std::vector<double> v; // XXX: probably unsafe for parallel reduce

boost::tuple<double*,double*> res = hpx::parallel::reduce(hpx::parallel::par,
    iter, iter+4,                                                         
    boost::make_tuple<double*,double*>(g,h), [&v](reference a, reference b) { 
        v.push_back(*boost::get<1>(b) * *boost::get<0>(b));
        return boost::make_tuple<double*,double*>(&v.back(), &v.back()); });
Michael Urman
  • 15,737
  • 2
  • 28
  • 44
  • thanks for the response, and yes due to various reasons I cannot just use ``(though I wish I could have). The file specifically is only a benchmark, so I'd like to have the pointers stay alive until the end of the test(which is not long at all). I attempted to do something similar to your `vector` but with an `atomic`, but c++11 does not support a `+=` operator for atomic doubles and I was shot back down. – Syntactic Fructose Jul 02 '14 at 21:10
  • @SyntacticFructose If your program's lifetime doesn't significantly exceed the lifetime of your doubles, it's not necessarily unreasonable to leak. (Unless that would invalidate what you're trying to measure with your benchmark.) Of course, to use the results, you'll still need a real solution. – Michael Urman Jul 03 '14 at 12:40