Calculate theoretical occupancy and make it available for tools.

PiperOrigin-RevId: 342991548
Change-Id: Iae71c91dd4dae1529f4c189b5c2cb1290c6682a6
This commit is contained in:
Yi Situ 2020-11-17 18:47:44 -08:00 committed by TensorFlower Gardener
parent fa933c373a
commit 7e59021b41
14 changed files with 288 additions and 99 deletions

View File

@ -87,15 +87,12 @@ TEST(OpStatsToTfStats, GpuTfStats) {
constexpr int64 kKernel5DurationNs = 10000;
// Mock kernel details for both kernel4 and kernel5.
const std::string kKernelDetails = R"MULTI(registers_per_thread:32
static_shared_memory_usage:0
dynamic_shared_memory_usage:16384
grid_x:2
grid_y:1
grid_z:1
block_x:32
block_y:1
block_z:1)MULTI";
const std::string kKernelDetails = R"MULTI(regs:32
static_shared:0
dynamic_shared:16384
grid:2,1,1
block:32,1,1
occ_pct:1.0)MULTI";
XSpace space;
XPlaneBuilder device_plane(

View File

@ -41,44 +41,35 @@ TEST(ConvertXplaneToKernelStats, MultiKernels) {
CreateXEvent(&device_trace_builder, &line_builder, "kernel_name_shortest",
/*offset_ps=*/10000, /*duration_ps=*/1000,
{{StatType::kTfOp, "mul_786"},
{StatType::kKernelDetails, R"MULTI(registers_per_thread:16
static_shared_memory_usage:0
dynamic_shared_memory_usage:0
grid_x:1
grid_y:1
grid_z:1
block_x:1
block_y:1
block_z:1)MULTI"},
{StatType::kKernelDetails, R"MULTI(regs:16
static_shared:0
dynamic_shared:0
grid:1,1,1
block:1,1,1
occ_pct:0.5)MULTI"},
{StatType::kEquation, ""}});
CreateXEvent(&device_trace_builder, &line_builder, "kernel_name_middle",
/*offset_ps=*/20000, /*duration_ps=*/2000,
{{StatType::kTfOp, "Conv2D"},
{StatType::kKernelDetails, R"MULTI(registers_per_thread:32
static_shared_memory_usage:0
dynamic_shared_memory_usage:16384
grid_x:2
grid_y:1
grid_z:1
block_x:32
block_y:1
block_z:1)MULTI"},
{StatType::kKernelDetails, R"MULTI(regs:32
static_shared:0
dynamic_shared:16384
grid:2,1,1
block:32,1,1
occ_pct=0.13)MULTI"},
{StatType::kEquation, ""}});
CreateXEvent(&device_trace_builder, &line_builder,
"volta_fp16_s884gemm_fp16_128x128_ldg8_f2f_tn",
/*offset_ps=*/30000, /*duration_ps=*/3000,
{{StatType::kTfOp, "Einsum_80"},
{StatType::kKernelDetails, R"MULTI(registers_per_thread:32
static_shared_memory_usage:0
dynamic_shared_memory_usage:16384
grid_x:3
grid_y:1
grid_z:1
block_x:64
block_y:1
block_z:1)MULTI"},
{StatType::kKernelDetails, R"MULTI(regs:32
static_shared:0
dynamic_shared:16384
grid:3,1,1
block:64,1,1
occ_pct:0.25)MULTI"},
{StatType::kEquation, ""}});
KernelReportMap reports;

View File

@ -158,6 +158,26 @@ tf_cuda_library(
],
)
cc_library(
name = "cupti_collector_header",
hdrs = ["cupti_collector.h"],
visibility = ["//visibility:public"],
deps = [
"//tensorflow/core:lib",
"//tensorflow/core:protos_all_cc",
"//tensorflow/core/profiler/protobuf:xplane_proto_cc",
"//tensorflow/core/profiler/utils:parse_annotation",
"//tensorflow/core/profiler/utils:xplane_builder",
"//tensorflow/core/profiler/utils:xplane_schema",
"//tensorflow/core/profiler/utils:xplane_utils",
"@com_google_absl//absl/container:fixed_array",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/container:node_hash_set",
"@com_google_absl//absl/strings",
],
)
tf_cuda_library(
name = "cupti_utils",
srcs = if_cuda_is_configured_compat(["cupti_utils.cc"]),

View File

@ -15,10 +15,13 @@ limitations under the License.
#include "tensorflow/core/profiler/internal/gpu/cupti_collector.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/hash/hash.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "third_party/gpus/cuda/include/cuda.h"
#include "third_party/gpus/cuda/include/cuda_occupancy.h"
#include "tensorflow/core/platform/abi.h"
#include "tensorflow/core/platform/host_info.h"
#include "tensorflow/core/platform/mutex.h"
@ -59,6 +62,34 @@ bool IsHostEvent(const CuptiTracerEvent& event, int64* line_id) {
}
}
struct DeviceOccupancyParams {
cudaOccFuncAttributes attributes = {};
int block_size = 0;
size_t dynamic_smem_size = 0;
friend bool operator==(const DeviceOccupancyParams& lhs,
const DeviceOccupancyParams& rhs) {
return 0 == memcmp(&lhs, &rhs, sizeof(lhs));
}
template <typename H>
friend H AbslHashValue(H hash_state, const DeviceOccupancyParams& params) {
return H::combine(
std::move(hash_state), params.attributes.maxThreadsPerBlock,
params.attributes.numRegs, params.attributes.sharedSizeBytes,
static_cast<uint32_t>(params.attributes.partitionedGCConfig),
static_cast<uint32_t>(params.attributes.shmemLimitConfig),
params.attributes.maxDynamicSharedSizeBytes, params.block_size,
params.dynamic_smem_size);
}
};
struct OccupancyStats {
double occupancy_pct = 0.0;
int min_grid_size = 0;
int suggested_block_size = 0;
};
struct CorrelationInfo {
CorrelationInfo(uint32 t, uint32 e) : thread_id(t), enqueue_time_ns(e) {}
uint32 thread_id;
@ -66,6 +97,35 @@ struct CorrelationInfo {
};
struct PerDeviceCollector {
OccupancyStats GetOccupancy(const DeviceOccupancyParams& params) const {
OccupancyStats stats;
if (device_properties.computeMajor == 0) {
return {};
}
const cudaOccDeviceState state = {};
cudaOccResult occ_result;
cudaOccError status = cudaOccMaxActiveBlocksPerMultiprocessor(
&occ_result, &device_properties, &params.attributes, &state,
params.block_size, params.dynamic_smem_size);
if (status != CUDA_OCC_SUCCESS) {
return {};
}
stats.occupancy_pct =
occ_result.activeBlocksPerMultiprocessor * params.block_size;
stats.occupancy_pct /= device_properties.maxThreadsPerMultiprocessor;
status = cudaOccMaxPotentialOccupancyBlockSize(
&stats.min_grid_size, &stats.suggested_block_size, &device_properties,
&params.attributes, &state, NULL, params.dynamic_smem_size);
if (status != CUDA_OCC_SUCCESS) {
return {};
}
return stats;
}
void CreateXEvent(const CuptiTracerEvent& event, XPlaneBuilder* plane,
uint64 start_gpu_ns, uint64 end_gpu_ns,
XLineBuilder* line) {
@ -105,16 +165,41 @@ struct PerDeviceCollector {
*plane->GetOrCreateStatMetadata(GetStatTypeStr(StatType::kContextId)),
absl::StrCat("$$", static_cast<uint64>(event.context_id)));
}
if (event.type == CuptiTracerEventType::Kernel) {
std::string kernel_details = absl::StrCat(
"regs:", event.kernel_info.registers_per_thread,
" shm:", event.kernel_info.static_shared_memory_usage,
" grid:", event.kernel_info.grid_x, ",", event.kernel_info.grid_y,
",", event.kernel_info.grid_z, " block:", event.kernel_info.block_x,
",", event.kernel_info.block_y, ",", event.kernel_info.block_z);
if (event.type == CuptiTracerEventType::Kernel &&
event.source == CuptiTracerEventSource::Activity) {
DeviceOccupancyParams params{};
params.attributes.maxThreadsPerBlock = INT_MAX;
params.attributes.numRegs =
static_cast<int>(event.kernel_info.registers_per_thread);
params.attributes.sharedSizeBytes =
event.kernel_info.static_shared_memory_usage;
params.attributes.partitionedGCConfig = PARTITIONED_GC_OFF;
params.attributes.shmemLimitConfig = FUNC_SHMEM_LIMIT_DEFAULT;
params.attributes.maxDynamicSharedSizeBytes = 0;
params.block_size = static_cast<int>(event.kernel_info.block_x *
event.kernel_info.block_y *
event.kernel_info.block_z);
params.dynamic_smem_size = event.kernel_info.dynamic_shared_memory_usage;
OccupancyStats& occ_stats = occupancy_cache[params];
if (occ_stats.occupancy_pct == 0) {
occ_stats = GetOccupancy(params);
}
xevent.AddStatValue(*plane->GetOrCreateStatMetadata(GetStatTypeStr(
StatType::kTheoreticalOccupancyPct)),
occ_stats.occupancy_pct);
xevent.AddStatValue(*plane->GetOrCreateStatMetadata(
GetStatTypeStr(StatType::kOccupancyMinGridSize)),
static_cast<int32>(occ_stats.min_grid_size));
xevent.AddStatValue(*plane->GetOrCreateStatMetadata(GetStatTypeStr(
StatType::kOccupancySuggestedBlockSize)),
static_cast<int32>(occ_stats.suggested_block_size));
xevent.AddStatValue(*plane->GetOrCreateStatMetadata(
GetStatTypeStr(StatType::kKernelDetails)),
*plane->GetOrCreateStatMetadata(kernel_details));
*plane->GetOrCreateStatMetadata(ToXStat(
event.kernel_info, occ_stats.occupancy_pct)));
} else if (event.type == CuptiTracerEventType::MemcpyH2D ||
event.type == CuptiTracerEventType::MemcpyD2H ||
event.type == CuptiTracerEventType::MemcpyD2D ||
@ -416,12 +501,49 @@ struct PerDeviceCollector {
GetStatTypeStr(StatType::kDevCapComputeCapMinor)),
*compute_capability_minor);
}
auto max_threads_per_block =
GetDeviceAttribute(device, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK);
auto max_threads_per_sm = GetDeviceAttribute(
device, CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR);
auto regs_per_block =
GetDeviceAttribute(device, CU_DEVICE_ATTRIBUTE_REGISTERS_PER_BLOCK);
auto regs_per_sm = GetDeviceAttribute(
device, CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_MULTIPROCESSOR);
auto warp_size = GetDeviceAttribute(device, CU_DEVICE_ATTRIBUTE_WARP_SIZE);
auto shared_mem_per_block = GetDeviceAttribute(
device, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK);
auto shared_mem_per_sm = GetDeviceAttribute(
device, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_MULTIPROCESSOR);
auto shared_mem_per_block_optin = GetDeviceAttribute(
device, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK_OPTIN);
// Precondition for calculating GPU occupancy is to have all of these
// inputs. Otherwise, GPU occupancy will be left unset as 0%.
if (core_count && compute_capability_major && compute_capability_minor &&
max_threads_per_block && max_threads_per_sm && regs_per_block &&
regs_per_sm && warp_size && shared_mem_per_block && shared_mem_per_sm &&
shared_mem_per_block_optin) {
device_properties.computeMajor = *compute_capability_major;
device_properties.computeMinor = *compute_capability_minor;
device_properties.numSms = *core_count;
device_properties.maxThreadsPerBlock = *max_threads_per_block;
device_properties.maxThreadsPerMultiprocessor = *max_threads_per_sm;
device_properties.regsPerBlock = *regs_per_block;
device_properties.regsPerMultiprocessor = *regs_per_sm;
device_properties.warpSize = *warp_size;
device_properties.sharedMemPerBlock = *shared_mem_per_block;
device_properties.sharedMemPerMultiprocessor = *shared_mem_per_sm;
device_properties.sharedMemPerBlockOptin = *shared_mem_per_block_optin;
}
}
mutex m;
std::vector<CuptiTracerEvent> events TF_GUARDED_BY(m);
absl::flat_hash_map<uint32, CorrelationInfo> correlation_info
TF_GUARDED_BY(m);
cudaOccDeviceProp device_properties;
absl::flat_hash_map<DeviceOccupancyParams, OccupancyStats> occupancy_cache;
};
} // namespace
@ -509,10 +631,13 @@ class CuptiTraceCollectorImpl : public CuptiTraceCollector {
std::string name = GpuPlaneName(device_ordinal);
XPlaneBuilder device_plane(FindOrAddMutablePlaneWithName(space, name));
device_plane.SetId(device_ordinal);
num_events += per_device_collector_[device_ordinal].Flush(
start_gpu_ns_, end_gpu_ns, &device_plane, &host_plane);
// Calculate device capabilities before flushing, so that device
// properties are available to the occupancy calculator in Flush().
per_device_collector_[device_ordinal].GetDeviceCapabilities(
device_ordinal, &device_plane);
num_events += per_device_collector_[device_ordinal].Flush(
start_gpu_ns_, end_gpu_ns, &device_plane, &host_plane);
NormalizeTimeStamps(&device_plane, start_walltime_ns_);
}
NormalizeTimeStamps(&host_plane, start_walltime_ns_);

