PSCF v1.4.0
DeviceArray.h
1#ifndef PSCF_DEVICE_ARRAY_H
2#define PSCF_DEVICE_ARRAY_H
3
4/*
5* PSCF - Polymer Self-Consistent Field
6*
7* Copyright 2015 - 2025, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include <util/misc/ReferenceCounter.h> // member
12#include <util/misc/CountedReference.h> // member
13
14// Forward declarations
15namespace Util {
16 template <typename Data> class DArray;
17}
18namespace Pscf {
19 class DeviceMemory;
20}
21
22namespace Pscf {
23
24 using namespace Util;
25
94 template <typename Data>
96 {
97
98 public:
99
103 typedef Data ValueType;
104
109
118
125
131 DeviceArray(DArray<Data> const & other);
132
138 virtual ~DeviceArray();
139
148
156
166 void associate(DeviceArray<Data>& arr, int beginId, int capacity);
167
185
196
213
226 virtual
228
243 virtual
245
251 int capacity() const;
252
260 bool isAllocated() const;
261
267 bool isOwner() const;
268
274 bool isAssociated() const;
275
279 Data* cArray();
280
284 Data const * cArray() const;
285
286 protected:
287
289 Data* dataPtr_;
290
293
296
299
300 };
301
302 // Inline member function definitions
303
304 /*
305 * Return array capacity.
306 */
307 template <typename Data> inline
309 { return capacity_; }
310
311 /*
312 * Return true if this object has access to a memory block.
313 */
314 template <typename Data> inline
316 { return (bool) dataPtr_; }
317
318 /*
319 * Does this object own data?
320 */
321 template <typename Data> inline
323 { return ((bool) dataPtr_ && !ref_.isAssociated()); }
324
325 /*
326 * Is this object associated with data it does not own?
327 */
328 template <typename Data> inline
330 { return ((bool) dataPtr_ && ref_.isAssociated()); }
331
332}
333
334#include "DeviceMemory.h"
335#include "cudaErrorCheck.h"
336#include <util/containers/DArray.h>
337#include <util/global.h>
338#include <cuda_runtime.h>
339
340namespace Pscf {
341
342 /*
343 * Default constructor.
344 */
345 template <typename Data>
347 : dataPtr_(nullptr),
348 capacity_(0)
349 {}
350
351 /*
352 * Allocating constructor.
353 */
354 template <typename Data>
359
360 /*
361 * Copy constructor.
362 *
363 * Allocates new memory and copies all elements by value.
364 */
365 template <typename Data>
367 : dataPtr_(nullptr),
368 capacity_(0)
369 {
370 if (!other.isAllocated()) {
371 UTIL_THROW("Other array must be allocated.");
372 }
373
374 allocate(other.capacity_);
375 cudaErrorCheck( cudaMemcpy(dataPtr_, other.cArray(),
376 capacity_ * sizeof(Data),
377 cudaMemcpyDeviceToDevice) );
378 }
379
380 /*
381 * Copy constructor, deep copy from host DArray<Data>
382 *
383 * Allocates new memory and copies all elements by value.
384 */
385 template <typename Data>
387 : dataPtr_(nullptr),
388 capacity_(0)
389 {
390 if (!other.isAllocated()) {
391 UTIL_THROW("Other array must be allocated.");
392 }
393
394 allocate(other.capacity());
395 cudaErrorCheck( cudaMemcpy(dataPtr_, other.cArray(),
396 capacity_ * sizeof(Data),
397 cudaMemcpyHostToDevice) );
398 }
399
400 /*
401 * Destructor.
402 */
403 template <typename Data>
405 {
406 if (isOwner()) {
407 if (refCounter_.hasRefs()) {
408 std::cout
409 << "Error: Destroying DeviceArray that is referred"
410 << "to by at least one CountedReference"
411 << std::endl;
412 }
413 cudaFree(dataPtr_);
414 }
415 if (ref_.isAssociated()) {
416 ref_.dissociate();
417 }
418 }
419
420 /*
421 * Allocate device memory, with this object then owns.
422 */
423 template <typename Data>
425 {
426 if (capacity <= 0) {
427 UTIL_THROW("Attempt to allocate with capacity <= 0");
428 }
429 if (isAllocated()) {
430 UTIL_THROW("Attempt to re-allocate an array");
431 }
432 if (ref_.isAssociated()) {
433 UTIL_THROW("Attempt to allocate array that uses external data");
434 }
435
436 cudaErrorCheck(cudaMalloc((void**) &dataPtr_,
437 capacity * sizeof(Data)));
439 }
440
441 /*
442 * Deallocate device memory owned by this object, if any.
443 */
444 template <typename Data>
446 {
448 UTIL_CHECK(!ref_.isAssociated());
449 UTIL_CHECK(!refCounter_.hasRefs());
450 cudaErrorCheck( cudaFree(dataPtr_) );
451 dataPtr_ = nullptr;
452 capacity_ = 0;
453 }
454
455 /*
456 * Associate this object with memory owned by a different DeviceArray.
457 */
458 template <typename Data>
460 int beginId, int capacity)
461 {
462 UTIL_CHECK(arr.isAllocated());
463 UTIL_CHECK(arr.isOwner());
464 UTIL_CHECK(beginId >= 0);
465 UTIL_CHECK(capacity > 0);
466 UTIL_CHECK(beginId + capacity <= arr.capacity());
468 UTIL_CHECK(!ref_.isAssociated());
469
470 // Copy data pointer and capacity
471 dataPtr_ = arr.cArray() + beginId;
473
474 // Associate ref_ member with reference counter of data owner
475 arr.addReference(ref_);
476 }
477
478 /*
479 * Associate this object with memory owned by a DeviceMemory object.
480 */
481 template <typename Data>
483 {
484 UTIL_CHECK(arr.isAllocated());
485 UTIL_CHECK(capacity > 0);
486 UTIL_CHECK(capacity * sizeof(Data) <= arr.capacity());
488 UTIL_CHECK(!ref_.isAssociated());
489
490 // Copy data pointer and capacity
491 dataPtr_ = (Data *) arr.cArray();
493
494 // Associate ref_ member with reference counter of data owner
495 arr.addReference(ref_);
496 }
497
498 /*
499 * Dissociate this object from external device memory
500 */
501 template <typename Data>
503 {
505 UTIL_CHECK(ref_.isAssociated());
506
507 dataPtr_ = nullptr; // reset to null
508 capacity_ = 0;
509 ref_.dissociate();
510 }
511
512 /*
513 * Associate an external CountedReference with the ReferenceCounter.
514 *
515 * This function should called to create an association in which
516 * this object is the data owner. The CountedReference parameter
517 * should be owned by the data user.
518 */
519 template <typename Data>
522
523 /*
524 * Assignment from another DeviceArray<Data>.
525 */
526 template <typename Data>
529 {
530 // Check for self assignment
531 if (this == &other) return *this;
532
533 // Precondition - RHS DeviceArray<Data> must be allocated
534 if (!other.isAllocated()) {
535 UTIL_THROW("Other DeviceArray<Data> must be allocated.");
536 }
537
538 // If this is not allocated, then allocate
539 if (!isAllocated()) {
540 allocate(other.capacity());
541 }
542
543 // Require equal capacity values
544 if (capacity_ != other.capacity_) {
545 UTIL_THROW("Cannot assign arrays of unequal capacity");
546 }
547
548 // Copy elements
549 cudaErrorCheck( cudaMemcpy(dataPtr_, other.cArray(),
550 capacity_ * sizeof(Data),
551 cudaMemcpyDeviceToDevice) );
552
553 return *this;
554 }
555
556 /*
557 * Assignment from RHS Util::DArray<Data>.
558 */
559 template <typename Data>
562 {
563 // Precondition
564 if (!other.isAllocated()) {
565 UTIL_THROW("RHS DArray<Data> must be allocated.");
566 }
567
568 // Allocate this if necessary
569 if (!isAllocated()) {
570 allocate(other.capacity());
571 }
572
573 // Require equal capacity values
574 if (capacity_ != other.capacity()) {
575 UTIL_THROW("Cannot assign arrays of unequal capacity");
576 }
577
578 // Copy elements
579 cudaErrorCheck( cudaMemcpy(dataPtr_, other.cArray(),
580 capacity_ * sizeof(Data),
581 cudaMemcpyHostToDevice) );
582
583 return *this;
584 }
585
586 /*
587 * Get a pointer to the underlying C array.
588 */
589 template <typename Data>
591 {
593 return dataPtr_;
594 }
595
596 /*
597 * Get a pointer to const to the underlying C array.
598 */
599 template <typename Data>
600 const Data* DeviceArray<Data>::cArray() const
601 {
603 return dataPtr_;
604 }
605
606} // namespace Pscf
607#endif
Dynamic array on the GPU device with aligned data.
Definition DeviceArray.h:96
bool isAssociated() const
Is this container associated with a memory block it does not own?
DeviceArray()
Default constructor.
bool isOwner() const
Does this container own an allocated memory block?
void addReference(CountedReference &reference)
Associate a reference with reference counter for this container.
void associate(DeviceMemory &arr, int capacity)
Associate this object with a DeviceMemory container.
void deallocate()
Dellocate the underlying C array.
DeviceArray(int capacity)
Allocating constructor.
void dissociate()
Dissociate this object from an externally owned memory block.
virtual ~DeviceArray()
Destructor.
virtual DeviceArray< Data > & operator=(const DeviceArray< Data > &other)
Assignment operator, assign from another DeviceArray<Data> array.
bool isAllocated() const
Return true if the array has allocated data, false otherwise.
DeviceArray(DArray< Data > const &other)
Copy constructor, deep copy DArray<Data> from host to device.
void associate(DeviceArray< Data > &arr, int beginId, int capacity)
Associate this object with a slice of a different DeviceArray.
DeviceArray(DeviceArray< Data > const &other)
Copy constructor.
void allocate(int capacity)
Allocate the underlying C array on the device.
Data const * cArray() const
Return const pointer to underlying C array.
Data * cArray()
Return pointer to underlying C array.
Data ValueType
Data type of each element.
Block of bare memory allocated on the GPU device.
void addReference(CountedReference &reference)
Associate a reference with the reference counter.
bool isAllocated() const
Return true if the array has been allocated, false otherwise.
void * cArray() const
Return pointer to underlying C array.
int capacity() const
Return allocated capacity.
Data * cArray()
Return a pointer to the underlying C array.
Definition Array.h:199
int capacity() const
Return allocated size.
Definition Array.h:144
Reference to a shared resource.
void associate(ReferenceCounter &counter)
Create an association with a ReferenceCounter.
Dynamically allocatable contiguous array template.
Definition DArray.h:32
bool isAllocated() const
Return true if this DArray has been allocated, false otherwise.
Definition DArray.h:151
Reference counter.
File containing preprocessor macros for error handling.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition global.h:49
PSCF package top-level namespace.
Utility classes for scientific computation.