Mantid
Loading...
Searching...
No Matches
BoxController.h
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7#pragma once
8
9#include "MantidAPI/DllConfig.h"
14#include "MantidNexus/NexusFile.h"
15
16#include <numeric>
17#include <optional>
18#include <vector>
19
20namespace Mantid {
21namespace API {
22
32class MANTID_API_DLL BoxController {
33public:
34 //-----------------------------------------------------------------------------------
39 BoxController(size_t nd)
40 : nd(nd), m_maxId(0), m_SplitThreshold(1024), m_splitTopInto(std::nullopt), m_numSplit(1), m_numTopSplit(1),
41 m_fileIO(std::shared_ptr<API::IBoxControllerIO>()) {
42 // TODO: Smarter ways to determine all of these values
43 m_maxDepth = 5;
44 m_numEventsAtMax = 0;
45 m_addingEvents_eventsPerTask = 1000;
46 m_significantEventsNumber = 10000000;
47 m_addingEvents_numTasksPerBlock = Kernel::ThreadPool::getNumPhysicalCores() * 5;
48 m_splitInto.resize(this->nd, 1);
49 resetNumBoxes();
50 }
51
52 virtual ~BoxController();
53 // create new box controller from the existing one
54 virtual BoxController *clone() const;
56 std::string toXMLString() const;
57
59 void fromXMLString(const std::string &xml);
60
62 bool operator==(const BoxController &other) const;
63
64 //-----------------------------------------------------------------------------------
68 size_t getNDims() const { return nd; }
69
70 //-----------------------------------------------------------------------------------
73 size_t getNextId() { return m_maxId++; }
74
75 //-----------------------------------------------------------------------------------
78 size_t getMaxId() const { return m_maxId; }
79
80 //-----------------------------------------------------------------------------------
85 void setMaxId(size_t newMaxId) { m_maxId = newMaxId; }
86
87 //-----------------------------------------------------------------------------------
89 inline std::mutex &getIdMutex() { return m_idMutex; }
90
91 //-----------------------------------------------------------------------------------
98 bool willSplit(size_t numPoints, size_t depth) const {
99 return (numPoints > m_SplitThreshold) && (depth < m_maxDepth);
100 }
101
102 //-----------------------------------------------------------------------------------
104 size_t getSplitThreshold() const { return m_SplitThreshold; }
105
109 void setSplitThreshold(size_t threshold) { m_SplitThreshold = threshold; }
110
111 //-----------------------------------------------------------------------------------
117 size_t getSplitInto(size_t dim) const {
118 // if (dim >= nd)
119 // throw std::invalid_argument("BoxController::setSplitInto() called
120 // with too high of a dimension index.");
121 return m_splitInto[dim];
122 }
123
124 //-----------------------------------------------------------------------------------
130 const std::vector<size_t> &getSplitIntoAll() const { return m_splitInto; }
131 //-----------------------------------------------------------------------------------
136 std::optional<std::vector<size_t>> getSplitTopInto() const {
137 // if (dim >= nd)
138 // throw std::invalid_argument("BoxController::setSplitInto() called
139 // with too high of a dimension index.");
140 return m_splitTopInto;
141 }
142
144 size_t getNumSplit() const { return m_numSplit; }
145
146 //-----------------------------------------------------------------------------------
150 void setSplitInto(size_t num) {
151 m_splitInto.clear();
152 m_splitInto.resize(nd, num);
153 calcNumSplit();
154 }
155
156 //-----------------------------------------------------------------------------------
162 void setSplitInto(size_t dim, size_t num) {
163 if (dim >= nd)
164 throw std::invalid_argument("BoxController::setSplitInto() called with "
165 "too high of a dimension index.");
166 m_splitInto[dim] = num;
167 calcNumSplit();
168 }
169
170 //-----------------------------------------------------------------------------------
176 void setSplitTopInto(size_t dim, size_t num) {
177 if (dim >= nd)
178 throw std::invalid_argument("BoxController::setSplitTopInto() called with "
179 "too high of a dimension index.");
180 // If the vector is not created, then create it
181 if (!m_splitTopInto) {
182 m_splitTopInto = std::vector<size_t>(nd, 1);
183 }
184 m_splitTopInto.value()[dim] = num;
185 calcNumTopSplit();
186 }
187
188 //-----------------------------------------------------------------------------------
193 void setAddingEvents_eventsPerTask(size_t m_addingEvents_eventsPerTask) {
194 this->m_addingEvents_eventsPerTask = m_addingEvents_eventsPerTask;
195 }
197 size_t getAddingEvents_eventsPerTask() const { return m_addingEvents_eventsPerTask; }
198
203 void setAddingEvents_numTasksPerBlock(size_t m_addingEvents_numTasksPerBlock) {
204 this->m_addingEvents_numTasksPerBlock = m_addingEvents_numTasksPerBlock;
205 }
206
208 size_t getAddingEvents_numTasksPerBlock() const { return m_addingEvents_numTasksPerBlock; }
209
210 //-----------------------------------------------------------------------------------
224 void getAddingEventsParameters(size_t &eventsPerTask, size_t &numTasksPerBlock) const {
225 // TODO: Smarter values here depending on nd, etc.
226 eventsPerTask = m_addingEvents_eventsPerTask;
227 numTasksPerBlock = m_addingEvents_numTasksPerBlock;
228 }
229
230 //-----------------------------------------------------------------------------------
232 size_t getMaxDepth() const { return m_maxDepth; }
233
238 void setMaxDepth(size_t value) {
239 m_maxDepth = value;
240 resetNumBoxes();
241 }
242
244 size_t getSignificantEventsNumber() const { return m_significantEventsNumber; }
245
246 //-----------------------------------------------------------------------------------
259 bool shouldSplitBoxes(size_t nEventsInOutput, size_t eventsAdded, size_t numMDBoxes) const {
260 // Avoid divide by zero
261 if (numMDBoxes == 0)
262 return false;
263 // Performance depends pretty strongly on WHEN you split the boxes.
264 // This is an empirically-determined way to optimize the splitting calls.
265 // Split when adding 1/16^th as many events as are already in the output,
266 // (because when the workspace gets very large you should split less often)
267 // But no more often than every 10 million events.
268 const size_t comparisonPoint = std::max(nEventsInOutput / 16, m_significantEventsNumber);
269 if (eventsAdded > comparisonPoint)
270 return true;
271
272 // Return true if the average # of events per box is big enough to split.
273 return ((eventsAdded / numMDBoxes) > m_SplitThreshold);
274 }
275 //-----------------------------------------------------------------------------------
276
277 void clearBoxesCounter(size_t depth) {
278 std::lock_guard<std::mutex> lock(m_mutexNumMDBoxes);
279 m_numMDBoxes[depth] = 0;
280 }
281
282 void clearGridBoxesCounter(size_t depth) {
283 std::lock_guard<std::mutex> lock(m_mutexNumMDBoxes);
284 m_numMDGridBoxes[depth] = 0;
285 }
286 void incGridBoxesCounter(size_t depth, size_t inc = 1) {
287 std::lock_guard<std::mutex> lock(m_mutexNumMDBoxes);
288 m_numMDGridBoxes[depth] += inc;
289 }
290
291 void incBoxesCounter(size_t depth, size_t inc = 1) {
292 std::lock_guard<std::mutex> lock(m_mutexNumMDBoxes);
293 m_numMDBoxes[depth] += inc;
294 }
295
306 void trackNumBoxes(size_t depth) {
307 std::lock_guard<std::mutex> lock(m_mutexNumMDBoxes);
308 if (m_numMDBoxes[depth] > 0) {
309 m_numMDBoxes[depth]--;
310 }
311 m_numMDGridBoxes[depth]++;
312
313 // We need to account for optional top level splitting
314 if (depth == 0 && m_splitTopInto) {
315
316 const auto &splitTopInto = m_splitTopInto.value();
317 size_t numSplitTop =
318 std::accumulate(splitTopInto.cbegin(), splitTopInto.cend(), size_t{1}, std::multiplies<size_t>());
319 m_numMDBoxes[depth + 1] += numSplitTop;
320 } else {
321 m_numMDBoxes[depth + 1] += m_numSplit;
322 }
323 }
324
326 const std::vector<size_t> &getNumMDBoxes() const { return m_numMDBoxes; }
327
330 const std::vector<size_t> &getNumMDGridBoxes() const { return m_numMDGridBoxes; }
331
334 const std::vector<double> &getMaxNumMDBoxes() const { return m_maxNumMDBoxes; }
335
337 size_t getTotalNumMDBoxes() const {
338 return std::accumulate(m_numMDBoxes.cbegin(), m_numMDBoxes.cend(), size_t{0}, std::plus<size_t>());
339 }
340
342 size_t getTotalNumMDGridBoxes() const {
343 return std::accumulate(m_numMDGridBoxes.cbegin(), m_numMDGridBoxes.cend(), size_t{0}, std::plus<size_t>());
344 }
345
348 double getAverageDepth() const {
349 double total = 0;
350 double maxNumberOfFinestBoxes = m_maxNumMDBoxes.back();
351 for (size_t depth = 0; depth < m_numMDBoxes.size(); depth++) {
352 // Add up the number of MDBoxes at that depth, weighed by their volume in
353 // units of the volume of the finest possible box.
354 // I.e. a box at level 1 is 100 x bigger than a box at level 2, so it
355 // counts 100x more.
356 total += double(depth * m_numMDBoxes[depth]) * (maxNumberOfFinestBoxes / m_maxNumMDBoxes[depth]);
357 }
358 return total / maxNumberOfFinestBoxes;
359 }
360
363 std::lock_guard<std::mutex> lock(m_mutexNumMDBoxes);
364 m_numMDBoxes.clear();
365 m_numMDBoxes.resize(m_maxDepth + 1, 0); // Reset to 0
366 m_numMDGridBoxes.resize(m_maxDepth + 1, 0); // Reset to 0
367 m_numMDBoxes[0] = 1; // Start at 1 at depth 0.
368 resetMaxNumBoxes(); // Also the maximums
369 }
370
371 // { return m_useWriteBuffer; }
374 bool isFileBacked() const { return bool(m_fileIO); }
376 IBoxControllerIO *getFileIO() { return m_fileIO.get(); }
379 void setFileBacked(const std::shared_ptr<IBoxControllerIO> &newFileIO, const std::string &fileName = "");
380 void clearFileBacked();
381 //-----------------------------------------------------------------------------------
382 // BoxCtrlChangesInterface *getChangesList(){return m_ChangesList;}
383 // void setChangesList(BoxCtrlChangesInterface *pl){m_ChangesList=pl;}
384 //-----------------------------------------------------------------------------------
385 // increase the counter, calculatinb events at max;
386 void rizeEventAtMax() { ++m_numEventsAtMax; }
389 size_t getNumEventAtMax() const { return m_numEventsAtMax; }
391 size_t claimIDRange(size_t range);
392
395 std::string getFilename() const;
398 bool useWriteBuffer() const;
399
400private:
403 m_numSplit = 1;
404 for (size_t d = 0; d < nd; d++) {
405 m_numSplit *= m_splitInto[d];
406 }
408 resetMaxNumBoxes();
409 }
410
413 m_numTopSplit = 1;
414 for (size_t d = 0; d < nd; d++) {
415 m_numTopSplit *= m_splitTopInto.value()[d];
416 }
418 resetMaxNumBoxes();
419 }
420
423 // Now calculate the max # of boxes
424 m_maxNumMDBoxes.resize(m_maxDepth + 1, 0); // Reset to 0
425 m_maxNumMDBoxes[0] = 1;
426 for (size_t depth = 1; depth < m_maxNumMDBoxes.size(); depth++) {
427 if (depth == 1 && m_splitTopInto) {
428 m_maxNumMDBoxes[depth] = m_maxNumMDBoxes[depth - 1] * double(m_numTopSplit);
429 } else {
430 m_maxNumMDBoxes[depth] = m_maxNumMDBoxes[depth - 1] * double(m_numSplit);
431 }
432 }
433 }
434
435protected:
438 BoxController(const BoxController &other);
440
441private:
443 size_t nd;
444
447 size_t m_maxId;
448
451
455
469 volatile size_t m_numEventsAtMax;
470
472 std::vector<size_t> m_splitInto;
473
475 std::optional<std::vector<size_t>> m_splitTopInto;
476
479
482
485
488
491 std::vector<size_t> m_numMDBoxes;
492
495 std::vector<size_t> m_numMDGridBoxes;
496
499
502 std::vector<double> m_maxNumMDBoxes;
503
505 std::mutex m_idMutex;
506
507 // the class which does actual IO operations, including MRU support list
508 std::shared_ptr<IBoxControllerIO> m_fileIO;
509
511 // size_t m_bytesPerEvent;
512public:
513};
514
516using BoxController_sptr = std::shared_ptr<BoxController>;
517
519using BoxController_const_sptr = std::shared_ptr<const BoxController>;
520
521} // namespace API
522
523} // namespace Mantid
double value
The value of the point.
Definition FitMW.cpp:51
This class is used by MDBox and MDGridBox in order to intelligently determine optimal behavior.
bool shouldSplitBoxes(size_t nEventsInOutput, size_t eventsAdded, size_t numMDBoxes) const
Determine when would be a good time to split MDBoxes into MDGridBoxes.
size_t getSplitInto(size_t dim) const
Return into how many to split along a dimension.
std::optional< std::vector< size_t > > m_splitTopInto
Splittin # for all dimensions in the top level.
void getAddingEventsParameters(size_t &eventsPerTask, size_t &numTasksPerBlock) const
Get parameters for adding events to a MDGridBox, trying to optimize parallel CPU use.
void incGridBoxesCounter(size_t depth, size_t inc=1)
IBoxControllerIO * getFileIO()
returns the pointer to the class, responsible for fileIO operations;
const std::vector< size_t > & getNumMDGridBoxes() const
Return the vector giving the number of MD Grid Boxes as a function of depth.
double getAverageDepth() const
Return the average recursion depth of gridding.
const std::vector< size_t > & getNumMDBoxes() const
Return the vector giving the number of MD Boxes as a function of depth.
std::shared_ptr< IBoxControllerIO > m_fileIO
std::vector< size_t > m_splitInto
Splitting # for all dimensions.
volatile size_t m_numEventsAtMax
number of events sitting in the boxes which should be split but are already split up to the max depth
size_t m_addingEvents_numTasksPerBlock
For adding events tasks.
bool isFileBacked() const
Returns if current box controller is file backed.
void setSplitInto(size_t dim, size_t num)
Set the way splitting will be done.
size_t m_numSplit
When you split a MDBox, it becomes this many sub-boxes.
size_t m_numTopSplit
When you split a top level MDBox by force, it becomes this many sub boxes.
void clearGridBoxesCounter(size_t depth)
void resetNumBoxes()
Reset the number of boxes tracked in m_numMDBoxes.
size_t m_maxDepth
Maximum splitting depth: don't go further than this many levels of recursion.
std::mutex m_mutexNumMDBoxes
Mutex for changing the number of MD Boxes.
size_t getNDims() const
Get # of dimensions.
size_t m_significantEventsNumber
This empirically-determined number of events takes a noticeable time to process and triggers box spli...
size_t getNumEventAtMax() const
return the numner of events, which are sitting at max depth and would be split if not due to the max ...
size_t getNumSplit() const
Return how many boxes (total) a MDGridBox will contain.
size_t getTotalNumMDBoxes() const
Return the total number of MD Boxes, irrespective of depth.
std::vector< size_t > m_numMDGridBoxes
For tracking how many MDGridBoxes (not MDBoxes) are at each recursion level.
void setSplitInto(size_t num)
Set the way splitting will be done.
void setAddingEvents_eventsPerTask(size_t m_addingEvents_eventsPerTask)
When adding events, how many events per task should be done?
size_t nd
Number of dimensions.
std::vector< size_t > m_numMDBoxes
For tracking how many MDBoxes (not MDGridBoxes) are at each recursion level.
void setSplitTopInto(size_t dim, size_t num)
Set the way splitting will be done for the top level.
size_t getAddingEvents_eventsPerTask() const
void setMaxDepth(size_t value)
Sets the max recursion depth allowed for grid box splitting.
size_t getSignificantEventsNumber() const
The number of events that triggers box splitting.
void calcNumTopSplit()
When you split an MDBox by force, it becomes this many sub boxes.
void setSplitThreshold(size_t threshold)
Set the splitting threshold.
void calcNumSplit()
When you split a MDBox, it becomes this many sub-boxes.
void clearBoxesCounter(size_t depth)
size_t m_addingEvents_eventsPerTask
For adding events tasks.
BoxController(size_t nd)
Constructor.
void setMaxId(size_t newMaxId)
Set the new maximum ID number anywhere in the workspace.
size_t m_SplitThreshold
Splitting threshold.
size_t getTotalNumMDGridBoxes() const
Return the total number of MDGridBox'es, irrespective of depth.
BoxController & operator=(const BoxController &)=delete
size_t getSplitThreshold() const
Return the splitting threshold, in # of events.
std::mutex m_idMutex
Mutex for getting IDs.
std::optional< std::vector< size_t > > getSplitTopInto() const
Return into how many to split along a dimension for the top level.
void incBoxesCounter(size_t depth, size_t inc=1)
const std::vector< size_t > & getSplitIntoAll() const
Return into how many to split along a every dimension.
void trackNumBoxes(size_t depth)
Call to track the number of MDBoxes are contained in the MDEventWorkspace This should be called when ...
void resetMaxNumBoxes()
Calculate the vector of the max # of MDBoxes per level.
size_t getAddingEvents_numTasksPerBlock() const
std::vector< double > m_maxNumMDBoxes
This is the maximum number of MD boxes there could be at each recursion level (e.g.
const std::vector< double > & getMaxNumMDBoxes() const
Return the vector giving the MAXIMUM number of MD Boxes as a function of depth.
void setAddingEvents_numTasksPerBlock(size_t m_addingEvents_numTasksPerBlock)
When adding events, how many events tasks per block should be done?
size_t m_maxId
The maximum ID number of any boxes in the workspace (not inclusive, i.e.
bool willSplit(size_t numPoints, size_t depth) const
Return true if the MDBox should split, given :
The header describes interface to IO Operations perfomed by the box controller May be replaced by a b...
std::shared_ptr< const BoxController > BoxController_const_sptr
Shared ptr to a const BoxController.
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
Helper class which provides the Collimation Length for SANS instruments.
STL namespace.
constexpr bool operator==(const wide_integer< Bits, Signed > &lhs, const wide_integer< Bits2, Signed2 > &rhs)