diff --git a/core/meta/inc/TIsAProxy.h b/core/meta/inc/TIsAProxy.h index 84e6747a64b73ee7231a41985358c88f2f6161dc..2680824b4c133e7ad5836a04ead15d34f28957c3 100644 --- a/core/meta/inc/TIsAProxy.h +++ b/core/meta/inc/TIsAProxy.h @@ -28,14 +28,22 @@ class TIsAProxy : public TVirtualIsAProxy { private: template <typename T> using Atomic_t = std::atomic<T>; + // On testing with the data from the 250202_181_RECO.root and doing "just" serializing + // the value 8 was the sweet spot of performance. With more slots, too much time is + // spent scanning the array of "last" seen and with less slots then the + // serialization induced by and/or the cost of executed `++fSubTypesReaders is slow + // down (noticeably) the streaming of branches with polymorphic containers. + static constexpr UInt_t fgMaxLastSlot = 8; + const std::type_info *fType; //Actual typeid of the proxy Atomic_t<TClass*> fClass; //Actual TClass - Atomic_t<void*> fLast; //points into fSubTypes map for last used values Char_t fSubTypes[72];//map of known sub-types mutable Atomic_t<UInt_t> fSubTypesReaders; //number of readers of fSubTypes Atomic_t<Bool_t> fSubTypesWriteLockTaken; //True if there is a writer Bool_t fVirtual; //Flag if class is virtual Atomic_t<Bool_t> fInit; //Initialization flag + Atomic_t<void*> fLasts[fgMaxLastSlot]; // points into fSubTypes map for last used values + Atomic_t<UChar_t> fNextLastSlot;// Next slot in fLasts to use for update (ring buffer) void* FindSubType(const std::type_info*) const; void* CacheSubType(const std::type_info*, TClass*); diff --git a/core/meta/src/TIsAProxy.cxx b/core/meta/src/TIsAProxy.cxx index 890510168ecea7c9ca5eb30608a4f41b3ad6912e..5039c659945bc247afdff9e996c1964d41563957 100644 --- a/core/meta/src/TIsAProxy.cxx +++ b/core/meta/src/TIsAProxy.cxx @@ -39,19 +39,22 @@ namespace { { return (ClassMap_t::value_type*)p; } + } //////////////////////////////////////////////////////////////////////////////// /// Standard initializing constructor TIsAProxy::TIsAProxy(const std::type_info& typ) - : fType(&typ), fClass(nullptr), fLast(nullptr), + : fType(&typ), fClass(nullptr), fSubTypesReaders(0), fSubTypesWriteLockTaken(kFALSE), fVirtual(kFALSE), fInit(kFALSE) { static_assert(sizeof(ClassMap_t)<=sizeof(fSubTypes), "ClassMap size is to large for array"); ::new(fSubTypes) ClassMap_t(); + for(auto& slot : fLasts) + slot = nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -72,7 +75,8 @@ void TIsAProxy::SetClass(TClass *cl) { GetMap(fSubTypes)->clear(); fClass = cl; - fLast = nullptr; + for(auto& slot : fLasts) + slot = nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -106,20 +110,30 @@ TClass* TIsAProxy::operator()(const void *obj) if ( typ == fType ) { return fClass.load(); } - auto last = ToPair(fLast.load()); - if ( last && typ == last->first ) { - return last->second; + for(auto& slot : fLasts) { + auto last = ToPair(slot); + if ( last && typ == last->first ) { + return last->second; + } } + // Check if type is already in sub-class cache - last = ToPair(FindSubType(typ)); + auto last = ToPair(FindSubType(typ)); if ( last == nullptr || last->second == nullptr ) { // Last resort: lookup root class auto cls = TClass::GetClass(*typ); last = ToPair(CacheSubType(typ,cls)); } - fLast.store(last); - return last == nullptr? nullptr: last->second; + UChar_t next = fNextLastSlot++; + if (next >= fgMaxLastSlot) { + UChar_t expected_value = next + 1; + next = next % fgMaxLastSlot; + fNextLastSlot.compare_exchange_strong(expected_value, next + 1); + } + fLasts[next].store(last); + + return last == nullptr ? nullptr: last->second; } ////////////////////////////////////////////////////////////////////////////////