33#include <unordered_map>
43void fillBatches(
Batches &batches,
double *output,
size_t nEvents, std::size_t nBatches, std::size_t nExtraArgs)
47 batches.
nExtra = nExtraArgs;
51void fillArrays(
Batch *arrays,
VarSpan vars,
double *buffer,
double *bufferDevice, std::size_t nEvents)
53 for (
int i = 0; i < vars.size(); i++) {
54 const std::span<const double> &span = vars[i];
55 arrays[i].
_isVector = span.empty() || span.size() >= nEvents;
56 if (!arrays[i]._isVector) {
60 arrays[i].
_array = bufferDevice + i;
66 arrays[i].
_array = span.data();
71int getGridSize(std::size_t
n)
84 constexpr int maxGridSize = 84;
85 return std::min(
int(std::ceil(
double(
n) /
blockSize)), maxGridSize);
106 cudaEvent_t
event =
nullptr;
110 StreamScratch() =
default;
111 StreamScratch(StreamScratch
const &) =
delete;
112 StreamScratch &operator=(StreamScratch
const &) =
delete;
114 Slot &acquire(std::size_t
n)
119 ERRCHECK(cudaEventSynchronize(slot.event));
120 slot.inFlight =
false;
122 if (slot.capacity <
n) {
127 ERRCHECK(cudaFreeHost(slot.host));
131 ERRCHECK(cudaFree(slot.device));
132 slot.device =
nullptr;
135 const std::size_t newCapacity = std::max<std::size_t>(
n, 1024);
136 ERRCHECK(cudaMallocHost(
reinterpret_cast<void **
>(&slot.host), newCapacity));
137 ERRCHECK(cudaMalloc(
reinterpret_cast<void **
>(&slot.device), newCapacity));
138 slot.capacity = newCapacity;
140 if (slot.event ==
nullptr) {
141 ERRCHECK(cudaEventCreateWithFlags(&slot.event, cudaEventDisableTiming));
148 void release(Slot &slot, cudaStream_t stream)
150 ERRCHECK(cudaEventRecord(slot.event, stream));
151 slot.inFlight =
true;
159 struct DeferredSlot {
160 char *
host =
nullptr;
166 DeferredSlot &acquireDeferred(std::size_t
n)
172 if (slot.capacity <
n) {
177 ERRCHECK(cudaFreeHost(slot.host));
181 ERRCHECK(cudaMallocHost(
reinterpret_cast<void **
>(&slot.host),
n));
194 std::memcpy(slot.dst, slot.host, slot.nPending *
sizeof(
double));
205 for (Slot &slot :
_slots) {
207 cudaEventSynchronize(slot.event);
209 cudaEventDestroy(slot.event);
211 cudaFreeHost(slot.host);
213 cudaFree(slot.device);
217 cudaFreeHost(slot.host);
255 using namespace CudaInterface;
257 std::size_t nEvents = output.size();
259 const std::size_t memSize =
sizeof(
Batches) + vars.size() *
sizeof(
Batch) + vars.size() *
sizeof(
double) +
260 extraArgs.size() *
sizeof(
double);
264 StreamScratch::Slot &slot = streamScratch.acquire(memSize);
268 auto batches =
reinterpret_cast<Batches *
>(slot.host);
269 auto arrays =
reinterpret_cast<Batch *
>(batches + 1);
270 auto scalarBuffer =
reinterpret_cast<double *
>(arrays + vars.size());
271 auto extraArgsHost =
reinterpret_cast<double *
>(scalarBuffer + vars.size());
273 auto batchesDevice =
reinterpret_cast<Batches *
>(slot.device);
274 auto arraysDevice =
reinterpret_cast<Batch *
>(batchesDevice + 1);
275 auto scalarBufferDevice =
reinterpret_cast<double *
>(arraysDevice + vars.size());
276 auto extraArgsDevice =
reinterpret_cast<double *
>(scalarBufferDevice + vars.size());
278 fillBatches(*batches, output.data(), nEvents, vars.size(), extraArgs.size());
279 fillArrays(arrays, vars, scalarBuffer, scalarBufferDevice, nEvents);
280 batches->
args = arraysDevice;
282 if (!extraArgs.empty()) {
283 std::copy(std::cbegin(extraArgs), std::cend(extraArgs), extraArgsHost);
284 batches->
extra = extraArgsDevice;
287 copyHostToDevice(slot.host, slot.device, memSize, cfg.
cudaStream());
289 const int gridSize = getGridSize(nEvents);
300 const std::size_t nBytes = extraArgs.size() *
sizeof(
double);
301 StreamScratch::DeferredSlot &deferredSlot = streamScratch.acquireDeferred(nBytes);
302 ERRCHECK(cudaMemcpyAsync(deferredSlot.host, extraArgsDevice, nBytes, cudaMemcpyDeviceToHost, stream));
303 deferredSlot.dst = extraArgs.data();
304 deferredSlot.nPending = extraArgs.size();
307 streamScratch.release(slot, stream);
312 std::span<const double> weights, std::span<const double> offsetProbas)
override;
324 ERRCHECK(::cudaStreamSynchronize(*stream));
329 found->second.flushDeferred();
337 mutable std::unordered_map<CudaInterface::CudaStream *, StreamScratch>
_scratchMap;
344 const double y =
a - (carry + otherCarry);
345 const double t =
sum +
y;
348 carry = (t -
sum) -
y;
358 for (
int i = blockDim.x / 2; i > 0; i >>= 1) {
359 if (threadIdx.x < i && (threadIdx.x + i) <
n) {
360 kahanSumUpdate(shared[threadIdx.x], shared[carry_index], shared[threadIdx.x + i], shared[carry_index + i]);
366 if (threadIdx.x == 0) {
367 result[blockIdx.x] = shared[0];
368 result[blockIdx.x + gridDim.x] = shared[carry_index];
372__global__
void kahanSum(
const double *__restrict__
input,
const double *__restrict__ carries,
size_t n,
373 double *__restrict__
result,
bool nll)
375 int thIdx = threadIdx.x;
376 int gthIdx = thIdx + blockIdx.x *
blockSize;
377 int carry_index = threadIdx.x + blockDim.x;
378 const int nThreadsTotal =
blockSize * gridDim.x;
381 extern __shared__
double shared[];
386 for (
int i = gthIdx; i <
n; i += nThreadsTotal) {
389 double val = nll == 1 ? -std::log(
input[i]) :
input[i];
394 shared[carry_index] = carry;
408__global__
void nllSumKernel(
const double *__restrict__ probas,
const double *__restrict__ weights,
409 const double *__restrict__ offsetProbas,
size_t nProbas,
double scalarProba,
410 size_t nWeights,
double *__restrict__
result,
double *__restrict__ stats)
412 int thIdx = threadIdx.x;
413 int gthIdx = thIdx + blockIdx.x *
blockSize;
414 int carry_index = threadIdx.x + blockDim.x;
415 const int nThreadsTotal =
blockSize * gridDim.x;
418 extern __shared__
double shared[];
423 unsigned int nNonPositive = 0;
424 unsigned int nNaN = 0;
425 unsigned int nInfinite = 0;
427 for (
int i = gthIdx; i < nWeights; i += nThreadsTotal) {
428 const double weight = weights[i];
434 const double proba = nProbas == 1 ? scalarProba : probas[i];
439 term = std::log(proba);
440 }
else if (std::isnan(proba)) {
445 if (std::isinf(proba)) {
448 term = std::log(proba);
451 term -= std::log(offsetProbas[i]);
461 if (nNonPositive != 0)
462 atomicAdd(&stats[1],
double(nNonPositive));
464 atomicAdd(&stats[2],
double(nNaN));
466 atomicAdd(&stats[3],
double(nInfinite));
469 shared[carry_index] = carry;
481 const int gridSize = getGridSize(
n);
484 StreamScratch::Slot &slot = streamScratch.acquire(2 * gridSize *
sizeof(
double));
485 auto devOut =
reinterpret_cast<double *
>(slot.device);
486 auto hostOut =
reinterpret_cast<double *
>(slot.host);
488 kahanSum<<<gridSize, blockSize, shMemSize, stream>>>(
input,
nullptr,
n, devOut, 0);
489 kahanSum<<<1, blockSize, shMemSize, stream>>>(devOut, devOut + gridSize, gridSize, devOut, 0);
493 streamScratch.release(slot, stream);
494 ERRCHECK(cudaStreamSynchronize(stream));
499 std::span<const double> weights, std::span<const double> offsetProbas)
502 if (probas.empty()) {
505 const int gridSize = getGridSize(weights.size());
510 StreamScratch::Slot &slot = streamScratch.acquire((6 + 2 * gridSize) *
sizeof(
double));
511 auto devOut =
reinterpret_cast<double *
>(slot.device);
512 auto hostOut =
reinterpret_cast<double *
>(slot.host);
516 for (
auto span : {probas, weights, offsetProbas}) {
520 cudaPointerAttributes
attr;
521 assert(span.size() <= 1 || span.data() ==
nullptr ||
522 (cudaPointerGetAttributes(&
attr, span.data()) == cudaSuccess &&
attr.type == cudaMemoryTypeDevice));
527 ERRCHECK(cudaMemsetAsync(devOut + 2, 0, 4 *
sizeof(
double), stream));
529 nllSumKernel<<<gridSize, blockSize, shMemSize, stream>>>(
530 probas.data(), weights.data(), offsetProbas.empty() ? nullptr : offsetProbas.data(), probas.size(),
531 probas.size() == 1 ? probas[0] : 0.0, weights.size(), devOut + 6, devOut + 2);
533 kahanSum<<<1, blockSize, shMemSize, stream>>>(devOut + 6, devOut + 6 + gridSize, gridSize, devOut, 0);
540 streamScratch.release(slot, stream);
541 ERRCHECK(cudaStreamSynchronize(stream));
549 if (hostOut[2] != 0.0) {
562class ScalarBufferContainer {
564 ScalarBufferContainer() {}
565 ScalarBufferContainer(std::size_t
size)
568 throw std::runtime_error(
"ScalarBufferContainer can only be of size 1");
571 double const *hostReadPtr()
const {
return &
_val; }
572 double const *deviceReadPtr()
const {
return &
_val; }
574 double *hostWritePtr() {
return &
_val; }
575 double *deviceWritePtr() {
return &
_val; }
577 void assignFromHost(std::span<const double>
input) {
_val =
input[0]; }
578 void assignFromDevice(std::span<const double>
input)
587class CPUBufferContainer {
591 double const *hostReadPtr()
const {
return _vec.data(); }
592 double const *deviceReadPtr()
const
594 throw std::bad_function_call();
598 double *hostWritePtr() {
return _vec.data(); }
599 double *deviceWritePtr()
601 throw std::bad_function_call();
605 void assignFromHost(std::span<const double>
input) {
_vec.assign(
input.begin(),
input.end()); }
606 void assignFromDevice(std::span<const double>
input)
615class GPUBufferContainer {
619 double const *hostReadPtr()
const
621 throw std::bad_function_call();
624 double const *deviceReadPtr()
const {
return _arr.data(); }
626 double *hostWritePtr()
const
628 throw std::bad_function_call();
631 double *deviceWritePtr()
const {
return const_cast<double *
>(
_arr.data()); }
633 void assignFromHost(std::span<const double>
input)
637 void assignFromDevice(std::span<const double>
input)
643 CudaInterface::DeviceArray<double>
_arr;
646class PinnedBufferContainer {
649 std::size_t
size()
const {
return _arr.size(); }
651 void setCudaStream(CudaInterface::CudaStream *stream) {
_cudaStream = stream; }
653 double const *hostReadPtr()
const
656 if (_lastAccess == LastAccessType::GPU_WRITE) {
662 ERRCHECK(cudaStreamSynchronize(*_cudaStream));
667 return const_cast<double *
>(
_arr.data());
669 double const *deviceReadPtr()
const
672 if (_lastAccess == LastAccessType::CPU_WRITE) {
680 double *hostWritePtr()
685 double *deviceWritePtr()
691 void assignFromHost(std::span<const double>
input) { std::copy(
input.begin(),
input.end(), hostWritePtr()); }
692 void assignFromDevice(std::span<const double>
input)
698 enum class LastAccessType {
705 CudaInterface::PinnedHostArray<double>
_arr;
711template <
class Container>
712class BufferImpl :
public AbsBuffer {
714 using Queue = std::queue<std::unique_ptr<Container>>;
716 BufferImpl(std::size_t
size, Queue &queue) :
_queue{queue}
719 _vec = std::make_unique<Container>(
size);
726 ~BufferImpl()
override {
_queue.emplace(std::move(_vec)); }
728 double const *hostReadPtr()
const override {
return _vec->hostReadPtr(); }
729 double const *deviceReadPtr()
const override {
return _vec->deviceReadPtr(); }
731 double *hostWritePtr()
override {
return _vec->hostWritePtr(); }
732 double *deviceWritePtr()
override {
return _vec->deviceWritePtr(); }
734 void assignFromHost(std::span<const double>
input)
override {
_vec->assignFromHost(
input); }
735 void assignFromDevice(std::span<const double>
input)
override {
_vec->assignFromDevice(
input); }
737 Container &vec() {
return *
_vec; }
740 std::unique_ptr<Container>
_vec;
744using ScalarBuffer = BufferImpl<ScalarBufferContainer>;
745using CPUBuffer = BufferImpl<CPUBufferContainer>;
746using GPUBuffer = BufferImpl<GPUBufferContainer>;
747using PinnedBuffer = BufferImpl<PinnedBufferContainer>;
749struct BufferQueuesMaps {
756class BufferManager :
public AbsBufferManager {
759 BufferManager() :
_queuesMaps{std::make_unique<BufferQueuesMaps>()} {}
761 std::unique_ptr<AbsBuffer> makeScalarBuffer()
override
763 return std::make_unique<ScalarBuffer>(1,
_queuesMaps->scalarBufferQueuesMap[1]);
765 std::unique_ptr<AbsBuffer> makeCpuBuffer(std::size_t
size)
override
769 std::unique_ptr<AbsBuffer> makeGpuBuffer(std::size_t
size)
override
773 std::unique_ptr<AbsBuffer> makePinnedBuffer(std::size_t
size, CudaInterface::CudaStream *stream =
nullptr)
override
776 out->vec().setCudaStream(stream);
788 return std::make_unique<BufferManager>();
std::vector< double > _vec
std::array< Slot, 64 > _slots
CudaInterface::CudaStream * _cudaStream
std::map< std::size_t, CPUBuffer::Queue > cpuBufferQueuesMap
std::map< std::size_t, ScalarBuffer::Queue > scalarBufferQueuesMap
std::vector< DeferredSlot > _deferredSlots
CudaInterface::DeviceArray< double > _arr
std::map< std::size_t, PinnedBuffer::Queue > pinnedBufferQueuesMap
LastAccessType _lastAccess
GPUBufferContainer _gpuBuffer
std::unique_ptr< BufferQueuesMaps > _queuesMaps
std::size_t _deferredCursor
std::map< std::size_t, GPUBuffer::Queue > gpuBufferQueuesMap
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t attr
These classes encapsulate the necessary data for the computations.
const double *__restrict _array
double *__restrict output
This class overrides some RooBatchComputeInterface functions, for the purpose of providing a cuda spe...
ReduceNLLOutput reduceNLL(RooBatchCompute::Config const &cfg, std::span< const double > probas, std::span< const double > weights, std::span< const double > offsetProbas) override
std::unordered_map< CudaInterface::CudaStream *, StreamScratch > _scratchMap
const std::vector< void(*)(Batches &)> _computeFunctions
CudaInterface::CudaStream * newCudaStream() const override
std::unique_ptr< AbsBufferManager > createBufferManager() const override
void deleteCudaStream(CudaInterface::CudaStream *stream) const override
void synchronizeCudaStream(CudaInterface::CudaStream *stream) const override
Wait until all work that was enqueued on the stream has completed.
double reduceSum(RooBatchCompute::Config const &cfg, InputArr input, size_t n) override
Return the sum of an input array.
StreamScratch & scratch(CudaInterface::CudaStream *stream)
std::string architectureName() const override
Architecture architecture() const override
void compute(RooBatchCompute::Config const &cfg, Computer computer, std::span< double > output, VarSpan vars, ArgSpan extraArgs) override
Compute multiple values using cuda kernels.
Minimal configuration struct to steer the evaluation of a single node with the RooBatchCompute librar...
CudaInterface::CudaStream * cudaStream() const
The interface which should be implemented to provide optimised computation functions for implementati...
std::vector< void(*)(Batches &)> getFunctions()
Returns a std::vector of pointers to the compute functions in this file.
static RooBatchComputeClass computeObj
Static object to trigger the constructor which overwrites the dispatch pointer.
__global__ void kahanSum(const double *__restrict__ input, const double *__restrict__ carries, size_t n, double *__restrict__ result, bool nll)
__global__ void nllSumKernel(const double *__restrict__ probas, const double *__restrict__ weights, const double *__restrict__ offsetProbas, size_t nProbas, double scalarProba, size_t nWeights, double *__restrict__ result, double *__restrict__ stats)
Computes the negative log likelihood sum with the same semantics as the CPU implementation of RooBatc...
__device__ void kahanSumReduction(double *shared, size_t n, double *__restrict__ result, int carry_index)
__device__ void kahanSumUpdate(double &sum, double &carry, double a, double otherCarry)
void copyDeviceToDevice(const T *src, T *dest, std::size_t n, CudaStream *stream=nullptr)
Copies data from the CUDA device to the CUDA device.
void copyHostToDevice(const T *src, T *dest, std::size_t n, CudaStream *stream=nullptr)
Copies data from the host to the CUDA device.
void copyDeviceToHost(const T *src, T *dest, std::size_t n, CudaStream *stream=nullptr)
Copies data from the CUDA device to the host.
Namespace for dispatching RooFit computations to various backends.
R__EXTERN RooBatchComputeInterface * dispatchCUDA
std::span< double > ArgSpan
const double *__restrict InputArr
std::span< const std::span< const double > > VarSpan
std::size_t nInfiniteValues
std::size_t nNonPositiveValues
static double packFloatIntoNaN(float payload)
Pack float into mantissa of a NaN.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
static uint64_t sum(uint64_t i)