New and faster decoding versions
Requires MR!10 from SIMCOND.
Introduced new raw data format (v4 and v5). The new data format has no local headers for each SiPM anymore. This improves the decoding speed a lot. Also new is the partitioning (5 banks per quadrant instead of 1) and a global truncation header.
For backwards compatibility, the decoding can still decode the old data formats (v2 and v3). The difference between v4 and v5 is that in v5 the adjacent clusters (that are split by the FE) are merged back and that very wide clusters are recovered into many adjacent clusters. These extra operations are not done in v4, which makes it a little bit faster at the costs of a reduction in tracking performance (possible these extra operations can be done on the TELL40 in the future). The encoding works only for the v5 format. To run the v4 decoding on v5 data, a flag is introduced in FTRawBankDecoder
(ForceVersion4
).
At same time, the container format for the FTLiteClusters is changed from FastClusterContainer to MultiIndexedContainer.
Closes LHCBSCIFI-101.
Merge request reports
Activity
- FT/FTDAQ/src/FTNewRawBankDecoder.cpp 0 → 100644
43 FTNewRawBankDecoder::operator()(const LHCb::RawEvent& rawEvent) const 44 { 45 const auto& banks = rawEvent.banks(LHCb::RawBank::FTCluster); 46 47 // Simple loop over the banks to determine total number of clusters 48 // Bank size is half the number of clusters and includes headers 49 // No 0.4 scaling 50 // In the future, the #clusters should be encoded in raw bank. 51 FTLiteClusters clus; 52 int totSize = 0; 53 for ( const LHCb::RawBank* bank : banks) { totSize += bank->size();} 54 clus.reserve(totSize); 55 56 // Store partition points for quadrants for faster sorting 57 std::vector<int> partitionPoints; 58 partitionPoints.reserve(48); // 48 quadrants Thanks! It's always 48 as far as I understood (3 stations, 4 layers, 4 quadrants). However, we want to fill it in order (using push_back then), so the boost solution looks much better.
Edited by Louis HenryIsn't there already header which defines eg
NStations
,NLayers
andNQuadrants
(to be 3,4 and 4 respectively)? In that case, I'd write this asstd::array<int,NStations*NLayers*NQuadrants>
-- and if not, I'd introduce those three constants first ;-)(note: these three constants should be qualified
constexpr
so that their product can be evaluated at compile-time)
- FT/FTDAQ/src/FTNewRawBankDecoder.cpp 0 → 100644
128 short int c = *it; 129 unsigned channel = ( c >> FTRawBank::cellShift ) & FTRawBank::cellMaximum; 130 int fraction = ( c >> FTRawBank::fractionShift ) & FTRawBank::fractionMaximum; 131 bool cSize = ( c >> FTRawBank::sizeShift ) & FTRawBank::sizeMaximum; 132 133 //not the last cluster 134 if( !cSize && it<first+nClus-1 ){ 135 short int c2 = *(it+1); 136 bool cSize2 = ( c2 >> FTRawBank::sizeShift ) & FTRawBank::sizeMaximum; 137 138 if( !cSize2 ){ //next cluster is not last fragment 139 clus.emplace_back(LHCb::FTChannelID{ 0,0,uniqueQuarter, 140 module, mat, sipm, channel }, 141 fraction, 4 ); 142 143 if ( msgLevel( MSG::VERBOSE ) ) { Putting
if
statements in a loop will not make it go faster. Please consider splitting things (duplicating!) into two loops: one which does the work, and one which just does the printout -- i.e. something like:const auto last = bank->end<short int>(); if (msgLevel(MSG::VERBOSE)) { auto first = bank->begin<short int>(); while (first!=last) { // generate just the output } } auto first = bank->begin<short int>(); while (first!=last) { // do the decoding without any verbose() output } }
In case that generates to much 'duplicate code', then consider doing something along the lines of:
enum class Verbose { True, False }; FTLiteClusters FTNewRawBankDecoder::operator()(const LHCb::RawEvent& rawEvent) const { return msgLevel(MSG::VERBOSE) ? decode<Verbose::True>(rawEvent) : decode<Verbose::False>(rawEvent); } template <Verbose verbose> FTLiteClusters FTNewRawBankDecoder::decode( const LHCb::RawEvent& rawEvent) const { { // take current code in operator(), and replace 'msgLevel(MSG::VERBOSE)' with 'verbose==Verbose::True' }
This will then generate two versions of
decode
, one 'slow' one with printout, and a 'fast' one without any printout, and do the check just once, and dispatch the relevant version.
- FT/FTDAQ/src/FTNewRawBankDecoder.cpp 0 → 100644
85 << " for source " << source << " size " << size << " bytes." 86 << endmsg; 87 throw GaudiException("Unsupported FT bank version", 88 "FTNewRawBankDecoder", 89 StatusCode::FAILURE); 90 } 91 92 auto first = bank->begin<short int>(); 93 auto last = bank->end<short int>(); 94 95 while( first != last ) { 96 int sipmHeader = *first++; 97 if ( first == last && sipmHeader == 0 ) continue; // padding at the end... 98 unsigned modulesipm = sipmHeader >> FTRawBank::sipmShift ; 99 unsigned module = modulesipm >> FTRawBank::sipmShift ; 100 LHCb::FTChannelID chanModuleSiPM = m_ftReadoutTool->sipm(modulesipm); Do you really need a separate tool to determine the channelID?
One reason to have such a tool is if there are multiple implementation which you want to be able to interchange, and thus you need a well-defined interface (
IFTReadoutTool
in this case). But is there really more than one implementation necessary?Another reason would be if there would be another place where the same functionality is required, i.e. if the tool would be shared between algorithms. So the question then is, is that the case?
If either of the two questions is yes, then there still remains the question on whether an interface which accepts a single unsigned, and returns an
FTChannelID
is optimal. For example, is it possible to return (a reference to) a (concrete) map (lookup table, ...) frommodulesipm
to (mat
,sipm
) which can be retrieved outside of the loop (and thus avoid a virtual function call inside the loop)Leaving the two first questions to later, what do you have in mind? Another property of FTRawBankDecoder that point to an xml (probably linked to the DDDB) that makes the mapping FTChannelID -> (module/sipm/channelID), leaving only station/layer/quarter/mat to the logical operations we are performing?
- FT/FTDAQ/src/FTNewRawBankDecoder.cpp 0 → 100644
20 // Standard constructor, initializes variables 21 //============================================================================= 22 FTNewRawBankDecoder::FTNewRawBankDecoder( const std::string& name, 23 ISvcLocator* pSvcLocator) 24 : Transformer ( name , pSvcLocator, 25 KeyValue{ "RawEventLocations", 26 Gaudi::Functional::concat_alternatives( LHCb::RawEventLocation::Other, 27 LHCb::RawEventLocation::Default )}, 28 KeyValue{ "OutputLocation", LHCb::FTLiteClusterLocation::Default } ) 29 { } 30 31 StatusCode FTNewRawBankDecoder::initialize() 32 { 33 StatusCode sc = Transformer::initialize(); 34 m_ftReadoutTool = tool<IFTReadoutTool>("FTReadoutTool",this); 35 m_ftReadoutTool->initialize(); Looking at the contents of
FTReadoutTool
, I wonder why it is a separate tool, and not just substituted 'in situ' in this algorithm. What triggered me is the need for aninitialize()
method in the tool. That suggest that the code must be tied into the Gaudi state machine, and at that point, you have an algorithm, not a tool. Hence the questions below, which are aimed at figuring out what is really needed, and whether a tool is the right thing to implement this.
- FT/FTDAQ/src/FTNewRawBankDecoder.h 0 → 100644
1 #ifndef FTNEWRAWBANKDECODER_H I would avoid adjectives like 'New' instead class names, as it will look weird in a few years time (and what will you call the next version? FtNewerRawBankDecoder?)
Instead, I'd put it in a namespace with a version number. And given that I think it would be good to put subsystem code in a dedicated namespace which is close to the package name, why not something like
namespace LHCb { namespace FT { namespace DAQ { namespace v2 { class RawBankDecoder : ... } } } }
Ideally, all subsystems would adopt such a common namespace pattern... (and eg. Rich::DAQ does already exist ;-)
I did not know about the namespace pattern, that seems neat! Concerning the NewRawBankDecoder, this is temporary (and I mean, really temporary) because we don't know which version we want to use and I wanted to start working in parallel. As I still try to master the intricacies of GIT I did not want to use two branches.
381 else{ 382 if( !cSize1 && it<last-1 && (getSipm(modulesipm) == getSipm(modulesipm2))) 383 { 385 384 make_clusters(firstChannel,c,c2); 386 385 ++it; 387 }//last edge found 388 }//not the last cluster 389 390 else{ //last cluster, so nothing we can do 391 make_cluster(source+modulesipm, fraction1, 4 ); 392 }//last cluster added 393 }//end loop over clusters in one sipm 394 }//end loop over rawbanks 386 } 387 else {//fragmented cluster, last edge found 388 make_cluster(source+modulesipm, fraction1, 4 ); mentioned in merge request Rec!1072 (merged)
mentioned in merge request Boole!153 (merged)
added 1 commit
- fc44900a - Simplify estimate for number of clusters from raw bank
added 1 commit
- 54bdfd8e - Split output from outer Tell40s in two banks and add 32b truncation header
- [2018-05-31 00:13] Validation started with lhcb-gaudi-head#1883
- [2018-05-31 11:06] Validation started with lhcb-gaudi-head#1884
Edited by Software for LHCb- Resolved by Marco Cattaneo
- Resolved by Marco Cattaneo
- Resolved by Marco Cattaneo
- Resolved by Marco Cattaneo