SDK: counter collection serialization per device (#1157)
Migrates profiler_serializer class in QueueController to have an instance per-agent instead of one globally. Other changes in this commit are to allow for maps of the queues associated with each agent to be passed to profiler_serializer when it is turned on/off. Existing test cases cover whether or not the kernels are serialized (multistream app). New test case added to show that this serialization only occurs on a per device level with a kernel launched on one device waiting for a value to be set on the other.
This commit is contained in:
@@ -53,6 +53,28 @@ set_tests_properties(
|
||||
"${counter-collection-buffer-env}" FAIL_REGULAR_EXPRESSION
|
||||
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
|
||||
|
||||
set_source_files_properties(per_dev_serialization.cpp PROPERTIES LANGUAGE HIP)
|
||||
add_executable(counter-collection-buffer-device-serialization)
|
||||
target_sources(counter-collection-buffer-device-serialization
|
||||
PRIVATE per_dev_serialization.cpp)
|
||||
target_link_libraries(counter-collection-buffer-device-serialization
|
||||
PRIVATE counter-collection-buffer-client Threads::Threads)
|
||||
|
||||
rocprofiler_samples_get_ld_library_path_env(LIBRARY_PATH_ENV)
|
||||
rocprofiler_samples_get_preload_env(PRELOAD_ENV counter-collection-buffer-client)
|
||||
|
||||
set(counter-collection-buffer-device-serialization-env "${PRELOAD_ENV}"
|
||||
"${LIBRARY_PATH_ENV}")
|
||||
|
||||
add_test(NAME counter-collection-buffer-device-serialization
|
||||
COMMAND $<TARGET_FILE:counter-collection-buffer-device-serialization>)
|
||||
|
||||
set_tests_properties(
|
||||
counter-collection-buffer-device-serialization
|
||||
PROPERTIES TIMEOUT 120 LABELS "samples" ENVIRONMENT
|
||||
"${counter-collection-buffer-device-serialization-env}"
|
||||
FAIL_REGULAR_EXPRESSION "${ROCPROFILER_DEFAULT_FAIL_REGEX}")
|
||||
|
||||
add_library(counter-collection-callback-client SHARED)
|
||||
target_sources(counter-collection-callback-client PRIVATE callback_client.cpp client.hpp)
|
||||
target_link_libraries(
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "client.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
@@ -74,6 +75,13 @@ get_buffer()
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::unordered_map<uint64_t, std::vector<rocprofiler_record_dimension_info_t>>**
|
||||
dimension_cache()
|
||||
{
|
||||
static std::unordered_map<uint64_t, std::vector<rocprofiler_record_dimension_info_t>>* cache;
|
||||
return &cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given counter, query the dimensions that it has. Typically you will
|
||||
* want to call this function once to get the dimensions and cache them.
|
||||
@@ -81,6 +89,20 @@ get_buffer()
|
||||
std::vector<rocprofiler_record_dimension_info_t>
|
||||
counter_dimensions(rocprofiler_counter_id_t counter)
|
||||
{
|
||||
if(*dimension_cache() == nullptr) return {};
|
||||
|
||||
if((*dimension_cache())->count(counter.handle) > 0)
|
||||
{
|
||||
return (*dimension_cache())->at(counter.handle);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void
|
||||
fill_dimension_cache(rocprofiler_counter_id_t counter)
|
||||
{
|
||||
assert(*dimension_cache() != nullptr);
|
||||
std::vector<rocprofiler_record_dimension_info_t> dims;
|
||||
rocprofiler_available_dimensions_cb_t cb =
|
||||
[](rocprofiler_counter_id_t,
|
||||
@@ -97,7 +119,7 @@ counter_dimensions(rocprofiler_counter_id_t counter)
|
||||
};
|
||||
ROCPROFILER_CALL(rocprofiler_iterate_counter_dimensions(counter, cb, &dims),
|
||||
"Could not iterate counter dimensions");
|
||||
return dims;
|
||||
(*dimension_cache())->emplace(counter.handle, dims);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,6 +273,7 @@ build_profile_for_agent(rocprofiler_agent_id_t agent,
|
||||
{
|
||||
std::clog << "Counter: " << counter.handle << " " << version.name << "\n";
|
||||
collect_counters.push_back(counter);
|
||||
fill_dimension_cache(counter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +398,10 @@ tool_fini(void* user_data)
|
||||
auto* output_stream = static_cast<std::ostream*>(user_data);
|
||||
*output_stream << std::flush;
|
||||
if(output_stream != &std::cout && output_stream != &std::cerr) delete output_stream;
|
||||
|
||||
auto* tmp_ptr = *dimension_cache();
|
||||
*dimension_cache() = nullptr;
|
||||
delete tmp_ptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -416,6 +443,9 @@ rocprofiler_configure(uint32_t version,
|
||||
&tool_fini,
|
||||
static_cast<void*>(output_stream)};
|
||||
|
||||
*dimension_cache() =
|
||||
new std::unordered_map<uint64_t, std::vector<rocprofiler_record_dimension_info_t>>();
|
||||
|
||||
// return pointer to configure data
|
||||
return &cfg;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// 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 "client.hpp"
|
||||
|
||||
#define HIP_CALL(call) \
|
||||
do \
|
||||
{ \
|
||||
hipError_t err = call; \
|
||||
if(err != hipSuccess) \
|
||||
{ \
|
||||
fprintf(stderr, "%s\n", hipGetErrorString(err)); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
__global__ void
|
||||
kernelA(int* wait_on, int value, int* no_opt)
|
||||
{
|
||||
while(*wait_on != value)
|
||||
{
|
||||
(*no_opt)++;
|
||||
};
|
||||
(*wait_on)--;
|
||||
}
|
||||
|
||||
int
|
||||
main(int, char**)
|
||||
{
|
||||
int ntotdevice = 0;
|
||||
HIP_CALL(hipGetDeviceCount(&ntotdevice));
|
||||
if(ntotdevice < 2) return 0;
|
||||
|
||||
start();
|
||||
int* check_value = nullptr;
|
||||
int* no_opt = nullptr;
|
||||
HIP_CALL(hipMallocManaged(&check_value, sizeof(*check_value)));
|
||||
HIP_CALL(hipMallocManaged(&no_opt, sizeof(*no_opt)));
|
||||
*no_opt = 0;
|
||||
*check_value = 1;
|
||||
// Will hang if per-device serialization is not functional
|
||||
HIP_CALL(hipSetDevice(0));
|
||||
hipLaunchKernelGGL(kernelA, dim3(1), dim3(1), 0, 0, check_value, 0, no_opt);
|
||||
HIP_CALL(hipSetDevice(1));
|
||||
hipLaunchKernelGGL(kernelA, dim3(1), dim3(1), 0, 0, check_value, 1, no_opt);
|
||||
HIP_CALL(hipSetDevice(0));
|
||||
HIP_CALL(hipDeviceSynchronize());
|
||||
|
||||
std::cerr << "Run complete\n";
|
||||
}
|
||||
Reference in New Issue
Block a user