55void fillBatches(
Batches &batches,
double *output,
size_t nEvents, std::size_t nBatches,
ArgSpan extraArgs)
57 batches.
extra = extraArgs.data();
60 batches.
nExtra = extraArgs.size();
64void fillArrays(std::span<Batch> arrays,
VarSpan vars, std::size_t nEvents)
66 for (std::size_t i = 0; i < vars.size(); i++) {
67 arrays[i]._array = vars[i].data();
68 arrays[i]._isVector = vars[i].empty() || vars[i].size() >= nEvents;
72inline void advance(
Batches &batches, std::size_t nEvents)
74 for (std::size_t i = 0; i < batches.
nBatches; i++) {
85void computeRange(
void (*computeFn)(
Batches &),
double *output, std::size_t totalNEvents,
VarSpan vars,
86 ArgSpan extraArgs, std::size_t begin, std::size_t count)
88 std::vector<Batch> arrays(vars.size());
90 fillBatches(batches, output, count, vars.size(), extraArgs);
91 fillArrays(arrays, vars, totalNEvents);
92 batches.
args = arrays.data();
93 advance(batches, begin);
95 std::size_t events = count;
112constexpr std::size_t parallelChunkSize = 16384;
116constexpr std::size_t minParallelSize = 2 * parallelChunkSize;
118std::size_t numChunks(std::size_t nEvents)
120 return (nEvents + parallelChunkSize - 1) / parallelChunkSize;
131 static std::mutex mutex;
136 static auto &executors = *
new std::map<int, std::unique_ptr<ROOT::TThreadExecutor>>;
137 std::lock_guard<std::mutex> lock{mutex};
138 auto &executor = executors[nThreads];
140 executor = std::make_unique<ROOT::TThreadExecutor>(nThreads);
148void parallelForChunks(
int nThreads, std::size_t nEvents, Func &&func)
156 const unsigned int nTasks = executor.
GetPoolSize() >
static_cast<unsigned int>(nThreads) ? nThreads : 0;
158 executor.
Foreach([&](
unsigned int iChunk) { func(iChunk); }, chunkIndices, nTasks);
162std::size_t chunkBegin(std::size_t iChunk)
164 return iChunk * parallelChunkSize;
168std::size_t chunkSize(std::size_t iChunk, std::size_t nEvents)
170 return std::min(parallelChunkSize, nEvents - chunkBegin(iChunk));
178 return cfg.
nThreads() > 1 && nEvents >= minParallelSize;
205 std::transform(out.begin(), out.end(), out.begin(), [](
unsigned char c) { return std::tolower(c); });
212 std::span<const double> offsetProbas)
override;
236 const std::size_t nEvents = output.size();
240 if (useParallelEvaluation(cfg, nEvents)) {
241 const std::size_t nChunks = numChunks(nEvents);
247 const std::size_t nExtra = extraArgs.size();
248 std::vector<double> extraCopies(nChunks * nExtra);
249 for (std::size_t iChunk = 0; iChunk < nChunks; ++iChunk) {
250 std::copy(extraArgs.begin(), extraArgs.end(), extraCopies.begin() + iChunk * nExtra);
253 parallelForChunks(cfg.
nThreads(), nEvents, [&](std::size_t iChunk) {
254 computeRange(computeFn, output.data(), nEvents, vars, {extraCopies.data() + iChunk * nExtra, nExtra},
255 chunkBegin(iChunk), chunkSize(iChunk, nEvents));
258 for (std::size_t k = 0; k < nExtra; ++k) {
260 for (std::size_t iChunk = 0; iChunk < nChunks; ++iChunk) {
261 delta += extraCopies[iChunk * nExtra + k] - extraArgs[k];
263 extraArgs[k] += delta;
271 computeRange(computeFn, output.data(), nEvents, vars, extraArgs, 0, nEvents);
276inline std::pair<double, double> getLog(
double prob,
ReduceNLLOutput &out)
280 return {std::log(prob), -prob};
283 if (std::isinf(prob)) {
287 if (std::isnan(prob)) {
292 return {std::log(prob), 0.0};
300 if (useParallelEvaluation(cfg,
n)) {
301 const std::size_t nChunks = numChunks(
n);
302 std::vector<ROOT::Math::KahanSum<double, 4u>> partials(nChunks);
303 parallelForChunks(cfg.
nThreads(),
n, [&](std::size_t iChunk) {
304 const std::size_t begin = chunkBegin(iChunk);
306 ROOT::Math::KahanSum<double, 4u>::Accumulate(input + begin, input + begin + chunkSize(iChunk, n));
311 for (
auto const &partial : partials) {
325struct NLLPartialResult {
331void reduceNLLRange(std::span<const double> probas, std::span<const double> weights,
332 std::span<const double> offsetProbas, std::size_t begin, std::size_t end, NLLPartialResult &
result)
334 for (std::size_t i = begin; i < end; ++i) {
336 if (0. == weights[i])
340 double term = logOut.first;
341 result.badness += logOut.second;
343 if (!offsetProbas.empty()) {
344 term -= std::log(offsetProbas[i]);
356 std::span<const double> weights, std::span<const double> offsetProbas)
358 const std::size_t
n = weights.size();
362 if (useParallelEvaluation(cfg,
n)) {
363 const std::size_t nChunks = numChunks(
n);
364 std::vector<NLLPartialResult> partials(nChunks);
365 parallelForChunks(cfg.
nThreads(),
n, [&](std::size_t iChunk) {
366 const std::size_t begin = chunkBegin(iChunk);
367 reduceNLLRange(probas, weights, offsetProbas, begin, begin + chunkSize(iChunk, n), partials[iChunk]);
371 for (
auto const &partial : partials) {
372 result.nllSum += partial.nllSum;
373 result.badness += partial.badness;
374 result.counters.nInfiniteValues += partial.counters.nInfiniteValues;
375 result.counters.nNonPositiveValues += partial.counters.nNonPositiveValues;
376 result.counters.nNaNValues += partial.counters.nNaNValues;
379 reduceNLLRange(probas, weights, offsetProbas, 0,
n,
result);
383 reduceNLLRange(probas, weights, offsetProbas, 0,
n,
result);
390 if (
result.badness != 0.) {
401class ScalarBufferContainer {
403 ScalarBufferContainer() {}
404 ScalarBufferContainer(std::size_t
size)
407 throw std::runtime_error(
"ScalarBufferContainer can only be of size 1");
410 double const *hostReadPtr()
const {
return &
_val; }
411 double const *deviceReadPtr()
const {
return &
_val; }
413 double *hostWritePtr() {
return &
_val; }
414 double *deviceWritePtr() {
return &
_val; }
416 void assignFromHost(std::span<const double>
input) {
_val =
input[0]; }
417 void assignFromDevice(std::span<const double>) {
throw std::bad_function_call(); }
423class CPUBufferContainer {
427 double const *hostReadPtr()
const {
return _vec.data(); }
428 double const *deviceReadPtr()
const
430 throw std::bad_function_call();
434 double *hostWritePtr() {
return _vec.data(); }
435 double *deviceWritePtr()
437 throw std::bad_function_call();
441 void assignFromHost(std::span<const double>
input) {
_vec.assign(
input.begin(),
input.end()); }
442 void assignFromDevice(std::span<const double>) {
throw std::bad_function_call(); }
448template <
class Container>
449class BufferImpl :
public AbsBuffer {
451 using Queue = std::queue<std::unique_ptr<Container>>;
453 BufferImpl(std::size_t
size, Queue &queue) :
_queue{queue}
456 _vec = std::make_unique<Container>(
size);
463 ~BufferImpl()
override {
_queue.emplace(std::move(
_vec)); }
465 double const *hostReadPtr()
const override {
return _vec->hostReadPtr(); }
466 double const *deviceReadPtr()
const override {
return _vec->deviceReadPtr(); }
468 double *hostWritePtr()
override {
return _vec->hostWritePtr(); }
469 double *deviceWritePtr()
override {
return _vec->deviceWritePtr(); }
471 void assignFromHost(std::span<const double>
input)
override {
_vec->assignFromHost(
input); }
472 void assignFromDevice(std::span<const double>
input)
override {
_vec->assignFromDevice(
input); }
474 Container &vec() {
return *
_vec; }
477 std::unique_ptr<Container>
_vec;
481using ScalarBuffer = BufferImpl<ScalarBufferContainer>;
482using CPUBuffer = BufferImpl<CPUBufferContainer>;
484struct BufferQueuesMaps {
489class BufferManager :
public AbsBufferManager {
492 BufferManager() :
_queuesMaps{std::make_unique<BufferQueuesMaps>()} {}
494 std::unique_ptr<AbsBuffer> makeScalarBuffer()
override
496 return std::make_unique<ScalarBuffer>(1,
_queuesMaps->scalarBufferQueuesMap[1]);
498 std::unique_ptr<AbsBuffer> makeCpuBuffer(std::size_t
size)
override
502 std::unique_ptr<AbsBuffer> makeGpuBuffer(std::size_t)
override {
throw std::bad_function_call(); }
503 std::unique_ptr<AbsBuffer> makePinnedBuffer(std::size_t, CudaInterface::CudaStream * =
nullptr)
override
505 throw std::bad_function_call();
514std::unique_ptr<AbsBufferManager> RooBatchComputeClass::createBufferManager()
const
516 return std::make_unique<BufferManager>();