View File

@ -55,25 +55,37 @@ struct MemAllocDetails {
struct KernelDetails {
// The number of registers used in this kernel.
uint64 registers_per_thread;
uint32 registers_per_thread;
// The amount of shared memory space used by a thread block.
uint64 static_shared_memory_usage;
uint32 static_shared_memory_usage;
// The amount of dynamic memory space used by a thread block.
uint64 dynamic_shared_memory_usage;
uint32 dynamic_shared_memory_usage;
// X-dimension of a thread block.
uint64 block_x;
uint32 block_x;
// Y-dimension of a thread block.
uint64 block_y;
uint32 block_y;
// Z-dimension of a thread block.
uint64 block_z;
uint32 block_z;
// X-dimension of a grid.
uint64 grid_x;
uint32 grid_x;
// Y-dimension of a grid.
uint64 grid_y;
uint32 grid_y;
// Z-dimension of a grid.
uint64 grid_z;
uint32 grid_z;
};
inline std::string ToXStat(const KernelDetails& kernel_info,
double occupancy_pct) {
return absl::StrCat(
"regs:", kernel_info.registers_per_thread,
" static_shared:", kernel_info.static_shared_memory_usage,
" dynamic_shared:", kernel_info.dynamic_shared_memory_usage,
" grid:", kernel_info.grid_x, ",", kernel_info.grid_y, ",",
kernel_info.grid_z, " block:", kernel_info.block_x, ",",
kernel_info.block_y, ",", kernel_info.block_z,
" occ_pct:", occupancy_pct);
}
enum class CuptiTracerEventType {
Unsupported = 0,
Kernel = 1,
@ -91,8 +103,9 @@ enum class CuptiTracerEventType {
const char* GetTraceEventTypeName(const CuptiTracerEventType& type);
enum class CuptiTracerEventSource {
DriverCallback = 0,
Activity = 1,
Invalid = 0,
DriverCallback = 1,
Activity = 2,
// Maybe consider adding runtime callback and metric api in the future.
};
@ -105,8 +118,8 @@ struct CuptiTracerEvent {
std::numeric_limits<uint64_t>::max();
static constexpr uint64 kInvalidStreamId =
std::numeric_limits<uint64_t>::max();
CuptiTracerEventType type;
CuptiTracerEventSource source;
CuptiTracerEventType type = CuptiTracerEventType::Unsupported;
CuptiTracerEventSource source = CuptiTracerEventSource::Invalid;
// Although CUpti_CallbackData::functionName is persistent, however
// CUpti_ActivityKernel4::name is not persistent, therefore we need a copy of
// it.
@ -114,9 +127,9 @@ struct CuptiTracerEvent {
// This points to strings in AnnotationMap, which should outlive the point
// where serialization happens.
absl::string_view annotation;
uint64 start_time_ns;
uint64 end_time_ns;
uint32 device_id;
uint64 start_time_ns = 0;
uint64 end_time_ns = 0;
uint32 device_id = 0;
uint32 correlation_id = kInvalidCorrelationId;
uint32 thread_id = kInvalidThreadId;
int64 context_id = kInvalidContextId;

View File

@ -291,7 +291,7 @@ void CUPTIAPI FreeCuptiActivityBuffer(CUcontext context, uint32_t stream_id,
void AddKernelEventUponApiExit(CuptiTraceCollector *collector, uint32 device_id,
const CUpti_CallbackData *cbdata,
uint64 start_time, uint64 end_time) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::Kernel;
event.source = CuptiTracerEventSource::DriverCallback;
event.name = cbdata->symbolName ? cbdata->symbolName : cbdata->functionName;
@ -310,7 +310,7 @@ CuptiTracerEvent PopulateMemcpyCallbackEvent(
CuptiTracerEventType type, const CUpti_CallbackData *cbdata,
size_t num_bytes, uint32 src_device, uint32 dst_device, bool async,
uint64 start_time, uint64 end_time) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = type;
event.source = CuptiTracerEventSource::DriverCallback;
event.start_time_ns = start_time;
@ -373,7 +373,7 @@ void AddCudaMallocEventUponApiExit(CuptiTraceCollector *collector,
uint64 start_time, uint64 end_time) {
const cuMemAlloc_v2_params_st *params =
reinterpret_cast<const cuMemAlloc_v2_params_st *>(cbdata->functionParams);
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::MemoryAlloc;
event.source = CuptiTracerEventSource::DriverCallback;
event.name = cbdata->functionName;
@ -392,7 +392,7 @@ void AddGenericEventUponApiExit(CuptiTraceCollector *collector,
uint32 device_id, CUpti_CallbackId cbid,
const CUpti_CallbackData *cbdata,
uint64 start_time, uint64 end_time) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::Generic;
event.source = CuptiTracerEventSource::DriverCallback;
event.name = cbdata->functionName;
@ -407,7 +407,7 @@ void AddGenericEventUponApiExit(CuptiTraceCollector *collector,
void AddKernelActivityEvent(CuptiTraceCollector *collector,
const CUpti_ActivityKernel4 *kernel) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::Kernel;
event.source = CuptiTracerEventSource::Activity;
event.name = kernel->name;
@ -433,7 +433,7 @@ void AddKernelActivityEvent(CuptiTraceCollector *collector,
void AddMemcpyActivityEvent(CuptiTraceCollector *collector,
const CUpti_ActivityMemcpy *memcpy) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
switch (memcpy->copyKind) {
case CUPTI_ACTIVITY_MEMCPY_KIND_HTOD:
event.type = CuptiTracerEventType::MemcpyH2D;
@ -477,7 +477,7 @@ void AddMemcpyActivityEvent(CuptiTraceCollector *collector,
// Invokes callback upon peer-2-peer memcpy between different GPU devices.
void AddMemcpy2ActivityEvent(CuptiTraceCollector *collector,
const CUpti_ActivityMemcpy2 *memcpy2) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::MemcpyP2P;
event.name = "MemcpyP2P";
event.source = CuptiTracerEventSource::Activity;
@ -500,7 +500,7 @@ void AddMemcpy2ActivityEvent(CuptiTraceCollector *collector,
void AddCuptiOverheadActivityEvent(CuptiTraceCollector *collector,
const CUpti_ActivityOverhead *overhead) {
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::Overhead;
event.name = getActivityOverheadKindString(overhead->overheadKind);
event.source = CuptiTracerEventSource::Activity;
@ -538,7 +538,7 @@ void AddUnifiedMemoryActivityEvent(
const CUpti_ActivityUnifiedMemoryCounter2 *record) {
VLOG(3) << "Cuda Unified Memory Activity, kind: " << record->counterKind
<< " src: " << record->srcId << " dst: " << record->dstId;
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::UnifiedMemory;
event.name = getActivityUnifiedMemoryKindString(record->counterKind);
event.source = CuptiTracerEventSource::Activity;
@ -935,7 +935,7 @@ class CudaEventRecorder {
std::string annotation;
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = CuptiTracerEventType::Kernel;
event.source = CuptiTracerEventSource::Activity; // on gpu device.
event.name = record.kernel_name;
@ -963,7 +963,7 @@ class CudaEventRecorder {
GetElapsedTimeUs(record.start_event, stream_info.ctx_info->end_event);
auto elapsed_us = GetElapsedTimeUs(record.start_event, record.stop_event);
CuptiTracerEvent event;
CuptiTracerEvent event{};
event.type = record.type;
event.name = GetTraceEventTypeName(event.type);
event.source = CuptiTracerEventSource::Activity;

View File

@ -288,7 +288,7 @@ TEST_F(DeviceTracerTest, TraceToXSpace) {
++total_events;
});
});
EXPECT_EQ(total_events, 5);
EXPECT_GE(total_events, 5);
}
} // namespace

View File

@ -2,6 +2,7 @@ syntax = "proto3";
package tensorflow.profiler;
// Next ID: 15
message KernelReport {
// Name of the kernel.
string name = 1;
@ -29,6 +30,8 @@ message KernelReport {
string op_name = 12;
// Number of occurrences.
uint32 occurrences = 13;
// Occupancy percentage.
float occupancy_pct = 14;
}
message KernelStatsDb {

View File

@ -466,6 +466,7 @@ tf_cc_test(
":kernel_stats_utils",
"//tensorflow/core:test",
"//tensorflow/core:test_main",
"//tensorflow/core/profiler/internal/gpu:cupti_collector_header",
"//tensorflow/core/profiler/protobuf:kernel_stats_proto_cc",
],
)

View File

@ -42,7 +42,7 @@ const int kMaxNumOfKernels = 1000;
void ParseKernelLaunchParams(absl::string_view xstat_kernel_details,
KernelReport* kernel) {
const std::vector<absl::string_view> params =
absl::StrSplit(xstat_kernel_details, absl::ByAnyChar(":\n"));
absl::StrSplit(xstat_kernel_details, absl::ByAnyChar(" :\n"));
constexpr uint32 kNumDimensions = 3;
for (uint32 dim = 0; dim < kNumDimensions; ++dim) {
@ -53,33 +53,36 @@ void ParseKernelLaunchParams(absl::string_view xstat_kernel_details,
// Process value pairs.
for (uint32 ii = 0; ii < params.size(); ii += 2) {
uint32 value = 0;
if (params[ii] == "registers_per_thread" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
double pct = 0.0;
if (params[ii] == "regs" && absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->set_registers_per_thread(value);
} else if (params[ii] == "static_shared_memory_usage" &&
} else if (params[ii] == "static_shared" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->set_static_shmem_bytes(value);
} else if (params[ii] == "dynamic_shared_memory_usage" &&
} else if (params[ii] == "dynamic_shared" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->set_dynamic_shmem_bytes(value);
} else if (params[ii] == "block_x" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->mutable_block_dim()->Set(0, value);
} else if (params[ii] == "block_y" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->mutable_block_dim()->Set(1, value);
} else if (params[ii] == "block_z" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->mutable_block_dim()->Set(2, value);
} else if (params[ii] == "grid_x" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->mutable_grid_dim()->Set(0, value);
} else if (params[ii] == "grid_y" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->mutable_grid_dim()->Set(1, value);
} else if (params[ii] == "grid_z" &&
absl::SimpleAtoi(params[ii + 1], &value)) {
kernel->mutable_grid_dim()->Set(2, value);
} else if (params[ii] == "block") {
const std::vector<absl::string_view>& block =
absl::StrSplit(params[ii + 1], ',');
uint32 tmp[3];
if (block.size() == 3 && absl::SimpleAtoi(block[0], &tmp[0]) &&
absl::SimpleAtoi(block[1], &tmp[1]) &&
absl::SimpleAtoi(block[2], &tmp[2])) {
std::copy_n(tmp, 3, kernel->mutable_block_dim()->begin());
}
} else if (params[ii] == "grid") {
const std::vector<absl::string_view>& grid =
absl::StrSplit(params[ii + 1], ',');
uint32 tmp[3];
if (grid.size() == 3 && absl::SimpleAtoi(grid[0], &tmp[0]) &&
absl::SimpleAtoi(grid[1], &tmp[1]) &&
absl::SimpleAtoi(grid[2], &tmp[2])) {
std::copy_n(tmp, 3, kernel->mutable_grid_dim()->begin());
}
} else if (params[ii] == "occ_pct" &&
absl::SimpleAtod(params[ii + 1], &pct)) {
kernel->set_occupancy_pct(pct);
}
}
}

View File

@ -26,7 +26,7 @@ limitations under the License.
namespace tensorflow {
namespace profiler {
// Populates kernel launch information from a KernelDetails XStat.
// Populates kernel launch information from a kKernelDetails XStat.
void ParseKernelLaunchParams(absl::string_view xstat_kernel_details,
KernelReport* kernel);

View File

@ -16,6 +16,7 @@ limitations under the License.
#include "tensorflow/core/profiler/utils/kernel_stats_utils.h"
#include "tensorflow/core/platform/test.h"
#include "tensorflow/core/profiler/internal/gpu/cupti_collector.h"
#include "tensorflow/core/profiler/protobuf/kernel_stats.pb.h"
namespace tensorflow {
@ -66,6 +67,33 @@ TEST(KernelStatsUtilsTest, TestGroupKernelReportsByOpName) {
EXPECT_EQ(op2_stats.tensor_core_duration_ns, 0);
}
TEST(KernelStatsUtilsTest, KernelDetailsXStatParser) {
KernelDetails kernel_info;
kernel_info.registers_per_thread = 10;
kernel_info.static_shared_memory_usage = 128;
kernel_info.dynamic_shared_memory_usage = 256;
kernel_info.block_x = 32;
kernel_info.block_y = 8;
kernel_info.block_z = 4;
kernel_info.grid_x = 3;
kernel_info.grid_y = 2;
kernel_info.grid_z = 1;
const double occupancy_pct = 50.0;
std::string xstat_kernel_details = ToXStat(kernel_info, occupancy_pct);
KernelReport kernel;
ParseKernelLaunchParams(xstat_kernel_details, &kernel);
// Verifies that the parser can parse kKernelDetails XStat.
EXPECT_EQ(kernel.registers_per_thread(), 10);
EXPECT_EQ(kernel.static_shmem_bytes(), 128);
EXPECT_EQ(kernel.dynamic_shmem_bytes(), 256);
EXPECT_EQ(kernel.block_dim()[0], 32);
EXPECT_EQ(kernel.block_dim()[1], 8);
EXPECT_EQ(kernel.block_dim()[2], 4);
EXPECT_EQ(kernel.grid_dim()[0], 3);
EXPECT_EQ(kernel.grid_dim()[1], 2);
EXPECT_EQ(kernel.grid_dim()[2], 1);
}
} // namespace
} // namespace profiler
} // namespace tensorflow

View File

@ -210,6 +210,10 @@ const StatTypeMap& GetStatTypeMap() {
{"batch_size_after_padding", kBatchSizeAfterPadding},
{"padding_amount", kPaddingAmount},
{"batching_input_task_size", kBatchingInputTaskSize},
// GPU related metrics.
{"theoretical_occupancy_pct", kTheoreticalOccupancyPct},
{"occupancy_min_grid_size", kOccupancyMinGridSize},
{"occupancy_suggested_block_size", kOccupancySuggestedBlockSize},
});
DCHECK_EQ(stat_type_map->size(), kNumStatTypes);
return *stat_type_map;

View File

@ -199,7 +199,11 @@ enum StatType {
kBatchSizeAfterPadding,
kPaddingAmount,
kBatchingInputTaskSize,
kLastStatType = kBatchingInputTaskSize,
// GPU occupancy metrics
kTheoreticalOccupancyPct,
kOccupancyMinGridSize,
kOccupancySuggestedBlockSize,
kLastStatType = kOccupancySuggestedBlockSize,
};
inline std::string GpuPlaneName(int32 device_ordinal) {