SWDEV-240806 - Initial commit for hipGraph and stream capture infrastructure

On StreamBegincapture captures the parameters passed to APIs and respective node will be created and added to graph
All parameters are passed to STREAM_CAPTURE macro, it checks if stream in capture mode and redirects the call to the capture function and returns
Updated hipStream and hipEvent with capture parameters
Added handling for hipStreamBeginCapture & hipStreamEndCapture

Change-Id: Ic8926a7b4336c2cc81f0b3a9a224aa392c474134


[ROCm/clr commit: 8cc0e04239]
This commit is contained in:
Anusha Godavarthy Surya
2021-03-02 16:23:47 -05:00
committed by Christophe Paquot
parent abc42e8c5c
commit 1fe1fe9361
17 changed files with 1836 additions and 15 deletions
@@ -3886,6 +3886,130 @@ const char* hipKernelNameRef(const hipFunction_t f);
const char* hipKernelNameRefByPtr(const void* hostFunction, hipStream_t stream);
int hipGetStreamDeviceId(hipStream_t stream);
#ifdef __cplusplus
/**
* An opaque value that represents a hip graph
*/
class hipGraph;
typedef hipGraph* hipGraph_t;
/**
* An opaque value that represents a hip graph node
*/
class hipGraphNode;
typedef hipGraphNode* hipGraphNode_t;
/**
* An opaque value that represents a hip graph Exec
*/
class hipGraphExec;
typedef hipGraphExec* hipGraphExec_t;
typedef enum hipGraphNodeType {
hipGraphNodeTypeKernel = 1, ///< GPU kernel node
hipGraphNodeTypeMemcpy = 2, ///< Memcpy 3D node
hipGraphNodeTypeMemset = 3, ///< Memset 1D node
hipGraphNodeTypeHost = 4, ///< Host (executable) node
hipGraphNodeTypeGraph = 5, ///< Node which executes an embedded graph
hipGraphNodeTypeEmpty = 6, ///< Empty (no-op) node
hipGraphNodeTypeWaitEvent = 7, ///< External event wait node
hipGraphNodeTypeEventRecord = 8, ///< External event record node
hipGraphNodeTypeMemcpy1D = 9, ///< Memcpy 1D node
hipGraphNodeTypeMemcpyFromSymbol = 10, ///< MemcpyFromSymbol node
hipGraphNodeTypeMemcpyToSymbol = 11, ///< MemcpyToSymbol node
hipGraphNodeTypeCount
} hipGraphNodeType;
typedef void (*hipHostFn_t)(void* userData);
typedef struct hipHostNodeParams {
hipHostFn_t fn;
void* userData;
} hipHostNodeParams;
typedef struct hipKernelNodeParams {
dim3 blockDim;
void** extra;
void* func;
dim3 gridDim;
void** kernelParams;
unsigned int sharedMemBytes;
} hipKernelNodeParams;
typedef struct hipMemsetParams {
void* dst;
unsigned int elementSize;
size_t height;
size_t pitch;
unsigned int value;
size_t width;
} hipMemsetParams;
enum hipGraphExecUpdateResult {
hipGraphExecUpdateSuccess = 0x0, ///< The update succeeded
hipGraphExecUpdateError = 0x1, ///< The update failed for an unexpected reason which is described
///< in the return value of the function
hipGraphExecUpdateErrorTopologyChanged = 0x2, ///< The update failed because the topology changed
hipGraphExecUpdateErrorNodeTypeChanged = 0x3, ///< The update failed because a node type changed
hipGraphExecUpdateErrorFunctionChanged =
0x4, ///< The update failed because the function of a kernel node changed
hipGraphExecUpdateErrorParametersChanged =
0x5, ///< The update failed because the parameters changed in a way that is not supported
hipGraphExecUpdateErrorNotSupported =
0x6, ///< The update failed because something about the node is not supported
hipGraphExecUpdateErrorUnsupportedFunctionChange = 0x7
};
enum hipStreamCaptureMode {
hipStreamCaptureModeGlobal = 0,
hipStreamCaptureModeThreadLocal,
hipStreamCaptureModeRelaxed
};
enum hipStreamCaptureStatus {
hipStreamCaptureStatusNone = 0, ///< Stream is not capturing
hipStreamCaptureStatusActive, ///< Stream is actively capturing
hipStreamCaptureStatusInvalidated ///< Stream is part of a capture sequence that has been
///< invalidated, but not terminated
};
hipError_t hipStreamBeginCapture(hipStream_t stream, hipStreamCaptureMode mode);
hipError_t hipStreamEndCapture(hipStream_t stream, hipGraph_t* pGraph);
// Creates a graph.
hipError_t hipGraphCreate(hipGraph_t* pGraph, unsigned int flags);
// Destroys a graph.
hipError_t hipGraphDestroy(hipGraph_t graph);
// Destroys an executable graph.
hipError_t hipGraphExecDestroy(hipGraphExec_t pGraphExec);
// Creates an executable graph from a graph.
hipError_t hipGraphInstantiate(hipGraphExec_t* pGraphExec, hipGraph_t graph,
hipGraphNode_t* pErrorNode, char* pLogBuffer, size_t bufferSize);
// Launches an executable graph in a stream.
hipError_t hipGraphLaunch(hipGraphExec_t graphExec, hipStream_t stream);
// Creates a kernel execution node and adds it to a graph.
hipError_t hipGraphAddKernelNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipKernelNodeParams* pNodeParams);
// Creates a memcpy node and adds it to a graph.
hipError_t hipGraphAddMemcpyNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipMemcpy3DParms* pCopyParams);
// Creates a memset node and adds it to a graph.
hipError_t hipGraphAddMemsetNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipMemsetParams* pMemsetParams);
#endif
// doxygen end graph API
/**
* @}
*/
#ifdef __cplusplus
} /* extern "c" */
#endif
@@ -4085,6 +4209,11 @@ static inline hipError_t hipUnbindTexture(
return hipUnbindTexture(&tex);
}
// doxygen end Texture
/**
* @}
*/
#endif // __cplusplus
#ifdef __GNUC__
@@ -280,6 +280,28 @@ typedef enum __HIP_NODISCARD hipError_t {
///< that was launched via cooperative launch APIs exceeds the maximum number of
///< allowed blocks for the current device
hipErrorNotSupported = 801, ///< Produced when the hip API is not supported/implemented
hipErrorStreamCaptureUnsupported = 900, ///< The operation is not permitted when the stream
///< is capturing.
hipErrorStreamCaptureInvalidated = 901, ///< The current capture sequence on the stream
///< has been invalidated due to a previous error.
hipErrorStreamCaptureMerge = 902, ///< The operation would have resulted in a merge of
///< two independent capture sequences.
hipErrorStreamCaptureUnmatched = 903, ///< The capture was not initiated in this stream.
hipErrorStreamCaptureUnjoined = 904, ///< The capture sequence contains a fork that was not
///< joined to the primary stream.
hipErrorStreamCaptureIsolation = 905, ///< A dependency would have been created which crosses
///< the capture sequence boundary. Only implicit
///< in-stream ordering dependencies are allowed
///< to cross the boundary
hipErrorStreamCaptureImplicit = 906, ///< The operation would have resulted in a disallowed
///< implicit dependency on a current capture sequence
///< from hipStreamLegacy.
hipErrorCapturedEvent = 907, ///< The operation is not permitted on an event which was last
///< recorded in a capturing stream.
hipErrorStreamCaptureWrongThread = 908, ///< A stream capture sequence not initiated with
///< the hipStreamCaptureModeRelaxed argument to
///< hipStreamBeginCapture was passed to
///< hipStreamEndCapture in a different thread.
hipErrorUnknown = 999, //< Unknown error.
// HSA Runtime Error Codes start here.
hipErrorRuntimeMemory = 1052, ///< HSA runtime memory call returned error. Typically not seen
@@ -142,6 +142,8 @@ add_library(hip64 OBJECT
cl_gl.cpp
cl_lqdflash_amd.cpp
fixme.cpp
hip_graph.cpp
hip_graph_internal.cpp
)
set_target_properties(hip64 PROPERTIES POSITION_INDEPENDENT_CODE ON)
+2
View File
@@ -321,6 +321,8 @@ bool createIpcEventShmemIfNeeded(hip::Event::ihipIpcEvent_t& ipc_evt) {
hipError_t hipEventRecord(hipEvent_t event, hipStream_t stream) {
HIP_INIT_API(hipEventRecord, event, stream);
STREAM_CAPTURE(hipEventRecord, stream, event);
if (event == nullptr) {
HIP_RETURN(hipErrorInvalidHandle);
}
+33 -2
View File
@@ -64,10 +64,18 @@ typedef struct ihipIpcEventShmem_s {
} ihipIpcEventShmem_t;
class Event {
public:
/// event recorded on stream where capture is active
bool onCapture_;
/// capture stream where event is recorded
hipStream_t captureStream_;
/// Previous captured nodes before event record
std::vector<hipGraphNode_t> nodesPrevToRecorded_;
public:
Event(unsigned int flags) : flags(flags), lock_("hipEvent_t", true),
event_(nullptr), recorded_(false) {
// No need to init event_ here as addMarker does that
onCapture_ = false;
device_id_ = hip::getCurrentDevice()->deviceId(); // Created in current device ctx
}
@@ -89,7 +97,30 @@ public:
const int deviceId() { return device_id_; }
void setDeviceId(int id) { device_id_ = id; }
//IPC Events
/// End capture on this event
void EndCapture() {
onCapture_ = false;
captureStream_ = nullptr;
}
/// Start capture when waited on this event
void StartCapture(hipStream_t stream) {
onCapture_ = true;
captureStream_ = stream;
}
/// Get capture status of the graph
bool GetCaptureStatus() { return onCapture_; }
/// Get capture stream where event is recorded
hipStream_t GetCaptureStream() { return captureStream_; }
/// Set capture stream where event is recorded
void SetCaptureStream(hipStream_t stream) { captureStream_ = stream; }
/// Returns previous captured nodes before event record
std::vector<hipGraphNode_t> GetNodesPrevToRecorded() const { return nodesPrevToRecorded_; }
/// Set last captured graph node before event record
void SetNodesPrevToRecorded(std::vector<hipGraphNode_t>& graphNode) {
nodesPrevToRecorded_ = graphNode;
}
// IPC Events
struct ihipIpcEvent_t {
std::string ipc_name_;
int ipc_fd_;
+415
View File
@@ -0,0 +1,415 @@
/* Copyright (c) 2021-present Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "hip_graph_internal.hpp"
#include "platform/command.hpp"
#include "hip_conversions.hpp"
#include "hip_platform.hpp"
#include "hip_event.hpp"
thread_local std::vector<hipStream_t> g_captureStreams;
std::unordered_map<amd::Command*, hipGraphExec_t> hipGraphExec::activeGraphExec_;
hipError_t ihipGraphAddKernelNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipKernelNodeParams* pNodeParams) {
if (pGraphNode == nullptr || graph == nullptr ||
(numDependencies > 0 && pDependencies == nullptr) || pNodeParams == nullptr) {
return hipErrorInvalidValue;
}
hipFunction_t func = nullptr;
hipError_t status =
PlatformState::instance().getStatFunc(&func, pNodeParams->func, ihipGetDevice());
if ((status != hipSuccess) || (func == nullptr)) {
return hipErrorInvalidDeviceFunction;
}
size_t globalWorkSizeX = static_cast<size_t>(pNodeParams->gridDim.x) * pNodeParams->blockDim.x;
size_t globalWorkSizeY = static_cast<size_t>(pNodeParams->gridDim.y) * pNodeParams->blockDim.y;
size_t globalWorkSizeZ = static_cast<size_t>(pNodeParams->gridDim.z) * pNodeParams->blockDim.z;
if (globalWorkSizeX > std::numeric_limits<uint32_t>::max() ||
globalWorkSizeY > std::numeric_limits<uint32_t>::max() ||
globalWorkSizeZ > std::numeric_limits<uint32_t>::max()) {
return hipErrorInvalidConfiguration;
}
status = ihipLaunchKernel_validate(
func, static_cast<uint32_t>(globalWorkSizeX), static_cast<uint32_t>(globalWorkSizeY),
static_cast<uint32_t>(globalWorkSizeZ), pNodeParams->blockDim.x, pNodeParams->blockDim.y,
pNodeParams->blockDim.z, pNodeParams->sharedMemBytes, pNodeParams->kernelParams,
pNodeParams->extra, ihipGetDevice(), 0);
if (status != hipSuccess) {
return status;
}
*pGraphNode = new hipGraphKernelNode(pNodeParams, func);
if (numDependencies == 0) {
graph->AddNode(*pGraphNode);
}
for (size_t i = 0; i < numDependencies; i++) {
if (graph->AddEdge(*(pDependencies + i), *pGraphNode) != hipSuccess) {
return hipErrorInvalidValue;
}
}
return hipSuccess;
}
hipError_t ihipGraphAddMemcpyNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipMemcpy3DParms* pCopyParams) {
if (pGraphNode == nullptr || graph == nullptr ||
(numDependencies > 0 && pDependencies == nullptr) || pCopyParams == nullptr) {
return hipErrorInvalidValue;
}
ihipMemcpy3D_validate(pCopyParams);
*pGraphNode = new hipGraphMemcpyNode(pCopyParams);
if (numDependencies == 0) {
graph->AddNode(*pGraphNode);
}
for (size_t i = 0; i < numDependencies; i++) {
if (graph->AddEdge(*(pDependencies + i), *pGraphNode) != hipSuccess) {
return hipErrorInvalidValue;
}
}
return hipSuccess;
}
hipError_t ihipGraphAddMemsetNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipMemsetParams* pMemsetParams) {
if (pGraphNode == nullptr || graph == nullptr ||
(numDependencies > 0 && pDependencies == nullptr) || pMemsetParams == nullptr) {
return hipErrorInvalidValue;
}
if (pMemsetParams->height == 1) {
ihipMemset_validate(pMemsetParams->dst, pMemsetParams->value, pMemsetParams->elementSize,
pMemsetParams->width * pMemsetParams->elementSize);
} else {
auto sizeBytes = pMemsetParams->width * pMemsetParams->height * 1;
ihipMemset3D_validate(
{pMemsetParams->dst, pMemsetParams->pitch, pMemsetParams->width, pMemsetParams->height},
pMemsetParams->value, {pMemsetParams->width, pMemsetParams->height, 1}, sizeBytes);
}
*pGraphNode = new hipGraphMemsetNode(pMemsetParams);
if (numDependencies == 0) {
graph->AddNode(*pGraphNode);
}
for (size_t i = 0; i < numDependencies; i++) {
if (graph->AddEdge(*(pDependencies + i), *pGraphNode) != hipSuccess) {
return hipErrorInvalidValue;
}
}
return hipSuccess;
}
hipError_t capturehipLaunchKernel(hipStream_t& stream, const void*& hostFunction, dim3& gridDim,
dim3& blockDim, void**& args, size_t& sharedMemBytes) {
ClPrint(amd::LOG_INFO, amd::LOG_API,
"[hipGraph] current capture node kernel launch on stream : %p", stream);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
hipKernelNodeParams nodeParams;
nodeParams.func = const_cast<void*>(hostFunction);
nodeParams.blockDim = blockDim;
nodeParams.extra = nullptr;
nodeParams.gridDim = gridDim;
nodeParams.kernelParams = args;
nodeParams.sharedMemBytes = sharedMemBytes;
hipGraphNode_t pGraphNode;
hipError_t status =
ihipGraphAddKernelNode(&pGraphNode, s->GetCaptureGraph(), s->GetLastCapturedNodes().data(),
s->GetLastCapturedNodes().size(), &nodeParams);
if (status != hipSuccess) {
return status;
}
s->SetLastCapturedNode(pGraphNode);
return hipSuccess;
}
hipError_t capturehipMemcpy3DAsync(hipStream_t& stream, const hipMemcpy3DParms*& p) {
ClPrint(amd::LOG_INFO, amd::LOG_API, "[hipGraph] current capture node Memcpy3D on stream : %p",
stream);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
hipGraphNode_t pGraphNode;
hipError_t status =
ihipGraphAddMemcpyNode(&pGraphNode, s->GetCaptureGraph(), s->GetLastCapturedNodes().data(),
s->GetLastCapturedNodes().size(), p);
if (status != hipSuccess) {
return status;
}
s->SetLastCapturedNode(pGraphNode);
return hipSuccess;
}
hipError_t capturehipMemcpyAsync(hipStream_t& stream, void*& dst, const void*& src,
size_t& sizeBytes, hipMemcpyKind& kind) {
ClPrint(amd::LOG_INFO, amd::LOG_API, "[hipGraph] current capture node Memcpy1D on stream : %p",
stream);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
hipGraphNode_t pGraphNode;
hipGraph_t graph = nullptr;
std::vector<hipGraphNode_t> pDependencies = s->GetLastCapturedNodes();
size_t numDependencies = s->GetLastCapturedNodes().size();
graph = s->GetCaptureGraph();
ihipMemcpy_validate(dst, src, sizeBytes, kind);
pGraphNode = new hipGraphMemcpyNode1D(dst, src, sizeBytes, kind);
if (numDependencies == 0) {
graph->AddNode(pGraphNode);
}
for (size_t i = 0; i < numDependencies; i++) {
if (graph->AddEdge(pDependencies[i], pGraphNode) != hipSuccess) {
return hipErrorInvalidValue;
}
}
s->SetLastCapturedNode(pGraphNode);
return hipSuccess;
}
hipError_t capturehipMemcpyFromSymbolAsync(hipStream_t& stream, void*& dst, const void*& symbol,
size_t& sizeBytes, size_t& offset, hipMemcpyKind& kind) {
ClPrint(amd::LOG_INFO, amd::LOG_API,
"[hipGraph] current capture node MemcpyFromSymbolNode on stream : %p", stream);
return hipSuccess;
}
hipError_t capturehipMemcpyToSymbolAsync(hipStream_t& stream, const void*& symbol, const void*& src,
size_t& sizeBytes, size_t& offset, hipMemcpyKind& kind) {
ClPrint(amd::LOG_INFO, amd::LOG_API,
"[hipGraph] current capture node MemcpyToSymbolNode on stream : %p", stream);
return hipSuccess;
}
hipError_t capturehipMemsetAsync(hipStream_t& stream, void*& dst, int& value, size_t& valueSize,
size_t& sizeBytes) {
ClPrint(amd::LOG_INFO, amd::LOG_API, "[hipGraph] current capture node Memset1D on stream : %p",
stream);
hipMemsetParams memsetParams = {0};
memsetParams.dst = dst;
memsetParams.value = value;
memsetParams.elementSize = valueSize;
memsetParams.width = sizeBytes / valueSize;
memsetParams.height = 1;
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
hipGraphNode_t pGraphNode;
hipError_t status =
ihipGraphAddMemsetNode(&pGraphNode, s->GetCaptureGraph(), s->GetLastCapturedNodes().data(),
s->GetLastCapturedNodes().size(), &memsetParams);
if (status != hipSuccess) {
return status;
}
s->SetLastCapturedNode(pGraphNode);
return hipSuccess;
}
hipError_t capturehipMemset2DAsync(hipStream_t& stream, void*& dst, size_t& pitch, int& value,
size_t& width, size_t& height) {
ClPrint(amd::LOG_INFO, amd::LOG_API, "[hipGraph] current capture node Memset2D on stream : %p",
stream);
hipMemsetParams memsetParams = {0};
memsetParams.dst = dst;
memsetParams.value = value;
memsetParams.width = width;
memsetParams.height = height;
memsetParams.pitch = pitch;
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
hipGraphNode_t pGraphNode;
hipError_t status =
ihipGraphAddMemsetNode(&pGraphNode, s->GetCaptureGraph(), s->GetLastCapturedNodes().data(),
s->GetLastCapturedNodes().size(), &memsetParams);
if (status != hipSuccess) {
return status;
}
s->SetLastCapturedNode(pGraphNode);
return hipSuccess;
}
hipError_t capturehipMemset3DAsync(hipStream_t& stream, hipPitchedPtr& pitchedDevPtr, int& value,
hipExtent& extent) {
ClPrint(amd::LOG_INFO, amd::LOG_API, "[hipGraph] current capture node Memset3D on stream : %p",
stream);
return hipSuccess;
}
hipError_t capturehipEventRecord(hipStream_t& stream, hipEvent_t& event) {
ClPrint(amd::LOG_INFO, amd::LOG_API,
"[hipGraph] current capture node EventRecord on stream : %p, Event %p", stream, event);
if (event == nullptr) {
HIP_RETURN(hipErrorInvalidHandle);
}
hip::Event* e = reinterpret_cast<hip::Event*>(event);
e->StartCapture(stream);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
std::vector<hipGraphNode_t> lastCapturedNodes = s->GetLastCapturedNodes();
if (!lastCapturedNodes.empty()) {
e->SetNodesPrevToRecorded(lastCapturedNodes);
}
return hipSuccess;
}
hipError_t capturehipStreamWaitEvent(hipEvent_t& event, hipStream_t& stream, unsigned int& flags) {
ClPrint(amd::LOG_INFO, amd::LOG_API,
"[hipGraph] current capture node StreamWaitEvent on stream : %p, Event %p", stream,
event);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
hip::Event* e = reinterpret_cast<hip::Event*>(event);
if (event == nullptr || stream == nullptr) {
HIP_RETURN(hipErrorInvalidValue);
}
if (!s->IsOriginStream()) {
s->SetCaptureGraph(reinterpret_cast<hip::Stream*>(e->GetCaptureStream())->GetCaptureGraph());
s->SetCaptureMode(reinterpret_cast<hip::Stream*>(e->GetCaptureStream())->GetCaptureMode());
s->SetParentStream(e->GetCaptureStream());
}
s->AddCrossCapturedNode(e->GetNodesPrevToRecorded());
g_captureStreams.push_back(stream);
return hipSuccess;
}
hipError_t hipStreamIsCapturing(hipStream_t stream, hipStreamCaptureStatus** pCaptureStatus) {
HIP_INIT_API(hipStreamIsCapturing, stream, pCaptureStatus);
if (stream == nullptr) {
HIP_RETURN(hipErrorInvalidValue);
}
hipStreamCaptureStatus captureStatus = reinterpret_cast<hip::Stream*>(stream)->GetCaptureStatus();
*pCaptureStatus = &captureStatus;
HIP_RETURN(hipSuccess);
}
hipError_t hipStreamBeginCapture(hipStream_t stream, hipStreamCaptureMode mode) {
HIP_INIT_API(hipStreamBeginCapture, stream, mode);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
// capture cannot be initiated on legacy stream
// It can be initiated if the stream is not already in capture mode
if (stream == nullptr || s->GetCaptureStatus() == hipStreamCaptureStatusActive) {
HIP_RETURN(hipErrorInvalidValue);
}
s->SetCaptureGraph(new hipGraph());
s->SetCaptureMode(mode);
s->SetOriginStream();
g_captureStreams.push_back(stream);
HIP_RETURN_DURATION(hipSuccess);
}
hipError_t hipStreamEndCapture(hipStream_t stream, hipGraph_t* pGraph) {
HIP_INIT_API(hipStreamEndCapture, stream, pGraph);
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
// Capture must be ended on the same stream in which it was initiated
if (!s->IsOriginStream()) {
HIP_RETURN(hipErrorStreamCaptureUnmatched);
}
// If mode is not hipStreamCaptureModeRelaxed, hipStreamEndCapture must be called on the stream
// from the same thread
if (s->GetCaptureMode() != hipStreamCaptureModeRelaxed &&
std::find(g_captureStreams.begin(), g_captureStreams.end(), stream) ==
g_captureStreams.end()) {
HIP_RETURN(hipErrorStreamCaptureWrongThread);
}
// If capture was invalidated, due to a violation of the rules of stream capture
if (s->GetCaptureStatus() == hipStreamCaptureStatusInvalidated) {
*pGraph = nullptr;
HIP_RETURN(hipErrorStreamCaptureInvalidated);
}
// check if all parallel streams have joined
if (s->GetCaptureGraph()->GetLeafNodeCount() != 1) {
return hipErrorStreamCaptureUnjoined;
}
*pGraph = s->GetCaptureGraph();
// end capture on all streams/events part of graph capture
HIP_RETURN_DURATION(s->EndCapture());
}
hipError_t hipGraphCreate(hipGraph_t* pGraph, unsigned int flags) {
HIP_INIT_API(hipGraphCreate, pGraph, flags);
*pGraph = new hipGraph();
HIP_RETURN(hipSuccess);
}
hipError_t hipGraphDestroy(hipGraph_t graph) {
HIP_INIT_API(hipGraphDestroy, graph);
delete graph;
HIP_RETURN(hipSuccess);
}
hipError_t hipGraphAddKernelNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipKernelNodeParams* pNodeParams) {
HIP_INIT_API(hipGraphAddKernelNode, pGraphNode, graph, pDependencies, numDependencies,
pNodeParams);
HIP_RETURN_DURATION(
ihipGraphAddKernelNode(pGraphNode, graph, pDependencies, numDependencies, pNodeParams););
}
hipError_t hipGraphAddMemcpyNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipMemcpy3DParms* pCopyParams) {
HIP_INIT_API(hipGraphAddMemcpyNode, pGraphNode, graph, pDependencies, numDependencies,
pCopyParams);
HIP_RETURN_DURATION(
ihipGraphAddMemcpyNode(pGraphNode, graph, pDependencies, numDependencies, pCopyParams););
}
hipError_t hipGraphAddMemsetNode(hipGraphNode_t* pGraphNode, hipGraph_t graph,
const hipGraphNode_t* pDependencies, size_t numDependencies,
const hipMemsetParams* pMemsetParams) {
HIP_INIT_API(hipGraphAddMemsetNode, pGraphNode, graph, pDependencies, numDependencies,
pMemsetParams);
HIP_RETURN_DURATION(
ihipGraphAddMemsetNode(pGraphNode, graph, pDependencies, numDependencies, pMemsetParams););
}
hipError_t ihipGraphInstantiate(hipGraphExec_t* pGraphExec, hipGraph_t graph,
hipGraphNode_t* pErrorNode, char* pLogBuffer, size_t bufferSize) {
std::vector<std::vector<Node>> parallelLists;
std::unordered_map<Node, std::vector<Node>> nodeWaitLists;
graph->GetRunList(parallelLists, nodeWaitLists);
std::vector<Node> levelOrder;
graph->LevelOrder(levelOrder);
*pGraphExec = new hipGraphExec(levelOrder, parallelLists, nodeWaitLists);
if (*pGraphExec != nullptr) {
return (*pGraphExec)->Init();
} else {
return hipErrorOutOfMemory;
}
}
hipError_t hipGraphInstantiate(hipGraphExec_t* pGraphExec, hipGraph_t graph,
hipGraphNode_t* pErrorNode, char* pLogBuffer, size_t bufferSize) {
HIP_INIT_API(hipGraphInstantiate, pGraphExec, graph);
HIP_RETURN_DURATION(ihipGraphInstantiate(pGraphExec, graph, pErrorNode, pLogBuffer, bufferSize));
}
hipError_t hipGraphExecDestroy(hipGraphExec_t pGraphExec) {
HIP_INIT_API(hipGraphExecDestroy, pGraphExec);
delete pGraphExec;
HIP_RETURN(hipSuccess);
}
hipError_t ihipGraphlaunch(hipGraphExec_t graphExec, hipStream_t stream) {
return graphExec->Run(stream);
}
hipError_t hipGraphLaunch(hipGraphExec_t graphExec, hipStream_t stream) {
HIP_INIT_API(hipGraphLaunch, graphExec, stream);
HIP_RETURN_DURATION(ihipGraphlaunch(graphExec, stream));
}
@@ -0,0 +1,48 @@
/* Copyright (c) 2021-present Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#pragma once
// forward declaration of capture methods
hipError_t capturehipLaunchKernel(hipStream_t& stream, const void*& hostFunction, dim3& gridDim,
dim3& blockDim, void**& args, size_t& sharedMemBytes);
hipError_t capturehipMemcpy3DAsync(hipStream_t& stream, const hipMemcpy3DParms*& p);
hipError_t capturehipMemcpyAsync(hipStream_t& stream, void*& dst, const void*& src,
size_t& sizeBytes, hipMemcpyKind& kind);
hipError_t capturehipMemcpyFromSymbolAsync(hipStream_t& stream, void*& dst, const void*& symbol,
size_t& sizeBytes, size_t& offset, hipMemcpyKind& kind);
hipError_t capturehipMemcpyToSymbolAsync(hipStream_t& stream, const void*& symbol, const void*& src,
size_t& sizeBytes, size_t& offset, hipMemcpyKind& kind);
hipError_t capturehipMemsetAsync(hipStream_t& stream, void*& dst, int& value, size_t& valueSize,
size_t& sizeBytes);
hipError_t capturehipMemset2DAsync(hipStream_t& stream, void*& dst, size_t& pitch, int& value,
size_t& width, size_t& height);
hipError_t capturehipMemset3DAsync(hipStream_t& stream, hipPitchedPtr& pitchedDevPtr, int& value,
hipExtent& extent);
hipError_t capturehipEventRecord(hipStream_t& stream, hipEvent_t& event);
hipError_t capturehipStreamWaitEvent(hipEvent_t& event, hipStream_t& stream, unsigned int& flags);
@@ -0,0 +1,35 @@
hipError_t ihipMemcpy3D_validate(const hipMemcpy3DParms* p);
hipError_t ihipMemcpy_validate(void* dst, const void* src, size_t sizeBytes, hipMemcpyKind kind);
hipError_t ihipMemcpyCommand(amd::Command*& command, void* dst, const void* src, size_t sizeBytes,
hipMemcpyKind kind, amd::HostQueue& queue);
hipError_t ihipLaunchKernel_validate(hipFunction_t f, uint32_t globalWorkSizeX,
uint32_t globalWorkSizeY, uint32_t globalWorkSizeZ,
uint32_t blockDimX, uint32_t blockDimY, uint32_t blockDimZ,
uint32_t sharedMemBytes, void** kernelParams, void** extra,
int deviceId, uint32_t params);
hipError_t ihipMemset_validate(void* dst, int64_t value, size_t valueSize, size_t sizeBytes);
hipError_t ihipMemset3D_validate(hipPitchedPtr pitchedDevPtr, int value, hipExtent extent,
size_t sizeBytes);
hipError_t ihipLaunchKernelCommand(amd::Command*& command, hipFunction_t f,
uint32_t globalWorkSizeX, uint32_t globalWorkSizeY,
uint32_t globalWorkSizeZ, uint32_t blockDimX, uint32_t blockDimY,
uint32_t blockDimZ, uint32_t sharedMemBytes,
amd::HostQueue* queue, void** kernelParams, void** extra,
hipEvent_t startEvent, hipEvent_t stopEvent, uint32_t flags,
uint32_t params, uint32_t gridId, uint32_t numGrids,
uint64_t prevGridSum, uint64_t allGridSum, uint32_t firstDevice);
hipError_t ihipMemcpy3DCommand(amd::Command*& command, const hipMemcpy3DParms* p,
amd::HostQueue* queue);
hipError_t ihipMemsetCommand(std::vector<amd::Command*>& commands, void* dst, int64_t value,
size_t valueSize, size_t sizeBytes, amd::HostQueue* queue);
hipError_t ihipMemset3DCommand(std::vector<amd::Command*>& commands, hipPitchedPtr pitchedDevPtr,
int value, hipExtent extent, amd::HostQueue* queue);
@@ -0,0 +1,364 @@
/* Copyright (c) 2021-present Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "hip_graph_internal.hpp"
#include <queue>
#define CASE_STRING(X, C) \
case X: \
case_string = #C; \
break;
const char* GetGraphNodeTypeString(uint32_t op) {
const char* case_string;
switch (static_cast<hipGraphNodeType>(op)) {
CASE_STRING(hipGraphNodeTypeKernel, KernelNode)
CASE_STRING(hipGraphNodeTypeMemcpy, Memcpy3DNode)
CASE_STRING(hipGraphNodeTypeMemset, MemsetNode)
CASE_STRING(hipGraphNodeTypeHost, HostNode)
CASE_STRING(hipGraphNodeTypeGraph, GraphNode)
CASE_STRING(hipGraphNodeTypeEmpty, EmptyNode)
CASE_STRING(hipGraphNodeTypeWaitEvent, WaitEventNode)
CASE_STRING(hipGraphNodeTypeEventRecord, EventRecordNode)
CASE_STRING(hipGraphNodeTypeMemcpy1D, Memcpy1DNode)
CASE_STRING(hipGraphNodeTypeMemcpyFromSymbol, MemcpyFromSymbolNode)
CASE_STRING(hipGraphNodeTypeMemcpyToSymbol, MemcpyToSymbolNode)
default:
case_string = "Unknown node type";
};
return case_string;
};
hipError_t hipGraph::AddNode(const Node& node) {
vertices_.emplace_back(node);
nodeOutDegree_[node] = 0;
nodeInDegree_[node] = 0;
node->SetLevel(0);
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] Add %s(%p)\n",
GetGraphNodeTypeString(node->GetType()), node);
return hipSuccess;
}
hipError_t hipGraph::AddEdge(const Node& parentNode, const Node& childNode) {
// if vertice doesn't exist, add it to the graph
if (std::find(vertices_.begin(), vertices_.end(), parentNode) == vertices_.end()) {
AddNode(parentNode);
}
if (std::find(vertices_.begin(), vertices_.end(), childNode) == vertices_.end()) {
AddNode(childNode);
}
// Check if edge already exists
auto connectedEdges = edges_.find(parentNode);
if (connectedEdges != edges_.end()) {
if (std::find(connectedEdges->second.begin(), connectedEdges->second.end(), childNode) !=
connectedEdges->second.end()) {
return hipSuccess;
}
connectedEdges->second.emplace_back(childNode);
} else {
edges_[parentNode] = {childNode};
}
nodeOutDegree_[parentNode]++;
nodeInDegree_[childNode]++;
childNode->SetLevel(std::max(childNode->GetLevel(), parentNode->GetLevel() + 1));
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] Add edge btwn %s(%p) - %s(%p)\n",
GetGraphNodeTypeString(parentNode->GetType()), parentNode,
GetGraphNodeTypeString(childNode->GetType()), childNode);
return hipSuccess;
}
// root nodes are all vertices with 0 in-degrees
std::vector<Node> hipGraph::GetRootNodes() const {
std::vector<Node> roots;
for (auto entry : vertices_) {
if (nodeInDegree_.at(entry) == 0) {
roots.push_back(entry);
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] root node: %s(%p)\n",
GetGraphNodeTypeString(entry->GetType()), entry);
}
}
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "\n");
return roots;
}
// leaf nodes are all vertices with 0 out-degrees
std::vector<Node> hipGraph::GetLeafNodes() const {
std::vector<Node> leafNodes;
for (auto entry : vertices_) {
if (nodeOutDegree_.at(entry) == 0) {
leafNodes.push_back(entry);
}
}
return leafNodes;
}
size_t hipGraph::GetLeafNodeCount() const {
int numLeafNodes = 0;
for (auto entry : vertices_) {
if (nodeOutDegree_.at(entry) == 0) {
numLeafNodes++;
}
}
return numLeafNodes;
}
std::vector<std::pair<Node, Node>> hipGraph::GetEdges() const {
std::vector<std::pair<Node, Node>> edges;
for (const auto& i : edges_) {
for (const auto& j : i.second) {
edges.push_back(std::make_pair(i.first, j));
}
}
return edges;
}
void hipGraph::GetRunListUtil(Node v, std::unordered_map<Node, bool>& visited,
std::vector<Node>& singleList,
std::vector<std::vector<Node>>& parallelLists,
std::unordered_map<Node, std::vector<Node>>& dependencies) {
// Mark the current node as visited.
visited[v] = true;
singleList.push_back(v);
// Recurse for all the vertices adjacent to this vertex
for (auto& adjNode : edges_[v]) {
if (!visited[adjNode]) {
// For the parallel list nodes add parent as the dependency
if (singleList.empty()) {
ClPrint(amd::LOG_INFO, amd::LOG_CODE,
"[hipGraph] For %s(%p)- add parent as dependency %s(%p)\n",
GetGraphNodeTypeString(adjNode->GetType()), adjNode,
GetGraphNodeTypeString(v->GetType()), v);
dependencies[adjNode].push_back(v);
}
GetRunListUtil(adjNode, visited, singleList, parallelLists, dependencies);
} else {
for (auto& list : parallelLists) {
// Merge singleList when adjNode matches with the first element of the list in existing
// lists
if (adjNode == list[0]) {
for (auto k = singleList.rbegin(); k != singleList.rend(); ++k) {
list.insert(list.begin(), *k);
}
singleList.erase(singleList.begin(), singleList.end());
}
}
// If the list cannot be merged with the existing list add as dependancy
if (!singleList.empty()) {
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] For %s(%p)- add dependency %s(%p)\n",
GetGraphNodeTypeString(adjNode->GetType()), adjNode,
GetGraphNodeTypeString(v->GetType()), v);
dependencies[adjNode].push_back(v);
}
}
}
if (!singleList.empty()) {
parallelLists.push_back(singleList);
singleList.erase(singleList.begin(), singleList.end());
}
}
// The function to do Topological Sort.
// It uses recursive GetRunListUtil()
void hipGraph::GetRunList(std::vector<std::vector<Node>>& parallelLists,
std::unordered_map<Node, std::vector<Node>>& dependencies) {
std::vector<Node> singleList;
// Mark all the vertices as not visited
std::unordered_map<Node, bool> visited;
for (auto node : vertices_) visited[node] = false;
// Call the recursive helper function for all vertices one by one
for (auto node : vertices_) {
if (visited[node] == false) {
GetRunListUtil(node, visited, singleList, parallelLists, dependencies);
}
}
for (size_t i = 0; i < parallelLists.size(); i++) {
for (size_t j = 0; j < parallelLists[i].size(); j++) {
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] list %d - %s(%p)\n", i + 1,
GetGraphNodeTypeString(parallelLists[i][j]->GetType()), parallelLists[i][j]);
}
}
}
hipError_t hipGraph::LevelOrder(std::vector<Node>& levelOrder) {
std::vector<Node> roots = GetRootNodes();
std::unordered_map<Node, bool> visited;
std::queue<Node> q;
for (auto it = roots.begin(); it != roots.end(); it++) {
q.push(*it);
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] %s(%p) level:%d \n",
GetGraphNodeTypeString((*it)->GetType()), *it, (*it)->GetLevel());
}
while (!q.empty()) {
Node& node = q.front();
q.pop();
levelOrder.push_back(node);
for (const auto& i : edges_[node]) {
if (visited.find(i) == visited.end() && i->GetLevel() == (node->GetLevel() + 1)) {
q.push(i);
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] %s(%p) level:%d \n",
GetGraphNodeTypeString(i->GetType()), i, i->GetLevel());
visited[i] = true;
}
}
}
return hipSuccess;
}
hipError_t hipGraphExec::CreateQueues() {
parallelQueues_.reserve(parallelLists_.size());
for (size_t i = 0; i < parallelLists_.size(); i++) {
amd::HostQueue* queue;
cl_command_queue_properties properties =
(callbacks_table.is_enabled() || HIP_FORCE_QUEUE_PROFILING) ? CL_QUEUE_PROFILING_ENABLE : 0;
queue = new amd::HostQueue(*hip::getCurrentDevice()->asContext(),
*hip::getCurrentDevice()->devices()[0], properties);
bool result = (queue != nullptr) ? queue->create() : false;
// Create a host queue
if (result) {
parallelQueues_.push_back(queue);
} else {
ClPrint(amd::LOG_ERROR, amd::LOG_CODE, "[hipGraph] Failed to create host queue\n");
return hipErrorOutOfMemory;
}
}
return hipSuccess;
}
hipError_t hipGraphExec::FillCommands() {
// Create commands
int i = 0;
hipError_t status;
for (const auto& list : parallelLists_) {
for (auto& node : list) {
status = node->CreateCommand(parallelQueues_[i]);
if (status != hipSuccess) return status;
}
i++;
}
// Add waitlists for all the commands
for (auto& node : levelOrder_) {
auto nodeWaitList = nodeWaitLists_.find(node);
if (nodeWaitList != nodeWaitLists_.end()) {
amd::Command::EventWaitList waitList;
for (auto depNode : nodeWaitList->second) {
for (auto command : depNode->GetCommands()) {
waitList.push_back(command);
}
}
for (auto command : nodeWaitList->first->GetCommands()) {
command->updateEventWaitList(waitList);
}
}
}
return status;
}
hipError_t hipGraphExec::Init() {
hipError_t status;
status = CreateQueues();
if (status != hipSuccess) {
return status;
}
status = FillCommands();
if (status != hipSuccess) {
return status;
}
rootCommand_ = nullptr;
/// stream should execute next command after graph finishes
/// Add marker to the stream that waits for all the last commands in parallel queues of graph
for (auto& singleList : parallelLists_) {
graphLastCmdWaitList_.push_back(singleList.back()->GetCommands().back());
}
return status;
}
void hipGraphExec::ResetGraph(cl_event event, cl_int command_exec_status, void* user_data) {
ClPrint(amd::LOG_INFO, amd::LOG_CODE, "[hipGraph] Inside resetGraph!\n");
hipGraphExec_t graphExec =
hipGraphExec::activeGraphExec_[reinterpret_cast<amd::Command*>(user_data)];
if (graphExec != nullptr) {
for (auto& node : graphExec->levelOrder_) {
for (auto& command : node->GetCommands()) {
command->resetStatus(CL_INT_MAX);
}
}
graphExec->rootCommand_->resetStatus(CL_INT_MAX);
graphExec->bExecPending_.store(false);
} else {
ClPrint(amd::LOG_ERROR, amd::LOG_CODE, "[hipGraph] graphExec is nullptr during resetGraph!\n");
}
}
hipError_t hipGraphExec::UpdateGraphToWaitOnRoot() {
for (auto& singleList : parallelLists_) {
amd::Command::EventWaitList waitList;
waitList.push_back(rootCommand_);
if (!singleList.empty()) {
auto commands = singleList[0]->GetCommands();
if (!commands.empty()) {
commands[0]->updateEventWaitList(waitList);
}
}
}
return hipSuccess;
}
hipError_t hipGraphExec::Run(hipStream_t stream) {
if (bExecPending_.load() == true) {
ClPrint(
amd::LOG_INFO, amd::LOG_CODE,
"[hipGraph] Same graph launched while previous one is active, wait for it to finish!\n");
lastEnqueuedGraphCmd_->awaitCompletion();
}
amd::HostQueue* queue = hip::getQueue(stream);
if (queue == nullptr) {
return hipErrorInvalidResourceHandle;
}
if (rootCommand_ == nullptr || rootCommand_->queue() != queue) {
if (rootCommand_ != nullptr) {
rootCommand_->release();
}
rootCommand_ = new amd::Marker(*queue, false, {});
UpdateGraphToWaitOnRoot();
}
rootCommand_->enqueue();
for (auto& node : levelOrder_) {
for (auto& command : node->GetCommands()) {
command->enqueue();
}
}
amd::Command* command = new amd::Marker(*queue, false, graphLastCmdWaitList_);
if (command == nullptr) {
return hipErrorOutOfMemory;
}
amd::Event& event = command->event();
if (!event.setCallback(CL_COMPLETE, hipGraphExec::ResetGraph, command)) {
return hipErrorInvalidHandle;
}
hipGraphExec::activeGraphExec_[command] = this;
lastEnqueuedGraphCmd_ = command;
bExecPending_.store(true);
command->enqueue();
command->release();
return hipSuccess;
}
@@ -0,0 +1,355 @@
/* Copyright (c) 2021-present Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#pragma once
#include <algorithm>
#include <queue>
#include <stack>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "hip/hip_runtime.h"
#include "hip_internal.hpp"
#include "hip_graph_helper.hpp"
typedef hipGraphNode* Node;
class hipGraphNode {
protected:
uint32_t level_;
hipGraphNodeType type_;
std::vector<amd::Command*> commands_;
bool visited_;
public:
hipGraphNode(hipGraphNodeType type) {
type_ = type;
level_ = 0;
visited_ = false;
}
virtual ~hipGraphNode() {
for (auto command : commands_) {
delete command;
}
}
virtual hipError_t CreateCommand(amd::HostQueue* queue) { return hipSuccess; }
std::vector<amd::Command*>& GetCommands() { return commands_; }
hipGraphNodeType GetType() { return type_; }
uint32_t GetLevel() { return level_; }
void SetLevel(uint32_t level) { level_ = level; }
};
class hipGraph {
std::unordered_map<Node, size_t> nodeInDegree_; // count of in coming edges for every vertex
std::unordered_map<Node, size_t> nodeOutDegree_; // count of outgoing edges for every vertex
std::vector<Node> vertices_;
std::unordered_map<Node, std::vector<Node>> edges_;
public:
hipGraph() {}
~hipGraph(){};
/// add node to the graph
hipError_t AddNode(const Node& node);
/// add edge to the graph
hipError_t AddEdge(const Node& parentNode, const Node& childNode);
/// Returns root nodes, all vertices with 0 in-degrees
std::vector<Node> GetRootNodes() const;
/// Returns leaf nodes, all vertices with 0 out-degrees
std::vector<Node> GetLeafNodes() const;
/// Returns number of leaf nodes
size_t GetLeafNodeCount() const;
/// Returns total numbers of nodes in the graph
size_t GetNodeCount() const { return vertices_.size(); }
/// returns all the nodes in the graph
std::vector<Node> GetNodes() const { return vertices_; }
/// returns all the edges in the graph
std::vector<std::pair<Node, Node>> GetEdges() const;
void GetRunListUtil(Node v, std::unordered_map<Node, bool>& visited,
std::vector<Node>& singleList, std::vector<std::vector<Node>>& parallelList,
std::unordered_map<Node, std::vector<Node>>& dependencies);
void GetRunList(std::vector<std::vector<Node>>& parallelList,
std::unordered_map<Node, std::vector<Node>>& dependencies);
hipError_t LevelOrder(std::vector<Node>& levelOrder);
};
class hipGraphKernelNode : public hipGraphNode {
hipKernelNodeParams* pKernelParams_;
hipFunction_t func_;
public:
hipGraphKernelNode(const hipKernelNodeParams* pNodeParams, const hipFunction_t func)
: hipGraphNode(hipGraphNodeTypeKernel) {
pKernelParams_ = new hipKernelNodeParams(*pNodeParams);
func_ = func;
}
~hipGraphKernelNode() { delete pKernelParams_; }
hipError_t CreateCommand(amd::HostQueue* queue) {
commands_.reserve(1);
amd::Command* command;
hipError_t status = ihipLaunchKernelCommand(
command, func_, pKernelParams_->gridDim.x * pKernelParams_->blockDim.x,
pKernelParams_->gridDim.y * pKernelParams_->blockDim.y,
pKernelParams_->gridDim.z * pKernelParams_->blockDim.z, pKernelParams_->blockDim.x,
pKernelParams_->blockDim.y, pKernelParams_->blockDim.z, pKernelParams_->sharedMemBytes,
queue, pKernelParams_->kernelParams, pKernelParams_->extra, nullptr, nullptr, 0, 0, 0, 0, 0,
0, 0);
commands_.emplace_back(command);
return status;
}
void GetParams(hipKernelNodeParams* params) {
std::memcpy(params, pKernelParams_, sizeof(hipKernelNodeParams));
}
void SetParams(hipKernelNodeParams* params) {
std::memcpy(pKernelParams_, params, sizeof(hipKernelNodeParams));
}
};
class hipGraphMemcpyNode : public hipGraphNode {
hipMemcpy3DParms* pCopyParams_;
public:
hipGraphMemcpyNode(const hipMemcpy3DParms* pCopyParams) : hipGraphNode(hipGraphNodeTypeMemcpy) {
pCopyParams_ = new hipMemcpy3DParms(*pCopyParams);
}
~hipGraphMemcpyNode() { delete pCopyParams_; }
hipError_t CreateCommand(amd::HostQueue* queue) {
commands_.reserve(1);
amd::Command* command;
hipError_t status = ihipMemcpy3DCommand(command, pCopyParams_, queue);
commands_.emplace_back(command);
return status;
}
void GetParams(hipMemcpy3DParms* params) {
std::memcpy(params, pCopyParams_, sizeof(hipMemcpy3DParms));
}
void SetParams(hipMemcpy3DParms* params) {
std::memcpy(pCopyParams_, params, sizeof(hipMemcpy3DParms));
}
};
class hipGraphMemcpyNode1D : public hipGraphNode {
void* dst_;
const void* src_;
size_t count_;
hipMemcpyKind kind_;
public:
hipGraphMemcpyNode1D(void* dst, const void* src, size_t count, hipMemcpyKind kind)
: hipGraphNode(hipGraphNodeTypeMemcpy1D), dst_(dst), src_(src), count_(count), kind_(kind) {}
~hipGraphMemcpyNode1D() {}
hipError_t CreateCommand(amd::HostQueue* queue) {
commands_.reserve(1);
amd::Command* command = nullptr;
hipError_t status = ihipMemcpyCommand(command, dst_, src_, count_, kind_, *queue);
commands_.emplace_back(command);
return status;
}
void SetParams(void* dst, const void* src, size_t count, hipMemcpyKind kind) {
dst_ = dst;
src_ = src;
count_ = count;
kind_ = kind;
}
};
template <class T> class hipGraphMemcpyNodeFromSymbol : public hipGraphNode {
void* dst_;
const T& symbol_;
size_t count_;
size_t offset_;
hipMemcpyKind kind_;
public:
hipGraphMemcpyNodeFromSymbol(void* dst, const void* symbol, size_t count, size_t offset,
hipMemcpyKind kind)
: hipGraphNode(hipGraphNodeTypeMemcpyFromSymbol),
dst_(dst),
symbol_(symbol),
count_(count),
offset_(offset),
kind_(kind) {}
~hipGraphMemcpyNodeFromSymbol() {}
hipError_t CreateCommand(amd::HostQueue* queue);
void SetParams(void* dst, const void* symbol, size_t count, size_t offset, hipMemcpyKind kind) {
dst_ = dst;
symbol_ = symbol;
count_ = count;
offset_ = offset;
kind_ = kind;
}
};
template <class T> class hipGraphMemcpyNodeToSymbol : public hipGraphNode {
const T& symbol_;
const void* src_;
size_t count_;
size_t offset_;
hipMemcpyKind kind_;
public:
hipGraphMemcpyNodeToSymbol(const T& symbol, void* src, size_t count, size_t offset,
hipMemcpyKind kind)
: hipGraphNode(hipGraphNodeTypeMemcpyToSymbol),
symbol_(symbol),
src_(src),
count_(count),
offset_(offset),
kind_(kind) {}
~hipGraphMemcpyNodeToSymbol() {}
hipError_t CreateCommand(amd::HostQueue* queue);
void SetParams(const T& symbol, void* src, size_t count, size_t offset, hipMemcpyKind kind) {
symbol_ = symbol;
src_ = src;
count_ = count;
offset_ = offset;
kind_ = kind;
}
};
class hipGraphMemsetNode : public hipGraphNode {
hipMemsetParams* pMemsetParams_;
public:
hipGraphMemsetNode(const hipMemsetParams* pMemsetParams) : hipGraphNode(hipGraphNodeTypeMemset) {
pMemsetParams_ = new hipMemsetParams(*pMemsetParams);
}
~hipGraphMemsetNode() { delete pMemsetParams_; }
hipError_t CreateCommand(amd::HostQueue* queue) {
if (pMemsetParams_->height == 1) {
return ihipMemsetCommand(commands_, pMemsetParams_->dst, pMemsetParams_->value,
pMemsetParams_->elementSize,
pMemsetParams_->width * pMemsetParams_->elementSize, queue);
} else {
return ihipMemset3DCommand(commands_,
{pMemsetParams_->dst, pMemsetParams_->pitch, pMemsetParams_->width,
pMemsetParams_->height},
pMemsetParams_->elementSize,
{pMemsetParams_->width, pMemsetParams_->height, 1}, queue);
}
return hipSuccess;
}
void GetParams(hipMemsetParams* params) {
std::memcpy(params, pMemsetParams_, sizeof(hipMemsetParams));
}
void SetParams(hipMemsetParams* params) {
std::memcpy(pMemsetParams_, params, sizeof(hipMemsetParams));
}
};
class hipGraphEventRecordNode : public hipGraphNode {
hipEvent_t event_;
public:
hipGraphEventRecordNode(hipEvent_t event)
: hipGraphNode(hipGraphNodeTypeEventRecord), event_(event) {}
~hipGraphEventRecordNode() {}
hipError_t CreateCommand(amd::HostQueue* queue);
void GetParams(hipEvent_t* event) { *event = event_; }
void SetParams(hipEvent_t event) { event_ = event; }
};
class hipGraphEventWaitNode : public hipGraphNode {
hipEvent_t event_;
public:
hipGraphEventWaitNode(hipEvent_t event)
: hipGraphNode(hipGraphNodeTypeWaitEvent), event_(event) {}
~hipGraphEventWaitNode() {}
hipError_t CreateCommand(amd::HostQueue* queue);
void GetParams(hipEvent_t* event) { *event = event_; }
void SetParams(hipEvent_t event) { event_ = event; }
};
class hipGraphHostNode : public hipGraphNode {
hipHostNodeParams* pNodeParams_;
public:
hipGraphHostNode(const hipHostNodeParams* pNodeParams) : hipGraphNode(hipGraphNodeTypeHost) {
pNodeParams_ = new hipHostNodeParams(*pNodeParams);
}
~hipGraphHostNode() { delete pNodeParams_; }
hipError_t CreateCommand(amd::HostQueue* queue);
void GetParams(hipHostNodeParams* params) {
std::memcpy(params, pNodeParams_, sizeof(hipHostNodeParams));
}
void SetParams(hipHostNodeParams* params) {
std::memcpy(pNodeParams_, params, sizeof(hipHostNodeParams));
}
};
class hipGraphExec {
std::vector<std::vector<Node>> parallelLists_;
std::vector<Node> levelOrder_;
std::unordered_map<Node, std::vector<Node>> nodeWaitLists_;
std::vector<amd::HostQueue*> parallelQueues_;
static std::unordered_map<amd::Command*, hipGraphExec_t> activeGraphExec_;
amd::Command::EventWaitList graphLastCmdWaitList_;
amd::Command* lastEnqueuedGraphCmd_;
std::atomic<bool> bExecPending_;
amd::Command* rootCommand_;
public:
hipGraphExec(std::vector<Node>& levelOrder, std::vector<std::vector<Node>>& lists,
std::unordered_map<Node, std::vector<Node>>& nodeWaitLists)
: parallelLists_(lists),
levelOrder_(levelOrder),
nodeWaitLists_(nodeWaitLists),
lastEnqueuedGraphCmd_(nullptr),
rootCommand_(nullptr) {
bExecPending_.store(false);
}
~hipGraphExec() {
for (auto queue : parallelQueues_) {
queue->release();
}
for (auto node : levelOrder_) {
delete node;
}
}
hipError_t CreateQueues();
hipError_t FillCommands();
hipError_t Init();
hipError_t UpdateGraphToWaitOnRoot();
hipError_t Run(hipStream_t stream);
static void ResetGraph(cl_event event, cl_int command_exec_status, void* user_data);
};
+14 -1
View File
@@ -275,4 +275,17 @@ __gnu_f2h_ieee
hipExtStreamGetCUMask
hipImportExternalMemory
hipExternalMemoryGetMappedBuffer
hipDestroyExternalMemory
hipDestroyExternalMemory
hipGraphCreate
hipGraphDestroy
hipGraphAddKernelNode
hipGraphAddMemsetNode
hipGraphAddMemcpyNode
hipGraphAddMemcpyNode1D
hipGraphAddHostNode
hipGraphInstantiate
hipGraphLaunch
hipStreamIsCapturing
hipStreamBeginCapture
hipStreamEndCapture
hipGraphExecDestroy
+13
View File
@@ -270,6 +270,19 @@ global:
hipImportExternalMemory;
hipExternalMemoryGetMappedBuffer;
hipDestroyExternalMemory;
hipGraphCreate;
hipGraphDestroy;
hipGraphAddKernelNode;
hipGraphAddMemsetNode;
hipGraphAddMemcpyNode;
hipGraphAddMemcpyNode1D;
hipGraphAddHostNode;
hipGraphInstantiate;
hipGraphLaunch;
hipStreamIsCapturing;
hipStreamBeginCapture;
hipStreamEndCapture;
hipGraphExecDestroy;
extern "C++" {
hip_impl::hipLaunchKernelGGLImpl*;
hip_impl::demangle*;
+78 -3
View File
@@ -26,6 +26,9 @@
#include "trace_helper.h"
#include "utils/debug.hpp"
#include "hip_formatting.hpp"
#include "hip_graph_capture.hpp"
#include <unordered_set>
#include <thread>
#include <stack>
@@ -123,6 +126,19 @@ typedef struct ihipIpcEventHandle_st {
} \
} while (0);
#define STREAM_CAPTURE(name, stream, ...) \
if (stream != nullptr && \
reinterpret_cast<hip::Stream*>(stream)->GetCaptureStatus() == \
hipStreamCaptureStatusActive) { \
hipError_t status = capture##name(stream, ##__VA_ARGS__); \
HIP_RETURN(status); \
}
#define EVENT_CAPTURE(name, event, ...) \
if (event != nullptr && reinterpret_cast<hip::Event*>(event)->GetCaptureStatus() == true) { \
hipError_t status = capture##name(event, ##__VA_ARGS__); \
HIP_RETURN(status); \
}
namespace hc {
class accelerator;
@@ -134,7 +150,8 @@ namespace hip {
class Stream {
public:
enum Priority : int {High = -1, Normal = 0, Low = 1};
enum Priority : int { High = -1, Normal = 0, Low = 1 };
private:
amd::HostQueue* queue_;
mutable amd::Monitor lock_;
@@ -144,11 +161,31 @@ namespace hip {
bool null_;
const std::vector<uint32_t> cuMask_;
/// Stream capture related parameters
/// Current capture status of the stream
hipStreamCaptureStatus captureStatus_;
/// Graph that is constructed with capture
hipGraph_t pCaptureGraph_;
/// Based on mode stream capture places restrictions on API calls that can be made within or
/// concurrently
hipStreamCaptureMode captureMode_;
bool originStream_;
/// Origin sream has no parent. Parent stream for the derived captured streams with event
/// dependencies
hipStream_t parentStream_;
/// Last graph node captured in the stream
std::vector<hipGraphNode_t> lastCapturedNodes_;
/// Derived streams/Paralell branches from the origin stream
std::vector<hipStream_t> parallelCaptureStreams_;
/// Capture events
std::vector<hipEvent_t> captureEvents_;
public:
Stream(Device* dev, Priority p = Priority::Normal, unsigned int f = 0, bool null_stream = false,
const std::vector<uint32_t>& cuMask = {});
const std::vector<uint32_t>& cuMask = {},
hipStreamCaptureStatus captureStatus = hipStreamCaptureStatusNone);
~Stream();
/// Creates the hip stream object, including AMD host queue
bool Create();
@@ -173,6 +210,44 @@ namespace hip {
/// Sync all non-blocking streams
static void syncNonBlockingStreams();
/// Returns capture status of the current stream
hipStreamCaptureStatus GetCaptureStatus() const { return captureStatus_; }
/// Returns capture mode of the current stream
hipStreamCaptureMode GetCaptureMode() const { return captureMode_; }
/// Returns if stream is origin stream
bool IsOriginStream() const { return originStream_; }
void SetOriginStream() { originStream_ = true; }
/// Returns captured graph
hipGraph_t GetCaptureGraph() const { return pCaptureGraph_; }
/// Returns last captured graph node
std::vector<hipGraphNode_t> GetLastCapturedNodes() const { return lastCapturedNodes_; }
/// Set last captured graph node
void SetLastCapturedNode(hipGraphNode_t graphNode) {
lastCapturedNodes_.clear();
lastCapturedNodes_.push_back(graphNode);
}
/// Append captured node via the wait event cross stream
void AddCrossCapturedNode(std::vector<hipGraphNode_t> graphNodes) {
for (auto node : graphNodes) {
lastCapturedNodes_.push_back(node);
}
}
/// Set graph that is being captured
void SetCaptureGraph(hipGraph_t pGraph) {
pCaptureGraph_ = pGraph;
captureStatus_ = hipStreamCaptureStatusActive;
}
/// reset capture parameters
hipError_t EndCapture();
/// Set capture status
void SetCaptureStatus(hipStreamCaptureStatus captureStatus) { captureStatus_ = captureStatus; }
/// Set capture mode
void SetCaptureMode(hipStreamCaptureMode captureMode) { captureMode_ = captureMode; }
/// Set parent stream
void SetParentStream(hipStream_t parentStream) { parentStream_ = parentStream; }
/// Get parent stream
hipStream_t GetParentStream() { return parentStream_; }
};
/// HIP Device class
+17 -3
View File
@@ -193,7 +193,7 @@ hipError_t ihipMalloc(void** ptr, size_t sizeBytes, unsigned int flags)
return hipSuccess;
}
inline hipError_t ihipMemcpy_validate(void* dst, const void* src, size_t sizeBytes,
hipError_t ihipMemcpy_validate(void* dst, const void* src, size_t sizeBytes,
hipMemcpyKind kind) {
if (dst == nullptr || src == nullptr) {
return hipErrorInvalidValue;
@@ -980,6 +980,8 @@ hipError_t hipMemcpyToSymbolAsync(const void* symbol, const void* src, size_t si
size_t offset, hipMemcpyKind kind, hipStream_t stream) {
HIP_INIT_API(hipMemcpyToSymbolAsync, symbol, src, sizeBytes, offset, kind, stream);
STREAM_CAPTURE(hipMemcpyToSymbolAsync, stream, symbol, src, sizeBytes, offset, kind);
size_t sym_size = 0;
hipDeviceptr_t device_ptr = nullptr;
@@ -995,6 +997,8 @@ hipError_t hipMemcpyFromSymbolAsync(void* dst, const void* symbol, size_t sizeBy
size_t offset, hipMemcpyKind kind, hipStream_t stream) {
HIP_INIT_API(hipMemcpyFromSymbolAsync, symbol, dst, sizeBytes, offset, kind, stream);
STREAM_CAPTURE(hipMemcpyFromSymbolAsync, stream, dst, symbol, sizeBytes, offset, kind);
size_t sym_size = 0;
hipDeviceptr_t device_ptr = nullptr;
@@ -1035,6 +1039,8 @@ hipError_t hipMemcpyAsync(void* dst, const void* src, size_t sizeBytes,
hipMemcpyKind kind, hipStream_t stream) {
HIP_INIT_API(hipMemcpyAsync, dst, src, sizeBytes, kind, stream);
STREAM_CAPTURE(hipMemcpyAsync, stream, dst, src, sizeBytes, kind);
amd::HostQueue* queue = hip::getQueue(stream);
HIP_RETURN_DURATION(ihipMemcpy(dst, src, sizeBytes, kind, *queue, true));
@@ -1927,6 +1933,8 @@ hipError_t hipMemcpy3D(const hipMemcpy3DParms* p) {
hipError_t hipMemcpy3DAsync(const hipMemcpy3DParms* p, hipStream_t stream) {
HIP_INIT_API(hipMemcpy3DAsync, p, stream);
STREAM_CAPTURE(hipMemcpy3DAsync, stream, p);
HIP_RETURN_DURATION(ihipMemcpy3D(p, stream, true));
}
@@ -1961,7 +1969,7 @@ hipError_t packFillMemoryCommand(amd::Command*& command, amd::Memory* memory, si
return hipSuccess;
}
inline hipError_t ihipMemset_validate(void* dst, int64_t value, size_t valueSize,
hipError_t ihipMemset_validate(void* dst, int64_t value, size_t valueSize,
size_t sizeBytes) {
if (sizeBytes == 0) {
// Skip if nothing needs filling.
@@ -2075,6 +2083,8 @@ hipError_t hipMemset(void* dst, int value, size_t sizeBytes) {
hipError_t hipMemsetAsync(void* dst, int value, size_t sizeBytes, hipStream_t stream) {
HIP_INIT_API(hipMemsetAsync, dst, value, sizeBytes, stream);
size_t valueSize = sizeof(int8_t);
STREAM_CAPTURE(hipMemsetAsync, stream, dst, value, valueSize, sizeBytes);
HIP_RETURN(ihipMemset(dst, value, sizeof(int8_t), sizeBytes, stream, true));
}
@@ -2118,7 +2128,7 @@ hipError_t hipMemsetD32Async(hipDeviceptr_t dst, int value, size_t count,
HIP_RETURN(ihipMemset(dst, value, sizeof(int32_t), count * sizeof(int32_t), stream, true));
}
inline hipError_t ihipMemset3D_validate(hipPitchedPtr pitchedDevPtr, int value, hipExtent extent,
hipError_t ihipMemset3D_validate(hipPitchedPtr pitchedDevPtr, int value, hipExtent extent,
size_t sizeBytes) {
size_t offset = 0;
amd::Memory* memory = getMemoryObject(pitchedDevPtr.ptr, offset);
@@ -2200,6 +2210,8 @@ hipError_t hipMemset2DAsync(void* dst, size_t pitch, int value,
size_t width, size_t height, hipStream_t stream) {
HIP_INIT_API(hipMemset2DAsync, dst, pitch, value, width, height, stream);
STREAM_CAPTURE(hipMemset2DAsync, stream, dst, pitch, value, width, height);
HIP_RETURN(ihipMemset3D({dst, pitch, width, height}, value, {width, height, 1}, stream, true));
}
@@ -2212,6 +2224,8 @@ hipError_t hipMemset3D(hipPitchedPtr pitchedDevPtr, int value, hipExtent extent)
hipError_t hipMemset3DAsync(hipPitchedPtr pitchedDevPtr, int value, hipExtent extent, hipStream_t stream) {
HIP_INIT_API(hipMemset3DAsync, pitchedDevPtr, value, extent, stream);
STREAM_CAPTURE(hipMemset3DAsync, stream, pitchedDevPtr, value, extent);
HIP_RETURN(ihipMemset3D(pitchedDevPtr, value, extent, stream, true));
}
+4 -2
View File
@@ -209,7 +209,7 @@ hipError_t hipFuncSetSharedMemConfig ( const void* func, hipSharedMemConfig conf
HIP_RETURN(hipSuccess);
}
inline hipError_t ihipLaunchKernel_validate(hipFunction_t f, uint32_t globalWorkSizeX,
hipError_t ihipLaunchKernel_validate(hipFunction_t f, uint32_t globalWorkSizeX,
uint32_t globalWorkSizeY, uint32_t globalWorkSizeZ,
uint32_t blockDimX, uint32_t blockDimY,
uint32_t blockDimZ, uint32_t sharedMemBytes,
@@ -484,7 +484,9 @@ extern "C" hipError_t hipLaunchKernel(const void *hostFunction,
hipStream_t stream)
{
HIP_INIT_API(hipLaunchKernel, hostFunction, gridDim, blockDim, args, sharedMemBytes, stream);
HIP_RETURN(ihipLaunchKernel(hostFunction, gridDim, blockDim, args, sharedMemBytes, stream, nullptr, nullptr, 0));
STREAM_CAPTURE(hipLaunchKernel, stream, hostFunction, gridDim, blockDim, args, sharedMemBytes);
HIP_RETURN(ihipLaunchKernel(hostFunction, gridDim, blockDim, args, sharedMemBytes, stream,
nullptr, nullptr, 0));
}
extern "C" hipError_t hipExtLaunchKernel(const void* hostFunction,
+31 -4
View File
@@ -31,10 +31,16 @@ static std::unordered_set<hip::Stream*> streamSet;
namespace hip {
// ================================================================================================
Stream::Stream(hip::Device* dev, Priority p,
unsigned int f, bool null_stream, const std::vector<uint32_t>& cuMask)
: queue_(nullptr), lock_("Stream Callback lock"), device_(dev),
priority_(p), flags_(f), null_(null_stream), cuMask_(cuMask) {}
Stream::Stream(hip::Device* dev, Priority p, unsigned int f, bool null_stream,
const std::vector<uint32_t>& cuMask, hipStreamCaptureStatus captureStatus)
: queue_(nullptr),
lock_("Stream Callback lock"),
device_(dev),
priority_(p),
flags_(f),
null_(null_stream),
cuMask_(cuMask),
captureStatus_(captureStatus) {}
// ================================================================================================
Stream::~Stream() {
@@ -47,6 +53,25 @@ Stream::~Stream() {
}
}
hipError_t Stream::EndCapture() {
for (auto event : captureEvents_) {
hip::Event* e = reinterpret_cast<hip::Event*>(event);
e->EndCapture();
}
for (auto stream : parallelCaptureStreams_) {
hip::Stream* s = reinterpret_cast<hip::Stream*>(stream);
s->EndCapture();
}
captureStatus_ = hipStreamCaptureStatusNone;
pCaptureGraph_ = nullptr;
originStream_ = false;
parentStream_ = nullptr;
lastCapturedNodes_.clear();
parallelCaptureStreams_.clear();
captureEvents_.clear();
return hipSuccess;
}
// ================================================================================================
bool Stream::Create() {
// Enable queue profiling if a profiler is attached which sets the callback_table flag
@@ -345,6 +370,8 @@ void WaitThenDecrementSignal(hipStream_t stream, hipError_t status, void* user_d
hipError_t hipStreamWaitEvent(hipStream_t stream, hipEvent_t event, unsigned int flags) {
HIP_INIT_API(hipStreamWaitEvent, stream, event, flags);
EVENT_CAPTURE(hipStreamWaitEvent, event, stream, flags);
if (event == nullptr) {
HIP_RETURN(hipErrorInvalidHandle);
}
@@ -0,0 +1,274 @@
/* Copyright (c) 2021-present Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include <hip/hip_runtime.h>
#include <chrono>
#include <test_common.h>
#include <vector>
/* HIT_START
* BUILD: %t %s ../../test_common.cpp EXCLUDE_HIP_PLATFORM nvidia
* TEST: %t EXCLUDE_HIP_PLATFORM all
* HIT_END
*/
#define THREADS_PER_BLOCK 512
#define GRAPH_LAUNCH_ITERATIONS 3
__global__ void reduce(float* d_in, double* d_out, size_t inputSize, size_t outputSize) {
// sdata is allocated in the kernel call: 3rd arg to <<<b, t, shmem>>>
int myId = threadIdx.x + blockDim.x * blockIdx.x;
int tid = threadIdx.x;
// do reduction in global mem
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
d_in[myId] += d_in[myId + s];
}
__syncthreads(); // make sure all adds at one stage are done!
}
// only thread 0 writes result for this block back to global mem
if (tid == 0) {
int blkx = blockIdx.x;
d_out[blockIdx.x] = d_in[myId];
}
}
__global__ void reduceFinal(double* d_in, double* d_out, size_t inputSize) {
// sdata is allocated in the kernel call: 3rd arg to <<<b, t, shmem>>>
int myId = threadIdx.x + blockDim.x * blockIdx.x;
int tid = threadIdx.x;
// do reduction in global mem
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
d_in[myId] += d_in[myId + s];
}
__syncthreads(); // make sure all adds at one stage are done!
}
// only thread 0 writes result for this block back to global mem
if (tid == 0) {
*d_out = d_in[myId];
}
}
void init_input(float* a, size_t size) {
for (size_t i = 0; i < size; i++) a[i] = (rand() & 0xFF) / (float)RAND_MAX;
}
bool hipGraphsUsingStreamCapture(float* inputVec_h, float* inputVec_d, double* outputVec_d,
double* result_d, size_t inputSize, size_t numOfBlocks) {
hipStream_t stream1, stream2, stream3, streamForGraph;
hipEvent_t forkStreamEvent, memsetEvent1, memsetEvent2;
hipGraph_t graph;
double result_h = 0.0;
HIPCHECK(hipStreamCreate(&stream1));
HIPCHECK(hipStreamCreate(&stream2));
HIPCHECK(hipStreamCreate(&stream3));
HIPCHECK(hipStreamCreate(&streamForGraph));
HIPCHECK(hipEventCreate(&forkStreamEvent));
HIPCHECK(hipEventCreate(&memsetEvent1));
HIPCHECK(hipEventCreate(&memsetEvent2));
HIPCHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal));
HIPCHECK(hipEventRecord(forkStreamEvent, stream1));
HIPCHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0));
HIPCHECK(hipStreamWaitEvent(stream3, forkStreamEvent, 0));
HIPCHECK(
hipMemcpyAsync(inputVec_d, inputVec_h, sizeof(float) * inputSize, hipMemcpyDefault, stream1));
HIPCHECK(hipMemsetAsync(outputVec_d, 0, sizeof(double) * numOfBlocks, stream2));
HIPCHECK(hipEventRecord(memsetEvent1, stream2));
HIPCHECK(hipMemsetAsync(result_d, 0, sizeof(double), stream3));
HIPCHECK(hipEventRecord(memsetEvent2, stream3));
HIPCHECK(hipStreamWaitEvent(stream1, memsetEvent1, 0));
hipLaunchKernelGGL(reduce, dim3(inputSize / THREADS_PER_BLOCK, 1, 1),
dim3(THREADS_PER_BLOCK, 1, 1), 0, stream1, inputVec_d, outputVec_d, inputSize,
numOfBlocks);
HIPCHECK(hipStreamWaitEvent(stream1, memsetEvent2, 0));
hipLaunchKernelGGL(reduceFinal, dim3(1, 1, 1), dim3(THREADS_PER_BLOCK, 1, 1), 0, stream1,
outputVec_d, result_d, numOfBlocks);
HIPCHECK(hipMemcpyAsync(&result_h, result_d, sizeof(double), hipMemcpyDefault, stream1));
HIPCHECK(hipStreamEndCapture(stream1, &graph));
hipGraphExec_t graphExec;
HIPCHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {
HIPCHECK(hipGraphLaunch(graphExec, streamForGraph));
}
HIPCHECK(hipStreamSynchronize(streamForGraph));
HIPCHECK(hipGraphExecDestroy(graphExec));
HIPCHECK(hipGraphDestroy(graph));
HIPCHECK(hipStreamDestroy(stream1));
HIPCHECK(hipStreamDestroy(stream2));
HIPCHECK(hipStreamDestroy(streamForGraph));
double result_h_cpu = 0.0;
for (int i = 0; i < inputSize; i++) {
result_h_cpu += inputVec_h[i];
}
if (result_h_cpu != result_h) {
printf("Final reduced sum = %lf %lf\n", result_h_cpu, result_h);
return false;
}
return true;
}
bool hipGraphsManual(float* inputVec_h, float* inputVec_d, double* outputVec_d, double* result_d,
size_t inputSize, size_t numOfBlocks) {
hipStream_t streamForGraph;
hipGraph_t graph;
std::vector<hipGraphNode_t> nodeDependencies;
hipGraphNode_t memcpyNode, kernelNode, memsetNode;
double result_h = 0.0;
HIPCHECK(hipStreamCreate(&streamForGraph));
hipKernelNodeParams kernelNodeParams = {0};
hipMemcpy3DParms memcpyParams = {0};
hipMemsetParams memsetParams = {0};
memcpyParams.srcArray = NULL;
memcpyParams.srcPos = make_hipPos(0, 0, 0);
memcpyParams.srcPtr = make_hipPitchedPtr(inputVec_h, sizeof(float) * inputSize, inputSize, 1);
memcpyParams.dstArray = NULL;
memcpyParams.dstPos = make_hipPos(0, 0, 0);
memcpyParams.dstPtr = make_hipPitchedPtr(inputVec_d, sizeof(float) * inputSize, inputSize, 1);
memcpyParams.extent = make_hipExtent(sizeof(float) * inputSize, 1, 1);
memcpyParams.kind = hipMemcpyHostToDevice;
memsetParams.dst = (void*)outputVec_d;
memsetParams.value = 0;
memsetParams.pitch = 0;
memsetParams.elementSize = sizeof(float); // elementSize can be max 4 bytes
memsetParams.width = numOfBlocks * 2;
memsetParams.height = 1;
HIPCHECK(hipGraphCreate(&graph, 0));
HIPCHECK(hipGraphAddMemcpyNode(&memcpyNode, graph, NULL, 0, &memcpyParams));
HIPCHECK(hipGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));
nodeDependencies.push_back(memsetNode);
nodeDependencies.push_back(memcpyNode);
void* kernelArgs[4] = {(void*)&inputVec_d, (void*)&outputVec_d, &inputSize, &numOfBlocks};
kernelNodeParams.func = (void*)reduce;
kernelNodeParams.gridDim = dim3(inputSize / THREADS_PER_BLOCK, 1, 1);
kernelNodeParams.blockDim = dim3(THREADS_PER_BLOCK, 1, 1);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = (void**)kernelArgs;
kernelNodeParams.extra = NULL;
HIPCHECK(hipGraphAddKernelNode(&kernelNode, graph, nodeDependencies.data(),
nodeDependencies.size(), &kernelNodeParams));
nodeDependencies.clear();
nodeDependencies.push_back(kernelNode);
memset(&memsetParams, 0, sizeof(memsetParams));
memsetParams.dst = result_d;
memsetParams.value = 0;
memsetParams.elementSize = sizeof(float);
memsetParams.width = 2;
memsetParams.height = 1;
HIPCHECK(hipGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));
nodeDependencies.push_back(memsetNode);
memset(&kernelNodeParams, 0, sizeof(kernelNodeParams));
kernelNodeParams.func = (void*)reduceFinal;
kernelNodeParams.gridDim = dim3(1, 1, 1);
kernelNodeParams.blockDim = dim3(THREADS_PER_BLOCK, 1, 1);
kernelNodeParams.sharedMemBytes = 0;
void* kernelArgs2[3] = {(void*)&outputVec_d, (void*)&result_d, &numOfBlocks};
kernelNodeParams.kernelParams = kernelArgs2;
kernelNodeParams.extra = NULL;
HIPCHECK(hipGraphAddKernelNode(&kernelNode, graph, nodeDependencies.data(),
nodeDependencies.size(), &kernelNodeParams));
nodeDependencies.clear();
nodeDependencies.push_back(kernelNode);
memset(&memcpyParams, 0, sizeof(memcpyParams));
memcpyParams.srcArray = NULL;
memcpyParams.srcPos = make_hipPos(0, 0, 0);
memcpyParams.srcPtr = make_hipPitchedPtr(result_d, sizeof(double), 1, 1);
memcpyParams.dstArray = NULL;
memcpyParams.dstPos = make_hipPos(0, 0, 0);
memcpyParams.dstPtr = make_hipPitchedPtr(&result_h, sizeof(double), 1, 1);
memcpyParams.extent = make_hipExtent(sizeof(double), 1, 1);
memcpyParams.kind = hipMemcpyDeviceToHost;
HIPCHECK(hipGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(),
nodeDependencies.size(), &memcpyParams));
nodeDependencies.clear();
nodeDependencies.push_back(memcpyNode);
hipGraphNode_t hostNode;
hipGraphExec_t graphExec;
HIPCHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {
HIPCHECK(hipGraphLaunch(graphExec, streamForGraph));
}
HIPCHECK(hipStreamSynchronize(streamForGraph));
HIPCHECK(hipGraphExecDestroy(graphExec));
HIPCHECK(hipGraphDestroy(graph));
HIPCHECK(hipStreamDestroy(streamForGraph));
double result_h_cpu = 0.0;
for (int i = 0; i < inputSize; i++) {
result_h_cpu += inputVec_h[i];
}
if (result_h_cpu != result_h) {
printf("Final reduced sum = %lf %lf\n", result_h_cpu, result_h);
return false;
}
return true;
}
int main(int argc, char** argv) {
size_t size = 1 << 12; // number of elements to reduce
size_t maxBlocks = 512;
// This will pick the best possible CUDA capable device
int devID = 1; // TODO: implement: findCudaDevice(argc, (const char**)argv); based of max GFLOPS
// incase of multiple devic
hipSetDevice(0); //
printf("%zu elements\n", size);
printf("threads per block = %d\n", THREADS_PER_BLOCK);
printf("Graph Launch iterations = %d\n", GRAPH_LAUNCH_ITERATIONS);
float *inputVec_d = NULL, *inputVec_h = NULL;
double *outputVec_d = NULL, *result_d;
inputVec_h = (float*)malloc(sizeof(float) * size);
HIPCHECK(hipMalloc(&inputVec_d, sizeof(float) * size));
HIPCHECK(hipMalloc(&outputVec_d, sizeof(double) * maxBlocks));
HIPCHECK(hipMalloc(&result_d, sizeof(double)));
init_input(inputVec_h, size);
bool status1 = hipGraphsManual(inputVec_h, inputVec_d, outputVec_d, result_d, size, maxBlocks);
bool status2 = hipGraphsUsingStreamCapture(inputVec_h, inputVec_d, outputVec_d, result_d, size, maxBlocks);
HIPCHECK(hipFree(inputVec_d));
HIPCHECK(hipFree(outputVec_d));
HIPCHECK(hipFree(result_d));
if(!status1) {
failed("Failed during hip Graph Manual\n");
}
if(!status2) {
failed("Failed during hip Graphs during stream capture\n");
}
passed();
}