// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This header file container defines fo gpu* macros which will resolve to // their equivalent hip* or cuda* versions depending on the compiler in use // A separate header (included at the end of this file) will undefine all #include"TensorGpuHipCudaDefines.h"
namespace Eigen {
staticconstint kGpuScratchSize = 1024;
// This defines an interface that GPUDevice can take to use // HIP / CUDA streams underneath. class StreamInterface { public: virtual ~StreamInterface() {}
// Allocate memory on the actual device where the computation will run virtualvoid* allocate(size_t num_bytes) const = 0; virtualvoid deallocate(void* buffer) const = 0;
// Return a scratchpad buffer of size 1k virtualvoid* scratchpad() const = 0;
// Return a semaphore. The semaphore is initially initialized to 0, and // each kernel using it is responsible for resetting to 0 upon completion // to maintain the invariant that the semaphore is always equal to 0 upon // each kernel start. virtualunsignedint* semaphore() const = 0;
};
class GpuDeviceProperties { public:
GpuDeviceProperties() :
initialized_(false), first_(true), device_properties_(nullptr) {}
~GpuDeviceProperties() { if (device_properties_) { delete[] device_properties_;
}
}
void initialize() { if (!initialized_) { // Attempts to ensure proper behavior in the case of multiple threads // calling this function simultaneously. This would be trivial to // implement if we could use std::mutex, but unfortunately mutex don't // compile with nvcc, so we resort to atomics and thread fences instead. // Note that if the caller uses a compiler that doesn't support c++11 we // can't ensure that the initialization is thread safe. if (first_.exchange(false)) { // We're the first thread to reach this point. int num_devices;
gpuError_t status = gpuGetDeviceCount(&num_devices); if (status != gpuSuccess) {
std::cerr << "Failed to get the number of GPU devices: "
<< gpuGetErrorString(status)
<< std::endl;
gpu_assert(status == gpuSuccess);
}
device_properties_ = new gpuDeviceProp_t[num_devices]; for (int i = 0; i < num_devices; ++i) {
status = gpuGetDeviceProperties(&device_properties_[i], i); if (status != gpuSuccess) {
std::cerr << "Failed to initialize GPU device #"
<< i
<< ": "
<< gpuGetErrorString(status)
<< std::endl;
gpu_assert(status == gpuSuccess);
}
}
std::atomic_thread_fence(std::memory_order_release);
initialized_ = true;
} else { // Wait for the other thread to inititialize the properties. while (!initialized_) {
std::atomic_thread_fence(std::memory_order_acquire);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
}
}
class GpuStreamDevice : public StreamInterface { public: // Use the default stream on the current device
GpuStreamDevice() : stream_(&default_stream), scratch_(NULL), semaphore_(NULL) {
gpuGetDevice(&device_);
} // Use the default stream on the specified device
GpuStreamDevice(int device) : stream_(&default_stream), device_(device), scratch_(NULL), semaphore_(NULL) {} // Use the specified stream. Note that it's the // caller responsibility to ensure that the stream can run on // the specified device. If no device is specified the code // assumes that the stream is associated to the current gpu device.
GpuStreamDevice(const gpuStream_t* stream, int device = -1)
: stream_(stream), device_(device), scratch_(NULL), semaphore_(NULL) { if (device < 0) {
gpuGetDevice(&device_);
} else { int num_devices;
gpuError_t err = gpuGetDeviceCount(&num_devices);
EIGEN_UNUSED_VARIABLE(err)
gpu_assert(err == gpuSuccess);
gpu_assert(device < num_devices);
device_ = device;
}
}
virtual ~GpuStreamDevice() { if (scratch_) {
deallocate(scratch_);
}
}
struct GpuDevice { // The StreamInterface is not owned: the caller is // responsible for its initialization and eventual destruction. explicit GpuDevice(const StreamInterface* stream) : stream_(stream), max_blocks_(INT_MAX) {
eigen_assert(stream);
} explicit GpuDevice(const StreamInterface* stream, int num_blocks) : stream_(stream), max_blocks_(num_blocks) {
eigen_assert(stream);
} // TODO(bsteiner): This is an internal API, we should not expose it.
EIGEN_STRONG_INLINE const gpuStream_t& stream() const { return stream_->stream();
}
EIGEN_STRONG_INLINE size_t lastLevelCacheSize() const { // We won't try to take advantage of the l2 cache for the time being, and // there is no l3 cache on hip/cuda devices. return firstLevelCacheSize();
}
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void synchronize() const { #ifndef EIGEN_GPU_COMPILE_PHASE
gpuError_t err = gpuStreamSynchronize(stream_->stream()); if (err != gpuSuccess) {
std::cerr << "Error detected in GPU stream: "
<< gpuGetErrorString(err)
<< std::endl;
gpu_assert(err == gpuSuccess);
} #else
gpu_assert(false && "The default device should be used instead to generate kernel code"); #endif
}
EIGEN_STRONG_INLINE int getNumGpuMultiProcessors() const { return stream_->deviceProperties().multiProcessorCount;
}
EIGEN_STRONG_INLINE int maxGpuThreadsPerBlock() const { return stream_->deviceProperties().maxThreadsPerBlock;
}
EIGEN_STRONG_INLINE int maxGpuThreadsPerMultiProcessor() const { return stream_->deviceProperties().maxThreadsPerMultiProcessor;
}
EIGEN_STRONG_INLINE int sharedMemPerBlock() const { return stream_->deviceProperties().sharedMemPerBlock;
}
EIGEN_STRONG_INLINE int majorDeviceVersion() const { return stream_->deviceProperties().major;
}
EIGEN_STRONG_INLINE int minorDeviceVersion() const { return stream_->deviceProperties().minor;
}
EIGEN_STRONG_INLINE int maxBlocks() const { return max_blocks_;
}
// This function checks if the GPU runtime recorded an error for the // underlying stream device. inlinebool ok() const { #ifdef EIGEN_GPUCC
gpuError_t error = gpuStreamQuery(stream_->stream()); return (error == gpuSuccess) || (error == gpuErrorNotReady); #else returnfalse; #endif
}
private: const StreamInterface* stream_; int max_blocks_;
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.