Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RPagePool.hxx
Go to the documentation of this file.
1/// \file ROOT/RPagePool.hxx
2/// \ingroup NTuple
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-09
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#ifndef ROOT_RPagePool
15#define ROOT_RPagePool
16
17#include <ROOT/RPage.hxx>
20#include <ROOT/RNTupleTypes.hxx>
21
22#include <cstddef>
23#include <map>
24#include <mutex>
25#include <typeindex>
26#include <typeinfo>
27#include <unordered_map>
28#include <unordered_set>
29#include <vector>
30
31namespace ROOT {
32namespace Internal {
33
34class RPageSource;
35
36// clang-format off
37/**
38\class ROOT::Internal::RPagePool
39\ingroup NTuple
40\brief A thread-safe cache of pages loaded from the page source.
41
42The page pool is used as a cache for pages loaded from a page source.
43In this way, identical page needed at the same time, only need to be loaded once.
44Page sources also use the page pool to stage (preload) pages unsealed by IMT tasks.
45*/
46// clang-format on
47class RPagePool {
48 friend class RPageRef;
49
50public:
51 // Search key for a set of pages covering the same column and in-memory target type.
52 // Within the set of pages, one needs to find the page of a given index.
53 struct RKey {
55 std::type_index fInMemoryType = std::type_index(typeid(void));
56
57 bool operator==(const RKey &other) const
58 {
59 return this->fColumnId == other.fColumnId && this->fInMemoryType == other.fInMemoryType;
60 }
61
62 bool operator!=(const RKey &other) const { return !(*this == other); }
63 };
64
65private:
66 /// Hash function to be used in the unordered map fLookupByKey
67 struct RKeyHasher {
68 /// Like boost::hash_combine
69 std::size_t operator()(const RKey &k) const
70 {
71 auto seed = std::hash<ROOT::DescriptorId_t>()(k.fColumnId);
72 return seed ^ (std::hash<std::type_index>()(k.fInMemoryType) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
73 }
74 };
75
76 /// Every page in the page pool is annotated with a search key and a reference counter.
77 struct REntry {
80 std::int64_t fRefCounter = 0;
81 };
82
83 /// Used in fLookupByKey to store both the absolute and the cluster-local page index of the referenced page.
84 /// This allows to do binary search for one or the other. Note that elements in fLookupByKey must have
85 /// _both_ values to be valid. If RPagePosition is used as a search key, only one of the two needs to be set.
89
90 bool operator<(const RPagePosition &other) const
91 {
93 (other.fGlobalFirstElement != ROOT::kInvalidNTupleIndex)) {
94 return fGlobalFirstElement < other.fGlobalFirstElement;
95 }
96
99 assert(other.fClusterFirstElement.GetClusterId() != ROOT::kInvalidDescriptorId &&
100 other.fClusterFirstElement.GetIndexInCluster() != ROOT::kInvalidNTupleIndex);
101 if (fClusterFirstElement.GetClusterId() == other.fClusterFirstElement.GetClusterId())
102 return fClusterFirstElement.GetIndexInCluster() < other.fClusterFirstElement.GetIndexInCluster();
103 return fClusterFirstElement.GetClusterId() < other.fClusterFirstElement.GetClusterId();
104 }
105
106 // Constructor used to store a page in fLookupByKey
107 explicit RPagePosition(const RPage &page)
108 : fGlobalFirstElement(page.GetGlobalRangeFirst()),
109 fClusterFirstElement({page.GetClusterInfo().GetId(), page.GetLocalRangeFirst()})
110 {
111 }
112
113 // Search key constructors
116 };
117
118 /// Performance counters that get registered in fMetrics
122 std::unique_ptr<RCounters> fCounters;
123
124 /// Every page pool is associated to exactly one page source. The page source is queried for pinned cluster
125 /// when pages are released.
127 std::vector<REntry> fEntries; ///< All cached pages in the page pool
128 /// Used in ReleasePage() to find the page index in fPages
129 std::unordered_map<void *, std::size_t> fLookupByBuffer;
130 /// Used in GetPage() to find the right page in fEntries. Lookup for the key (pair of on-disk and in-memory type)
131 /// takes place in O(1). The selected pages are identified by index into the fEntries vector (map's value)
132 /// and sorted by the position of the page in the column (map's key). Thus, access to pages of the page set
133 /// has logarithmic complexity.
134 std::unordered_map<RKey, std::map<RPagePosition, std::size_t>, RKeyHasher> fLookupByKey;
135 /// Remembers pages with reference counter 0, organized by the page's cluster id. The pages are identified
136 /// by their page buffer address. The fLookupByBuffer map can be used to resolve the address to a page.
137 /// Once a page gets used, it is removed from the unused pages list. Evict will remove all unused pages
138 /// from a given cluster id.
139 std::unordered_map<ROOT::DescriptorId_t, std::unordered_set<void *>> fUnusedPages;
140 std::mutex fLock; ///< The page pool is accessed concurrently due to parallel decompression
141
142 /// The page pool counters are observed by the page source
144
145 /// Add a new page to the fLookupByBuffer and fLookupByKey data structures.
146 REntry &AddPage(RPage page, const RKey &key, std::int64_t initialRefCounter);
147
148 /// Give back a page to the pool and decrease the reference counter. There must not be any pointers anymore into
149 /// this page. If the reference counter drops to zero, the page pool might decide to call the deleter given in
150 /// during registration. Pages of pinned clusters are given back to the "unused pages" pool and are not immedately
151 /// evicted. Called by the RPageRef destructor.
152 void ReleasePage(const RPage &page);
153
154 /// Called by PreloadPage() if the page at hand is new and thus added with ref counter 0
155 void AddToUnusedPages(const RPage &page);
156 /// Called by GetPage(), when the reference counter increases from zero to one
157 void RemoveFromUnusedPages(const RPage &page);
158
159 /// Called both by ReleasePage() and by Evict() to remove an unused page from the pool
160 void ErasePage(std::size_t entryIdx, decltype(fLookupByBuffer)::iterator lookupByBufferItr);
161
162public:
164 RPagePool(const RPagePool&) = delete;
165 RPagePool& operator =(const RPagePool&) = delete;
166 ~RPagePool() = default;
167
168 /// Adds a new page to the pool. Upon registration, the page pool takes ownership of the page's memory.
169 /// The new page has its reference counter set to 1.
171 /// Like RegisterPage() but the reference counter is initialized to 0. In addition, the page is added
172 /// to the set of unused pages of the page's cluster (see Evict()).
173 void PreloadPage(RPage page, RKey key);
174 /// Removes unused pages (pages with reference counter 0) from the page pool. Users of PreloadPage() should
175 /// use Evict() appropriately to avoid accumulation of unused pages.
177 /// Tries to find the page corresponding to column and index in the cache. If the page is found, its reference
178 /// counter is increased
181
183};
184
185// clang-format off
186/**
187\class ROOT::Internal::RPageRef
188\ingroup NTuple
189\brief Reference to a page stored in the page pool
190
191The referenced page knows about its page pool and decreases the reference counter on destruction.
192*/
193// clang-format on
194class RPageRef {
195 friend class RPagePool;
196
199
200 // Called as delegated constructor and directly by the page pool
202 {
203 // We leave the fPage::fPageAllocator member unset (nullptr), since fPage is a non-owning view on the page
204 fPage.fBuffer = page.fBuffer;
205 fPage.fElementSize = page.fElementSize;
206 fPage.fNElements = page.fNElements;
207 fPage.fMaxElements = page.fMaxElements;
208 fPage.fRangeFirst = page.fRangeFirst;
209 fPage.fClusterInfo = page.fClusterInfo;
210 }
211
212public:
213 RPageRef() = default;
214 RPageRef(const RPageRef &other) = delete;
215 RPageRef &operator=(const RPageRef &other) = delete;
216
218
220 {
221 if (this != &other) {
222 std::swap(fPage, other.fPage);
223 std::swap(fPagePool, other.fPagePool);
224 }
225 return *this;
226 }
227
229 {
230 if (fPagePool)
232 }
233
234 const RPage &Get() const { return fPage; }
235};
236
237} // namespace Internal
238} // namespace ROOT
239
240#endif
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
A thread-safe integral performance counter.
A collection of Counter objects with a name, a unit, and a description.
A thread-safe cache of pages loaded from the page source.
Definition RPagePool.hxx:47
std::unique_ptr< RCounters > fCounters
void ErasePage(std::size_t entryIdx, decltype(fLookupByBuffer)::iterator lookupByBufferItr)
Called both by ReleasePage() and by Evict() to remove an unused page from the pool.
Definition RPagePool.cxx:72
ROOT::Experimental::Detail::RNTupleMetrics fMetrics
The page pool counters are observed by the page source.
void Evict(ROOT::DescriptorId_t clusterId)
Removes unused pages (pages with reference counter 0) from the page pool.
REntry & AddPage(RPage page, const RKey &key, std::int64_t initialRefCounter)
Add a new page to the fLookupByBuffer and fLookupByKey data structures.
Definition RPagePool.cxx:32
std::unordered_map< ROOT::DescriptorId_t, std::unordered_set< void * > > fUnusedPages
Remembers pages with reference counter 0, organized by the page's cluster id.
RPagePool(const RPagePool &)=delete
std::mutex fLock
The page pool is accessed concurrently due to parallel decompression.
RPageRef GetPage(RKey key, ROOT::NTupleSize_t globalIndex)
Tries to find the page corresponding to column and index in the cache.
RPagePool(RPageSource &pageSource)
Definition RPagePool.cxx:24
ROOT::Experimental::Detail::RNTupleMetrics & GetMetrics()
std::unordered_map< RKey, std::map< RPagePosition, std::size_t >, RKeyHasher > fLookupByKey
Used in GetPage() to find the right page in fEntries.
void AddToUnusedPages(const RPage &page)
Called by PreloadPage() if the page at hand is new and thus added with ref counter 0.
void PreloadPage(RPage page, RKey key)
Like RegisterPage() but the reference counter is initialized to 0.
Definition RPagePool.cxx:64
RPageRef RegisterPage(RPage page, RKey key)
Adds a new page to the pool.
Definition RPagePool.cxx:58
RPagePool & operator=(const RPagePool &)=delete
void RemoveFromUnusedPages(const RPage &page)
Called by GetPage(), when the reference counter increases from zero to one.
RPageSource & fPageSource
Every page pool is associated to exactly one page source.
std::unordered_map< void *, std::size_t > fLookupByBuffer
Used in ReleasePage() to find the page index in fPages.
std::vector< REntry > fEntries
All cached pages in the page pool.
void ReleasePage(const RPage &page)
Give back a page to the pool and decrease the reference counter.
Definition RPagePool.cxx:98
Reference to a page stored in the page pool.
RPageRef(RPageRef &&other)
RPageRef & operator=(const RPageRef &other)=delete
RPageRef & operator=(RPageRef &&other)
RPageRef(const RPage &page, RPagePool *pagePool)
const RPage & Get() const
RPageRef(const RPageRef &other)=delete
Abstract interface to read data from an ntuple.
A page is a slice of a column that is mapped into memory.
Definition RPage.hxx:44
RClusterInfo fClusterInfo
Definition RPage.hxx:76
std::uint32_t fNElements
Definition RPage.hxx:72
std::uint32_t fMaxElements
The capacity of the page in number of elements.
Definition RPage.hxx:74
ROOT::NTupleSize_t fRangeFirst
Definition RPage.hxx:75
std::uint32_t fElementSize
Definition RPage.hxx:71
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
ROOT::NTupleSize_t GetIndexInCluster() const
ROOT::DescriptorId_t GetClusterId() const
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
Performance counters that get registered in fMetrics.
ROOT::Experimental::Detail::RNTupleAtomicCounter & fNPage
Every page in the page pool is annotated with a search key and a reference counter.
Definition RPagePool.hxx:77
Hash function to be used in the unordered map fLookupByKey.
Definition RPagePool.hxx:67
std::size_t operator()(const RKey &k) const
Like boost::hash_combine.
Definition RPagePool.hxx:69
ROOT::DescriptorId_t fColumnId
Definition RPagePool.hxx:54
bool operator!=(const RKey &other) const
Definition RPagePool.hxx:62
bool operator==(const RKey &other) const
Definition RPagePool.hxx:57
Used in fLookupByKey to store both the absolute and the cluster-local page index of the referenced pa...
Definition RPagePool.hxx:86
bool operator<(const RPagePosition &other) const
Definition RPagePool.hxx:90
RPagePosition(ROOT::NTupleSize_t globalIndex)
RPagePosition(RNTupleLocalIndex localIndex)