SLIDE 1
Streamlined InputRecord / DataAllocator API
Giulio Eulisse (CERN) WP4 - 24th Nov 2017
1
SLIDE 2 Improved API for DataAllocator (1)
- 2::framework::DataAllocator is the class which users must use to create
- utput messages to be sent to the downstream DataProcessors.
It is now possible to create and serialise an instance of a TObject derived class via:
DataAllocator::make<T>(OutputSpec &spec, Args... args);
e.g. in 1:
[](ProcessingContext &ctx) { auto h = ctx.allocator().make<TH1>(OutputSpec{”QC”, ”HISTO”}, ”h”, ”test”, 100, -10., 10.); }
1https://github.com/AliceO2Group/AliceO2/blob/dev/Utilities/QC/Workflow/src/RootObjectProducerSpec.cxx
2
SLIDE 3
Improved API for DataAllocator (2)
Similarly the API to create PODs or Collection<POD> has been revamped and made similar to the ROOT one:
struct XYZ { float x,y,z; }; ... [](ProcessingContext &ctx) { // Create a XYZ instance which will be sent once lambda completes auto &a = ctx.allocator().make<XYZ>(OutputSpec{”TPC”, ”POINT”}); // Create a Collection<XYZ> with 1000 instances in it. auto &c = ctx.allocator().make<XYZ>(OutputSpec{”TPC”, ”POINT”}, 1000); }
3
SLIDE 4
Improved API for InputRecord (1)
An o2::framework::InputRecord represents the inputs being passed to the processing function. The API to access the various parts of a record is now orthogonal as well. In the ROOT case it becomes:
{InputSpec{”histos”, ”QC”, ”HISTO”}} ... [](ProcessingContext &ctx) { auto h = ctx.inputs().get<TH1F>(”histos”); }
Notice that h will be a copy of the actual contents of the message buffer, due to the fact ROOT selializes objects.
4
SLIDE 5
Improved API for InputRecord (2)
The same API can be used to extract a POD instance from the message as well:
{InputSpec{”points”, ”QC”, ”HISTO”}} ... [](ProcessingContext &ctx) { XYZ &x = ctx.inputs().get<XYZ>(”points”); }
5
SLIDE 6
Improved API for InputRecord (3)
By default, however, InputRecord::get still returns a o2::framework::DataRef i.e. the (header, payload) pointers.
DataRefUtils::as<T> can then be used to extract stuff as well. {InputSpec{”points”, ”TPC”, ”POINTS”}} ... [](ProcessingContext &ctx) { auto ref = ctx.inputs().get(”points”); Collection<XYZ> c = DataRefUtils::as<XYZ>(ref); }
6
SLIDE 7 I already have an object…
Use DataAllocator::adopt to surrender object ownership to the framework. E.g. from Matthias last presentation2:
auto dataobject = std::unique_ptr<TObject>(producer->produceData()); auto serialized = std::make_unique<TMessage>(kMESS_OBJECT); serialized->WriteObject(dataobject.get()); auto tgt = pc.allocator().newChunk(OutputSpec{”QC”, ”ROOTOBJECT”, 0, OutputSpec::QA}, serialized->BufferSize()); memcpy(tgt.data, serialized->Buffer(), tgt.size);
becomes:
pc.allocator().adopt(OutputSpec{”QC”, ”ROOTOBJECT”, 0, OutputSpec::QA}, producer->produceData());
2https://github.com/AliceO2Group/AliceO2/blob/dev/Utilities/QC/Workflow/src/RootObjectProducerSpec.cxx
7
SLIDE 8
Suggested future improvements
A non-owning pointer o2::ref<T>. Returned by ::make<T>, ::get<T> methods whenever the object is actually owned by the DPL. At the moment I use a standard reference, but this has the drawback that if you are not careful you end up with a copy of the contents. A static helper method o2::make<T>. To be used in place of
DataAllocator::make<T> to hide the need to know about the allocator.
Similarly o2::adopt<T> could be provided. Pass InputRecord directly. Assuming getting the inputs is very frequent,
ctx.inputs().get(”something”) would becomes inputs.get(”somthing”) }
8