SWDEV-468424 - Add support to capture multiple AQL Packets

=> Added support to capture multiple AQL Packets.
=> Added Interface to callback to hip runtime from rocclr to allocate
kernel args from the graph kernel arg pool.
=> Enabled Support to capture memset node.

Change-Id: I7e1c2ba06927459e024653058af142bd82192c43
This commit is contained in:
Anusha GodavarthySurya
2024-08-01 13:30:04 +00:00
committed by Anusha Godavarthy Surya
parent e988e5e448
commit bd3a35bde1
5 changed files with 87 additions and 75 deletions
+11 -4
View File
@@ -1651,7 +1651,15 @@ hipError_t hipGraphExecMemsetNodeSetParams(hipGraphExec_t hGraphExec, hipGraphNo
if (clonedNode == nullptr) {
HIP_RETURN(hipErrorInvalidValue);
}
HIP_RETURN(reinterpret_cast<hip::GraphMemsetNode*>(clonedNode)->SetParams(pNodeParams, true));
hipError_t status = reinterpret_cast<hip::GraphMemsetNode*>(clonedNode)->SetParams(pNodeParams, true);
if(status != hipSuccess) {
HIP_RETURN(status);
}
if (DEBUG_CLR_GRAPH_PACKET_CAPTURE) {
status = reinterpret_cast<hip::GraphExec*>(hGraphExec)
->UpdateAQLPacket(reinterpret_cast<hip::GraphKernelNode*>(clonedNode));
}
HIP_RETURN(status);
}
hipError_t hipGraphAddDependencies(hipGraph_t graph, const hipGraphNode_t* from,
@@ -1773,7 +1781,7 @@ hipError_t hipGraphExecChildGraphNodeSetParams(hipGraphExec_t hGraphExec, hipGra
std::vector<hip::GraphNode*> childGraphNodes;
reinterpret_cast<hip::ChildGraphNode*>(clonedNode)->TopologicalOrder(childGraphNodes);
for (std::vector<hip::GraphNode*>::size_type i = 0; i != childGraphNodes.size(); i++) {
if (childGraphNodes[i]->GetType() == hipGraphNodeTypeKernel) {
if (childGraphNodes[i]->GraphCaptureEnabled()) {
status = reinterpret_cast<hip::GraphExec*>(hGraphExec)
->UpdateAQLPacket(reinterpret_cast<hip::GraphKernelNode*>(childGraphNodes[i]));
if (status != hipSuccess) {
@@ -2478,8 +2486,7 @@ hipError_t hipGraphExecUpdate(hipGraphExec_t hGraphExec, hipGraph_t hGraph,
*updateResult_out = hipGraphExecUpdateErrorNotSupported;
}
HIP_RETURN(hipErrorGraphExecUpdateFailure);
} else if (DEBUG_CLR_GRAPH_PACKET_CAPTURE &&
oldGraphExecNodes[i]->GetType() == hipGraphNodeTypeKernel) {
} else if (DEBUG_CLR_GRAPH_PACKET_CAPTURE && newGraphNodes[i]->GraphCaptureEnabled()) {
status =
reinterpret_cast<hip::GraphExec*>(hGraphExec)
->UpdateAQLPacket(reinterpret_cast<hip::GraphKernelNode*>(oldGraphExecNodes[i]));
+7 -23
View File
@@ -392,17 +392,7 @@ hipError_t AllocKernelArgForGraphNode(std::vector<hip::Node>& topoOrder,
}
}
if (node->GraphCaptureEnabled()) {
// From the kernel pool allocate the kern arg size required for the current node.
address kernArgOffset = nullptr;
if (node->GetKernargSegmentByteSize()) {
kernArgOffset = graphExec->kernArgManager_->AllocKernArg(
node->GetKernargSegmentByteSize(), node->GetKernargSegmentAlignment());
if (kernArgOffset == nullptr) {
return hipErrorMemoryAllocation;
}
}
// Form GPU packet capture for the kernel node.
node->CaptureAndFormPacket(capture_stream, kernArgOffset);
node->CaptureAndFormPacket(capture_stream, graphExec->GetKernelArgManager());
} else if (node->GetType() == hipGraphNodeTypeGraph) {
auto childNode = reinterpret_cast<hip::ChildGraphNode*>(node);
auto& childParallelLists = childNode->GetParallelLists();
@@ -443,17 +433,10 @@ hipError_t GraphExec::CaptureAQLPackets() {
}
// ================================================================================================
hipError_t GraphExec::UpdateAQLPacket(hip::GraphKernelNode* node) {
hipError_t GraphExec::UpdateAQLPacket(hip::GraphNode* node) {
hipError_t status = hipSuccess;
if (parallelLists_.size() == 1) {
size_t pool_new_usage = 0;
address kernArgOffset = nullptr;
kernArgOffset =
kernArgManager_->AllocKernArg(node->GetKerArgSize(), node->GetKernargSegmentAlignment());
if (kernArgOffset == nullptr) {
return hipErrorMemoryAllocation;
}
node->CaptureAndFormPacket(capture_stream_, kernArgOffset);
node->CaptureAndFormPacket(capture_stream_, kernArgManager_);
}
return hipSuccess;
}
@@ -556,9 +539,10 @@ hipError_t EnqueueGraphWithSingleList(std::vector<hip::Node>& topoOrder, hip::St
for (int i = 0; i < topoOrder.size(); i++) {
if (topoOrder[i]->GraphCaptureEnabled()) {
if (topoOrder[i]->GetEnabled()) {
hip_stream->vdev()->dispatchAqlPacket(topoOrder[i]->GetAqlPacket(),
topoOrder[i]->GetKernelName(),
accumulate);
std::vector<uint8_t*>& gpuPackets = topoOrder[i]->GetAqlPackets();
for (auto& packet : gpuPackets) {
hip_stream->vdev()->dispatchAqlPacket(packet, topoOrder[i]->GetKernelName(), accumulate);
}
}
} else {
topoOrder[i]->SetStream(hip_stream, graphExec);
+46 -39
View File
@@ -166,6 +166,41 @@ struct hipGraphNodeDOTAttribute {
}
};
class GraphKernelArgManager : public amd::ReferenceCountedObject, public amd::GraphKernelArgManager {
public:
GraphKernelArgManager() : amd::ReferenceCountedObject() {}
~GraphKernelArgManager() {
//! Release the kernel arg pools
auto device = g_devices[ihipGetDevice()]->devices()[0];
for (auto& element : kernarg_graph_) {
device->hostFree(element.kernarg_pool_addr_, element.kernarg_pool_size_);
}
kernarg_graph_.clear();
}
// Allocate kernel arg pool for the given size.
bool AllocGraphKernargPool(size_t pool_size);
// Allocate kernel args from current chunck for given size and alignment.
// If kernel arg pool is full allocate new chunck and alloc kern args from new pool.
address AllocKernArg(size_t size, size_t alignment) override;
// Do HDP flush/When HDP flush register is invalid fallback to Readback
void ReadBackOrFlush();
private:
struct KernelArgPoolGraph {
KernelArgPoolGraph(address base_addr, size_t size)
: kernarg_pool_addr_(base_addr), kernarg_pool_size_(size), kernarg_pool_offset_(0) {}
address kernarg_pool_addr_; //! Base address of the kernel arg pool
size_t kernarg_pool_size_; //! Size of the pool
size_t kernarg_pool_offset_; //! Current offset in the kernel arg alloc
};
bool device_kernarg_pool_ = false; //! Indicate if kernel pool in device mem
std::vector<KernelArgPoolGraph> kernarg_graph_; //! Vector of allocated kernarg pool
using KernelArgImpl = device::Settings::KernelArgImpl;
};
struct GraphNode : public hipGraphNodeDOTAttribute {
protected:
hip::Stream* stream_ = nullptr;
@@ -184,7 +219,7 @@ struct GraphNode : public hipGraphNodeDOTAttribute {
static std::unordered_set<GraphNode*> nodeSet_;
static amd::Monitor nodeSetLock_;
unsigned int isEnabled_;
uint8_t gpuPacket_[64]; //!< GPU Packet to enqueue during graph launch
std::vector<uint8_t *> gpuPackets_; //!< GPU Packet to enqueue during graph launch
std::string capturedKernelName_;
size_t alignedKernArgSize_ = 256; //!< Aligned size required for kernel args
size_t kernargSegmentByteSize_ = 256; //!< Kernel arg segment byte size
@@ -224,6 +259,9 @@ struct GraphNode : public hipGraphNodeDOTAttribute {
for (auto node : dependencies_) {
node->RemoveEdge(this);
}
for (auto packet : gpuPackets_) {
delete packet;
}
amd::ScopedLock lock(nodeSetLock_);
nodeSet_.erase(this);
}
@@ -237,16 +275,16 @@ struct GraphNode : public hipGraphNodeDOTAttribute {
return true;
}
// Return gpu packet address to update with actual packet under capture.
uint8_t* GetAqlPacket() { return gpuPacket_; }
std::vector<uint8_t *>& GetAqlPackets() { return gpuPackets_; }
void SetKernelName(const std::string& kernelName) { capturedKernelName_ = kernelName; }
const std::string& GetKernelName() const { return capturedKernelName_; }
size_t GetKerArgSize() const { return alignedKernArgSize_; }
size_t GetKernargSegmentByteSize() const { return kernargSegmentByteSize_; }
size_t GetKernargSegmentAlignment() const { return kernargSegmentAlignment_; }
void CaptureAndFormPacket(hip::Stream* capture_stream, address kernArgOffset) {
void CaptureAndFormPacket(hip::Stream* capture_stream, GraphKernelArgManager* kernArgMgr) {
hipError_t status = CreateCommand(capture_stream);
for (auto& command : commands_) {
command->setCapturingState(true, GetAqlPacket(), kernArgOffset, &capturedKernelName_);
command->setCapturingState(true, &gpuPackets_, kernArgMgr, &capturedKernelName_);
// Enqueue command to capture GPU Packet. The packet is not submitted to the device.
// The packet is stored in gpuPacket_ and submitted during graph launch.
command->submit(*(command->queue())->vdev());
@@ -404,6 +442,9 @@ struct GraphNode : public hipGraphNodeDOTAttribute {
case hipGraphNodeTypeKernel:
isGraphCapture = true;
break;
case hipGraphNodeTypeMemset:
isGraphCapture = true;
break;
default:
break;
}
@@ -582,40 +623,6 @@ struct Graph {
}
};
struct GraphKernelNode;
struct GraphKernelArgManager : public amd::ReferenceCountedObject {
public:
GraphKernelArgManager() : ReferenceCountedObject() {}
~GraphKernelArgManager() {
//! Release the kernel arg pools
auto device = g_devices[ihipGetDevice()]->devices()[0];
for (auto& element : kernarg_graph_) {
device->hostFree(element.kernarg_pool_addr_, element.kernarg_pool_size_);
}
kernarg_graph_.clear();
}
// Allocate kernel arg pool for the given size.
bool AllocGraphKernargPool(size_t pool_size);
// Allocate kernel args from current chunck for given size and alignment.
// If kernel arg pool is full allocate new chunck and alloc kern args from new pool.
address AllocKernArg(size_t size, size_t alignment);
// Do HDP flush/When HDP flush register is invalid fallback to Readback
void ReadBackOrFlush();
private:
struct KernelArgPoolGraph {
KernelArgPoolGraph(address base_addr, size_t size)
: kernarg_pool_addr_(base_addr), kernarg_pool_size_(size), kernarg_pool_offset_(0) {}
address kernarg_pool_addr_; //! Base address of the kernel arg pool
size_t kernarg_pool_size_; //! Size of the pool
size_t kernarg_pool_offset_; //! Current offset in the kernel arg alloc
};
bool device_kernarg_pool_ = false; //! Indicate if kernel pool in device mem
std::vector<KernelArgPoolGraph> kernarg_graph_; //! Vector of allocated kernarg pool
using KernelArgImpl = device::Settings::KernelArgImpl;
};
struct GraphExec : public amd::ReferenceCountedObject {
std::vector<std::vector<Node>> parallelLists_;
@@ -699,7 +706,7 @@ struct GraphExec : public amd::ReferenceCountedObject {
hipError_t Run(hipStream_t stream);
// Capture GPU Packets from graph commands
hipError_t CaptureAQLPackets();
hipError_t UpdateAQLPacket(hip::GraphKernelNode* node);
hipError_t UpdateAQLPacket(hip::GraphNode* node);
// Kenrel arg manger is for the entire graph.
// Child graph also shares the same kernel arg manager object. some apps have 100's of
// child graph nodes and each child graph has only one node.