Context: On one of my C++11 application, object serialization and publish of message is time consuming. Therefore I want to do it in a separate thread using Intel TBB library (more specifically using a tbb::task_group)
Issue: the object to serialize is a struct where some of the properties are std::vector<std::unique_ptr<SomeObject>>
, making it impossible to pass by copy to the lambda executed in a task
Approximately it look like
struct MockedDC {
MockedDC(int x, std::vector<std::unique_ptr<SomeObject>> v) : x(x),
v(std::move(v)) {};
int x;
std::vector<std::unique_ptr<SomeObject>> v;
};
The "solution" I found, is to reconstruct on the heap with the move-constructor my instance and wrap it in a shared_ptr<MockedDC>
which is copyable. In the end the function which invoke the tbb::task_group::run
look like
// function called like this `executeInThread(taskGroup, std::move(mockedDC))`
void executeInThread(tbb::task_group& taskGroup, MockedDC mockedDC) {
const std::shared_ptr<MockedDC> sharedMockedDC(new MockedDC(std::move(mockedDC)));
auto f = [sharedMockedDC] {
const auto serialized(serializer(*sharedMockedDC)); // pass by reference
publish(serialized);
};
taskGroup.run(f);
};
it compile and run fine, but I can't put it under pressure as it will be in real life condition so my question is is it safe/sane to do this ?
I found on another stackoverflow question an alternative, but the implementation looks difficult to maintain given my C++ knowledge :) that's why I want to stick with the shared_ptr
approach as suggested somewhere else
What I tried so far: I wrote a dummy code to test the thing, but I think its not enough to validate this approach. I also wanted to compile with some sanitization flags, but tbb fail to link with a bunch of errors like undefined reference to __ubsan_handle_pointer_overflow
Here is the dummy example if that help to answer (it compile and run without issues (except some int overflow but that not an issue I guess))
#include <cstdio>
#include <iostream>
#include <memory>
#include <vector>
#include <numeric>
#include "tbb/task_scheduler_init.h"
#include "tbb/task_group.h"
struct MockedDC {
MockedDC(int seed, size_t baseLen) : seed(seed), baseLen(baseLen) {
this->a_MDC.reserve(baseLen);
for (size_t i = 0; i < baseLen; ++i)
this->a_MDC.emplace_back(new int((seed + i) / (seed + 1)));
};
int seed;
size_t baseLen;
std::vector<std::unique_ptr<int>> a_MDC;
};
void executeInThread(tbb::task_group& taskGroup, MockedDC mockedDC) {
const std::shared_ptr<MockedDC> sharedMockedDC(new MockedDC(std::move(mockedDC)));
auto f = [sharedMockedDC] {
std::cout <<
std::accumulate(sharedMockedDC->a_MDC.begin(), sharedMockedDC->a_MDC.end(), 0, [](int acc, const std::unique_ptr<int>& rhs) {
return acc + *rhs;
})
<< std::endl << std::flush;
};
taskGroup.run(f);
};
void triggerTest(tbb::task_group& taskGroup) {
for (size_t i = 0; i < 1000000; ++i) {
MockedDC mdc(i, 10000000);
executeInThread(taskGroup, std::move(mdc));
}
return ;
};
int main() {
tbb::task_scheduler_init tbbInit(tbb::task_scheduler_init::automatic);
//tbb::task_scheduler_init tbbInit(8);
tbb::task_group taskGroup;
triggerTest(taskGroup);
taskGroup.wait();
return (0);
};
PS: using C++14 new capture by move doesn't work because of TBB library :/
Aucun commentaire:
Enregistrer un commentaire