| // Copyright 2026 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "iree/tooling/profile/explain.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "iree/tooling/profile/dispatch.h" |
| #include "iree/tooling/profile/memory.h" |
| #include "iree/tooling/profile/reader.h" |
| #include "iree/tooling/profile/summary.h" |
| |
| #define IREE_PROFILE_EXPLAIN_TOP_FUNCTION_COUNT 10 |
| |
| typedef enum iree_profile_explain_timing_source_e { |
| // Device dispatch event timestamps. |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_DEVICE_DISPATCH = 0, |
| // Host execution event timestamps for CPU/local dispatch spans. |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_HOST_EXECUTION = 1, |
| } iree_profile_explain_timing_source_t; |
| |
| typedef struct iree_profile_explain_function_rank_t { |
| // Timing source used to rank this function. |
| iree_profile_explain_timing_source_t timing_source; |
| // Session-local physical device ordinal for this function aggregate. |
| uint32_t physical_device_ordinal; |
| // Producer-local executable identifier for this function aggregate. |
| uint64_t executable_id; |
| // Function ordinal for this function aggregate. |
| uint32_t function_ordinal; |
| // Total dispatch count for this function aggregate. |
| uint64_t dispatch_count; |
| // Valid dispatch count for this function aggregate. |
| uint64_t valid_count; |
| // Invalid dispatch count for this function aggregate. |
| uint64_t invalid_count; |
| // Maximum valid dispatch duration in raw device ticks. |
| uint64_t maximum_ticks; |
| // Total valid dispatch duration in raw device ticks. |
| uint64_t total_ticks; |
| // Total tiles reported by host dispatch spans, or 0 when unavailable. |
| uint64_t total_tile_count; |
| // Sum of valid per-tile duration totals in nanoseconds, or 0 when absent. |
| int64_t total_tile_duration_sum_ns; |
| // Average valid dispatch duration in nanoseconds when available. |
| double average_ns; |
| // Maximum valid dispatch duration in nanoseconds when available. |
| int64_t maximum_ns; |
| // Total valid dispatch duration in nanoseconds when available. |
| int64_t total_ns; |
| // True when nanosecond duration values are available for this rank. |
| bool has_duration_ns; |
| } iree_profile_explain_function_rank_t; |
| |
| typedef struct iree_profile_explain_interval_t { |
| // Inclusive dispatch start tick for this interval. |
| uint64_t start_tick; |
| // Exclusive dispatch end tick for this interval. |
| uint64_t end_tick; |
| } iree_profile_explain_interval_t; |
| |
| typedef struct iree_profile_explain_queue_summary_t { |
| // Number of queue submissions with at least one matching dispatch aggregate. |
| uint64_t submission_count; |
| // Number of queue submissions with at least one valid dispatch timestamp. |
| uint64_t valid_submission_count; |
| // Number of queue submissions with at least one invalid dispatch timestamp. |
| uint64_t invalid_submission_count; |
| // Unioned busy span for valid dispatch intervals in raw device ticks. |
| double busy_ticks; |
| // Sum of valid dispatch durations in raw device ticks. |
| uint64_t total_dispatch_ticks; |
| // Number of observed gaps between unioned valid dispatch intervals. |
| uint64_t gap_count; |
| // Sum of observed gaps in raw device ticks. |
| double total_gap_ticks; |
| // Largest observed gap in raw device ticks. |
| double max_gap_ticks; |
| } iree_profile_explain_queue_summary_t; |
| |
| typedef struct iree_profile_explain_device_row_t { |
| // Device metadata row represented by these derived metrics. |
| const iree_profile_model_device_t* device; |
| // True when the device has a driver host CPU timestamp clock fit. |
| bool has_clock_fit; |
| // Nanoseconds per device tick when |has_clock_fit| is true. |
| double nanoseconds_per_tick; |
| // Device tick frequency in Hz when |has_clock_fit| is true. |
| double tick_frequency_hz; |
| // Visible span covered by valid queue dispatch intervals in device ticks. |
| double visible_span_ticks; |
| // Sum of valid dispatch durations for this device in device ticks. |
| uint64_t total_dispatch_ticks; |
| // Ratio of summed dispatch time to the visible queue span. |
| double active_over_visible_ratio; |
| // True when |total_dispatch_time_ns| was derived from the clock fit. |
| bool has_total_dispatch_time_ns; |
| // Sum of valid dispatch durations mapped to nanoseconds. |
| int64_t total_dispatch_time_ns; |
| } iree_profile_explain_device_row_t; |
| |
| typedef struct iree_profile_explain_queue_row_t { |
| // Queue metadata row represented by these derived metrics. |
| const iree_hal_profile_queue_record_t* queue; |
| // Aggregate dispatch timing and gap summary for the queue. |
| iree_profile_explain_queue_summary_t summary; |
| // True when the queue device has a driver host CPU timestamp clock fit. |
| bool has_clock_fit; |
| // Nanoseconds per device tick when |has_clock_fit| is true. |
| double nanoseconds_per_tick; |
| // True when |total_dispatch_time_ns| was derived from the clock fit. |
| bool has_total_dispatch_time_ns; |
| // Sum of valid dispatch durations mapped to nanoseconds. |
| int64_t total_dispatch_time_ns; |
| // True when queue event records were present for gap analysis context. |
| bool gap_analysis_available; |
| } iree_profile_explain_queue_row_t; |
| |
| typedef struct iree_profile_explain_queue_operation_totals_t { |
| // Event counts indexed by iree_hal_profile_queue_event_type_t. |
| uint64_t event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_HOST_CALL + 1]; |
| // Transfer payload bytes indexed by iree_hal_profile_queue_event_type_t. |
| uint64_t payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_HOST_CALL + 1]; |
| // Event counts indexed by iree_hal_profile_queue_dependency_strategy_t. |
| uint64_t strategy_counts |
| [IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_SOFTWARE_DEFER + 1]; |
| } iree_profile_explain_queue_operation_totals_t; |
| |
| static double iree_profile_explain_function_rank_score( |
| const iree_profile_explain_function_rank_t* rank) { |
| return rank->has_duration_ns ? (double)rank->total_ns |
| : (double)rank->total_ticks; |
| } |
| |
| static int iree_profile_explain_compare_function_rank(const void* lhs, |
| const void* rhs) { |
| const iree_profile_explain_function_rank_t* a = |
| (const iree_profile_explain_function_rank_t*)lhs; |
| const iree_profile_explain_function_rank_t* b = |
| (const iree_profile_explain_function_rank_t*)rhs; |
| const double a_score = iree_profile_explain_function_rank_score(a); |
| const double b_score = iree_profile_explain_function_rank_score(b); |
| if (a_score < b_score) return 1; |
| if (a_score > b_score) return -1; |
| return 0; |
| } |
| |
| static int iree_profile_explain_compare_top_event(const void* lhs, |
| const void* rhs) { |
| const iree_profile_dispatch_top_event_t* a = |
| (const iree_profile_dispatch_top_event_t*)lhs; |
| const iree_profile_dispatch_top_event_t* b = |
| (const iree_profile_dispatch_top_event_t*)rhs; |
| if (a->duration_ticks < b->duration_ticks) return 1; |
| if (a->duration_ticks > b->duration_ticks) return -1; |
| return 0; |
| } |
| |
| static int iree_profile_explain_compare_top_host_event(const void* lhs, |
| const void* rhs) { |
| const iree_profile_host_dispatch_top_event_t* a = |
| (const iree_profile_host_dispatch_top_event_t*)lhs; |
| const iree_profile_host_dispatch_top_event_t* b = |
| (const iree_profile_host_dispatch_top_event_t*)rhs; |
| if (a->duration_ns < b->duration_ns) return 1; |
| if (a->duration_ns > b->duration_ns) return -1; |
| return 0; |
| } |
| |
| static int iree_profile_explain_compare_interval(const void* lhs, |
| const void* rhs) { |
| const iree_profile_explain_interval_t* a = |
| (const iree_profile_explain_interval_t*)lhs; |
| const iree_profile_explain_interval_t* b = |
| (const iree_profile_explain_interval_t*)rhs; |
| if (a->start_tick < b->start_tick) return -1; |
| if (a->start_tick > b->start_tick) return 1; |
| if (a->end_tick < b->end_tick) return -1; |
| if (a->end_tick > b->end_tick) return 1; |
| return 0; |
| } |
| |
| static bool iree_profile_explain_try_fit_driver_host_cpu_clock( |
| const iree_profile_dispatch_context_t* context, |
| uint32_t physical_device_ordinal, |
| iree_profile_model_clock_fit_t* out_clock_fit) { |
| const iree_profile_model_device_t* device = |
| iree_profile_model_find_device(&context->model, physical_device_ordinal); |
| return iree_profile_model_device_try_fit_clock_exact( |
| device, IREE_PROFILE_MODEL_CLOCK_TIME_DOMAIN_HOST_CPU_TIMESTAMP_NS, |
| out_clock_fit); |
| } |
| |
| static const char* iree_profile_explain_timing_source_string( |
| iree_profile_explain_timing_source_t timing_source) { |
| switch (timing_source) { |
| case IREE_PROFILE_EXPLAIN_TIMING_SOURCE_DEVICE_DISPATCH: |
| return "device_dispatch_event"; |
| case IREE_PROFILE_EXPLAIN_TIMING_SOURCE_HOST_EXECUTION: |
| return "host_execution_event"; |
| } |
| return "unknown"; |
| } |
| |
| static iree_status_t iree_profile_explain_collect_function_ranks( |
| const iree_profile_dispatch_context_t* context, |
| iree_allocator_t host_allocator, |
| iree_profile_explain_function_rank_t** out_ranks, |
| iree_host_size_t* out_rank_count) { |
| *out_ranks = NULL; |
| *out_rank_count = 0; |
| const bool use_host_execution = context->valid_dispatch_count == 0 && |
| context->valid_host_dispatch_count != 0; |
| const iree_host_size_t candidate_count = |
| use_host_execution ? context->host_dispatch_aggregate_count |
| : context->aggregate_count; |
| if (candidate_count == 0) return iree_ok_status(); |
| |
| iree_profile_explain_function_rank_t* ranks = NULL; |
| iree_status_t status = iree_allocator_malloc_array_uninitialized( |
| host_allocator, candidate_count, sizeof(ranks[0]), (void**)&ranks); |
| |
| iree_host_size_t rank_count = 0; |
| if (iree_status_is_ok(status)) { |
| if (use_host_execution) { |
| for (iree_host_size_t i = 0; i < context->host_dispatch_aggregate_count; |
| ++i) { |
| const iree_profile_host_dispatch_aggregate_t* aggregate = |
| &context->host_dispatch_aggregates[i]; |
| if (aggregate->valid_count == 0) continue; |
| iree_profile_explain_function_rank_t* rank = &ranks[rank_count++]; |
| memset(rank, 0, sizeof(*rank)); |
| rank->timing_source = IREE_PROFILE_EXPLAIN_TIMING_SOURCE_HOST_EXECUTION; |
| rank->physical_device_ordinal = aggregate->physical_device_ordinal; |
| rank->executable_id = aggregate->executable_id; |
| rank->function_ordinal = aggregate->function_ordinal; |
| rank->dispatch_count = aggregate->dispatch_count; |
| rank->valid_count = aggregate->valid_count; |
| rank->invalid_count = aggregate->invalid_count; |
| rank->maximum_ns = aggregate->maximum_ns; |
| rank->total_ns = aggregate->total_ns; |
| rank->average_ns = aggregate->mean_ns; |
| rank->total_tile_count = aggregate->total_tile_count; |
| rank->total_tile_duration_sum_ns = |
| aggregate->total_tile_duration_sum_ns; |
| rank->has_duration_ns = true; |
| } |
| } else { |
| for (iree_host_size_t i = 0; i < context->aggregate_count; ++i) { |
| const iree_profile_dispatch_aggregate_t* aggregate = |
| &context->aggregates[i]; |
| if (aggregate->valid_count == 0) continue; |
| |
| const iree_profile_model_device_t* device = |
| iree_profile_model_find_device(&context->model, |
| aggregate->physical_device_ordinal); |
| iree_profile_model_clock_fit_t clock_fit; |
| const bool has_clock_fit = |
| iree_profile_model_device_try_fit_clock_exact( |
| device, |
| IREE_PROFILE_MODEL_CLOCK_TIME_DOMAIN_HOST_CPU_TIMESTAMP_NS, |
| &clock_fit); |
| |
| iree_profile_explain_function_rank_t* rank = &ranks[rank_count++]; |
| memset(rank, 0, sizeof(*rank)); |
| rank->timing_source = |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_DEVICE_DISPATCH; |
| rank->physical_device_ordinal = aggregate->physical_device_ordinal; |
| rank->executable_id = aggregate->executable_id; |
| rank->function_ordinal = aggregate->function_ordinal; |
| rank->dispatch_count = aggregate->dispatch_count; |
| rank->valid_count = aggregate->valid_count; |
| rank->invalid_count = aggregate->invalid_count; |
| rank->maximum_ticks = aggregate->maximum_ticks; |
| rank->total_ticks = aggregate->total_ticks; |
| if (has_clock_fit && |
| iree_profile_model_clock_fit_scale_ticks_to_ns( |
| &clock_fit, aggregate->maximum_ticks, &rank->maximum_ns) && |
| iree_profile_model_clock_fit_scale_ticks_to_ns( |
| &clock_fit, aggregate->total_ticks, &rank->total_ns)) { |
| rank->has_duration_ns = true; |
| rank->average_ns = |
| aggregate->mean_ticks * |
| iree_profile_model_clock_fit_ns_per_tick(&clock_fit); |
| } |
| } |
| } |
| } |
| |
| if (iree_status_is_ok(status)) { |
| qsort(ranks, rank_count, sizeof(ranks[0]), |
| iree_profile_explain_compare_function_rank); |
| *out_ranks = ranks; |
| *out_rank_count = rank_count; |
| } else { |
| iree_allocator_free(host_allocator, ranks); |
| } |
| return status; |
| } |
| |
| static void iree_profile_explain_compute_queue_operation_totals( |
| const iree_profile_dispatch_context_t* context, |
| iree_profile_explain_queue_operation_totals_t* out_totals) { |
| memset(out_totals, 0, sizeof(*out_totals)); |
| for (iree_host_size_t i = 0; i < context->queue_query.queue_event_count; |
| ++i) { |
| const iree_hal_profile_queue_event_t* event = |
| &context->queue_query.queue_events[i].record; |
| if (event->type <= IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_HOST_CALL) { |
| ++out_totals->event_counts[event->type]; |
| out_totals->payload_bytes[event->type] += event->payload_length; |
| } |
| if (event->dependency_strategy <= |
| IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_SOFTWARE_DEFER) { |
| ++out_totals->strategy_counts[event->dependency_strategy]; |
| } |
| } |
| } |
| |
| static double iree_profile_explain_visible_span_ticks( |
| const iree_profile_dispatch_context_t* context, |
| uint32_t physical_device_ordinal) { |
| uint64_t earliest_start_tick = UINT64_MAX; |
| uint64_t latest_end_tick = 0; |
| for (iree_host_size_t i = 0; i < context->queue_aggregate_count; ++i) { |
| const iree_profile_dispatch_queue_aggregate_t* aggregate = |
| &context->queue_aggregates[i]; |
| if (aggregate->physical_device_ordinal != physical_device_ordinal || |
| aggregate->valid_count == 0) { |
| continue; |
| } |
| earliest_start_tick = |
| iree_min(earliest_start_tick, aggregate->earliest_start_tick); |
| latest_end_tick = iree_max(latest_end_tick, aggregate->latest_end_tick); |
| } |
| return iree_profile_model_span_ticks(earliest_start_tick, latest_end_tick); |
| } |
| |
| static bool iree_profile_explain_accumulate_ticks(uint64_t* total_ticks, |
| uint64_t duration_ticks) { |
| if (duration_ticks > UINT64_MAX - *total_ticks) return false; |
| *total_ticks += duration_ticks; |
| return true; |
| } |
| |
| static iree_status_t iree_profile_explain_total_dispatch_ticks_for_device( |
| const iree_profile_dispatch_context_t* context, |
| uint32_t physical_device_ordinal, uint64_t* out_total_ticks) { |
| *out_total_ticks = 0; |
| for (iree_host_size_t i = 0; i < context->aggregate_count; ++i) { |
| const iree_profile_dispatch_aggregate_t* aggregate = |
| &context->aggregates[i]; |
| if (aggregate->physical_device_ordinal == physical_device_ordinal) { |
| if (!iree_profile_explain_accumulate_ticks(out_total_ticks, |
| aggregate->total_ticks)) { |
| return iree_make_status( |
| IREE_STATUS_OUT_OF_RANGE, |
| "device summary dispatch tick total overflow device=%u", |
| physical_device_ordinal); |
| } |
| } |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_resolve_device_row( |
| const iree_profile_dispatch_context_t* context, |
| const iree_profile_model_device_t* device, |
| iree_profile_explain_device_row_t* out_row) { |
| memset(out_row, 0, sizeof(*out_row)); |
| out_row->device = device; |
| iree_profile_model_clock_fit_t clock_fit; |
| out_row->has_clock_fit = iree_profile_model_device_try_fit_clock_exact( |
| device, IREE_PROFILE_MODEL_CLOCK_TIME_DOMAIN_HOST_CPU_TIMESTAMP_NS, |
| &clock_fit); |
| if (out_row->has_clock_fit) { |
| out_row->nanoseconds_per_tick = |
| iree_profile_model_clock_fit_ns_per_tick(&clock_fit); |
| out_row->tick_frequency_hz = |
| iree_profile_model_clock_fit_tick_frequency_hz(&clock_fit); |
| } |
| out_row->visible_span_ticks = iree_profile_explain_visible_span_ticks( |
| context, device->physical_device_ordinal); |
| IREE_RETURN_IF_ERROR(iree_profile_explain_total_dispatch_ticks_for_device( |
| context, device->physical_device_ordinal, |
| &out_row->total_dispatch_ticks)); |
| out_row->active_over_visible_ratio = |
| out_row->visible_span_ticks > 0.0 |
| ? (double)out_row->total_dispatch_ticks / out_row->visible_span_ticks |
| : 0.0; |
| out_row->has_total_dispatch_time_ns = |
| out_row->has_clock_fit && out_row->total_dispatch_ticks != 0 && |
| iree_profile_model_clock_fit_scale_ticks_to_ns( |
| &clock_fit, out_row->total_dispatch_ticks, |
| &out_row->total_dispatch_time_ns); |
| return iree_ok_status(); |
| } |
| |
| static bool iree_profile_explain_queue_aggregate_matches( |
| const iree_hal_profile_queue_record_t* queue, |
| const iree_profile_dispatch_queue_aggregate_t* aggregate) { |
| return aggregate->physical_device_ordinal == queue->physical_device_ordinal && |
| aggregate->queue_ordinal == queue->queue_ordinal && |
| aggregate->stream_id == queue->stream_id; |
| } |
| |
| static iree_status_t iree_profile_explain_collect_queue_intervals( |
| const iree_profile_dispatch_context_t* context, |
| const iree_hal_profile_queue_record_t* queue, |
| iree_profile_explain_interval_t* intervals, |
| iree_host_size_t* inout_interval_count, |
| iree_profile_explain_queue_summary_t* summary) { |
| for (iree_host_size_t i = 0; i < context->queue_aggregate_count; ++i) { |
| const iree_profile_dispatch_queue_aggregate_t* aggregate = |
| &context->queue_aggregates[i]; |
| if (!iree_profile_explain_queue_aggregate_matches(queue, aggregate)) { |
| continue; |
| } |
| |
| ++summary->submission_count; |
| if (!iree_profile_explain_accumulate_ticks(&summary->total_dispatch_ticks, |
| aggregate->total_ticks)) { |
| return iree_make_status( |
| IREE_STATUS_OUT_OF_RANGE, |
| "queue summary dispatch tick total overflow device=%u queue=%u" |
| " stream=%" PRIu64, |
| queue->physical_device_ordinal, queue->queue_ordinal, |
| queue->stream_id); |
| } |
| if (aggregate->valid_count != 0) { |
| ++summary->valid_submission_count; |
| intervals[(*inout_interval_count)++] = (iree_profile_explain_interval_t){ |
| aggregate->earliest_start_tick, aggregate->latest_end_tick}; |
| } |
| if (aggregate->invalid_count != 0) { |
| ++summary->invalid_submission_count; |
| } |
| } |
| return iree_ok_status(); |
| } |
| |
| static void iree_profile_explain_merge_queue_intervals( |
| iree_profile_explain_interval_t* intervals, iree_host_size_t interval_count, |
| iree_profile_explain_queue_summary_t* summary) { |
| if (interval_count == 0) return; |
| qsort(intervals, interval_count, sizeof(intervals[0]), |
| iree_profile_explain_compare_interval); |
| |
| uint64_t merged_start_tick = intervals[0].start_tick; |
| uint64_t merged_end_tick = intervals[0].end_tick; |
| for (iree_host_size_t i = 1; i < interval_count; ++i) { |
| if (intervals[i].start_tick <= merged_end_tick) { |
| merged_end_tick = iree_max(merged_end_tick, intervals[i].end_tick); |
| continue; |
| } |
| summary->busy_ticks += (double)(merged_end_tick - merged_start_tick); |
| const uint64_t gap_ticks = intervals[i].start_tick - merged_end_tick; |
| ++summary->gap_count; |
| summary->total_gap_ticks += (double)gap_ticks; |
| summary->max_gap_ticks = |
| iree_max(summary->max_gap_ticks, (double)gap_ticks); |
| merged_start_tick = intervals[i].start_tick; |
| merged_end_tick = intervals[i].end_tick; |
| } |
| summary->busy_ticks += (double)(merged_end_tick - merged_start_tick); |
| } |
| |
| static iree_status_t iree_profile_explain_summarize_queue( |
| const iree_profile_dispatch_context_t* context, |
| const iree_hal_profile_queue_record_t* queue, |
| iree_allocator_t host_allocator, |
| iree_profile_explain_queue_summary_t* out_summary) { |
| memset(out_summary, 0, sizeof(*out_summary)); |
| if (context->queue_aggregate_count == 0) return iree_ok_status(); |
| |
| iree_profile_explain_interval_t* intervals = NULL; |
| iree_status_t status = iree_allocator_malloc_array_uninitialized( |
| host_allocator, context->queue_aggregate_count, sizeof(intervals[0]), |
| (void**)&intervals); |
| |
| iree_host_size_t interval_count = 0; |
| if (iree_status_is_ok(status)) { |
| status = iree_profile_explain_collect_queue_intervals( |
| context, queue, intervals, &interval_count, out_summary); |
| } |
| if (iree_status_is_ok(status)) { |
| iree_profile_explain_merge_queue_intervals(intervals, interval_count, |
| out_summary); |
| } |
| |
| iree_allocator_free(host_allocator, intervals); |
| return status; |
| } |
| |
| static iree_status_t iree_profile_explain_resolve_queue_row( |
| const iree_profile_dispatch_context_t* context, |
| const iree_hal_profile_queue_record_t* queue, |
| iree_allocator_t host_allocator, |
| iree_profile_explain_queue_row_t* out_row) { |
| memset(out_row, 0, sizeof(*out_row)); |
| out_row->queue = queue; |
| IREE_RETURN_IF_ERROR(iree_profile_explain_summarize_queue( |
| context, queue, host_allocator, &out_row->summary)); |
| |
| iree_profile_model_clock_fit_t clock_fit; |
| out_row->has_clock_fit = iree_profile_explain_try_fit_driver_host_cpu_clock( |
| context, queue->physical_device_ordinal, &clock_fit); |
| if (out_row->has_clock_fit) { |
| out_row->nanoseconds_per_tick = |
| iree_profile_model_clock_fit_ns_per_tick(&clock_fit); |
| } |
| out_row->has_total_dispatch_time_ns = |
| out_row->has_clock_fit && out_row->summary.valid_submission_count != 0 && |
| iree_profile_model_clock_fit_scale_ticks_to_ns( |
| &clock_fit, out_row->summary.total_dispatch_ticks, |
| &out_row->total_dispatch_time_ns); |
| out_row->gap_analysis_available = context->queue_query.queue_event_count != 0; |
| return iree_ok_status(); |
| } |
| |
| static void iree_profile_explain_print_hint_text(const char* severity, |
| const char* category, |
| const char* message, |
| FILE* file) { |
| fprintf(file, " [%s] %s: %s\n", severity, category, message); |
| } |
| |
| static void iree_profile_explain_print_hint_jsonl(const char* severity, |
| const char* category, |
| const char* message, |
| FILE* file) { |
| fprintf(file, "{\"type\":\"explain_hint\",\"severity\":"); |
| iree_profile_fprint_json_string(file, iree_make_cstring_view(severity)); |
| fprintf(file, ",\"category\":"); |
| iree_profile_fprint_json_string(file, iree_make_cstring_view(category)); |
| fprintf(file, ",\"message\":"); |
| iree_profile_fprint_json_string(file, iree_make_cstring_view(message)); |
| fputs("}\n", file); |
| } |
| |
| static bool iree_profile_explain_has_pool_waits( |
| const iree_profile_memory_context_t* memory_context) { |
| for (iree_host_size_t i = 0; i < memory_context->device_count; ++i) { |
| if (memory_context->devices[i].pool_wait_count != 0) return true; |
| } |
| return false; |
| } |
| |
| static void iree_profile_explain_copy_sorted_top_dispatches( |
| const iree_profile_dispatch_context_t* dispatch_context, |
| iree_profile_dispatch_top_event_t* out_top_dispatches) { |
| memcpy(out_top_dispatches, dispatch_context->top_dispatches, |
| dispatch_context->top_dispatch_count * sizeof(out_top_dispatches[0])); |
| qsort(out_top_dispatches, dispatch_context->top_dispatch_count, |
| sizeof(out_top_dispatches[0]), iree_profile_explain_compare_top_event); |
| } |
| |
| static void iree_profile_explain_copy_sorted_top_host_dispatches( |
| const iree_profile_dispatch_context_t* dispatch_context, |
| iree_profile_host_dispatch_top_event_t* out_top_dispatches) { |
| memcpy(out_top_dispatches, dispatch_context->top_host_dispatches, |
| dispatch_context->top_host_dispatch_count * |
| sizeof(out_top_dispatches[0])); |
| qsort(out_top_dispatches, dispatch_context->top_host_dispatch_count, |
| sizeof(out_top_dispatches[0]), |
| iree_profile_explain_compare_top_host_event); |
| } |
| |
| static void iree_profile_explain_print_text_header( |
| const iree_profile_summary_t* summary, |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_memory_context_t* memory_context, FILE* file) { |
| fprintf(file, "IREE HAL profile explain\n"); |
| fprintf(file, |
| "health: records=%" PRIu64 " chunks=%" PRIu64 |
| " unknown_records=%" PRIu64 " unknown_chunks=%" PRIu64 |
| " truncated_chunks=%" PRIu64 " dropped_records=%" PRIu64 "\n", |
| summary->file_record_count, summary->chunk_count, |
| summary->unknown_record_count, summary->unknown_chunk_count, |
| summary->truncated_chunk_count, summary->dropped_record_count); |
| if (summary->non_ok_session_end_count != 0) { |
| fprintf(file, |
| "session_status: non_ok_session_end=%" PRIu64 |
| " first_session_id=%" PRIu64 " first_stream_id=%" PRIu64 |
| " first_event_id=%" PRIu64 |
| " first_status_code=%u" |
| " first_status=%s\n", |
| summary->non_ok_session_end_count, summary->first_non_ok_session_id, |
| summary->first_non_ok_stream_id, summary->first_non_ok_event_id, |
| (uint32_t)summary->first_non_ok_session_status_code, |
| iree_profile_status_code_name( |
| (uint32_t)summary->first_non_ok_session_status_code)); |
| } |
| fprintf(file, |
| "coverage: dispatches=%" PRIu64 " valid=%" PRIu64 " invalid=%" PRIu64 |
| " host_dispatches=%" PRIu64 " valid_host=%" PRIu64 |
| " invalid_host=%" PRIu64 " queues=%" PRIhsz " queue_events=%" PRIhsz |
| " command_buffers=%" PRIhsz " command_operations=%" PRIhsz |
| " memory_events=%" PRIu64 "\n", |
| dispatch_context->matched_dispatch_count, |
| dispatch_context->valid_dispatch_count, |
| dispatch_context->invalid_dispatch_count, |
| dispatch_context->matched_host_dispatch_count, |
| dispatch_context->valid_host_dispatch_count, |
| dispatch_context->invalid_host_dispatch_count, |
| dispatch_context->model.queue_count, |
| dispatch_context->queue_query.queue_event_count, |
| dispatch_context->model.command_buffer_count, |
| dispatch_context->model.command_operation_count, |
| memory_context->matched_event_count); |
| } |
| |
| static iree_status_t iree_profile_explain_print_text_devices( |
| const iree_profile_dispatch_context_t* dispatch_context, FILE* file) { |
| fprintf(file, "devices:\n"); |
| for (iree_host_size_t i = 0; i < dispatch_context->model.device_count; ++i) { |
| const iree_profile_model_device_t* device = |
| &dispatch_context->model.devices[i]; |
| iree_profile_explain_device_row_t row; |
| IREE_RETURN_IF_ERROR(iree_profile_explain_resolve_device_row( |
| dispatch_context, device, &row)); |
| fprintf(file, |
| " device[%u]: clock_fit=%s clock_samples=%" PRIu64 |
| " visible_span_ticks=%.3f active_dispatch_ticks=%" PRIu64 |
| " active_over_visible=%.3f\n", |
| row.device->physical_device_ordinal, |
| row.has_clock_fit ? "true" : "false", |
| row.device->clock_sample_count, row.visible_span_ticks, |
| row.total_dispatch_ticks, row.active_over_visible_ratio); |
| if (row.has_total_dispatch_time_ns) { |
| fprintf(file, |
| " visible_span_ns=%.3f active_dispatch_ns=%" PRId64 |
| " tick_frequency_hz=%.3f\n", |
| row.visible_span_ticks * row.nanoseconds_per_tick, |
| row.total_dispatch_time_ns, row.tick_frequency_hz); |
| } |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_print_text_queues( |
| const iree_profile_dispatch_context_t* dispatch_context, |
| iree_allocator_t host_allocator, FILE* file) { |
| fprintf(file, "queues:\n"); |
| for (iree_host_size_t i = 0; i < dispatch_context->model.queue_count; ++i) { |
| const iree_hal_profile_queue_record_t* queue = |
| &dispatch_context->model.queues[i].record; |
| iree_profile_explain_queue_row_t row; |
| IREE_RETURN_IF_ERROR(iree_profile_explain_resolve_queue_row( |
| dispatch_context, queue, host_allocator, &row)); |
| fprintf(file, |
| " queue device=%u ordinal=%u stream=%" PRIu64 |
| ": submissions=%" PRIu64 " valid=%" PRIu64 " invalid=%" PRIu64 |
| " busy_ticks=%.3f" |
| " total_dispatch_ticks=%" PRIu64, |
| row.queue->physical_device_ordinal, row.queue->queue_ordinal, |
| row.queue->stream_id, row.summary.submission_count, |
| row.summary.valid_submission_count, |
| row.summary.invalid_submission_count, row.summary.busy_ticks, |
| row.summary.total_dispatch_ticks); |
| if (row.has_total_dispatch_time_ns) { |
| fprintf(file, " busy_ns=%.3f total_dispatch_ns=%" PRId64, |
| row.summary.busy_ticks * row.nanoseconds_per_tick, |
| row.total_dispatch_time_ns); |
| } |
| if (row.gap_analysis_available) { |
| fprintf(file, " gaps=%" PRIu64 " total_gap_ticks=%.3f max_gap_ticks=%.3f", |
| row.summary.gap_count, row.summary.total_gap_ticks, |
| row.summary.max_gap_ticks); |
| if (row.has_clock_fit) { |
| fprintf(file, " total_gap_ns=%.3f max_gap_ns=%.3f", |
| row.summary.total_gap_ticks * row.nanoseconds_per_tick, |
| row.summary.max_gap_ticks * row.nanoseconds_per_tick); |
| } |
| } else { |
| fprintf(file, " gaps=unavailable_without_queue_events"); |
| } |
| fputc('\n', file); |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_print_text_top_functions( |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_explain_function_rank_t* function_ranks, |
| iree_host_size_t function_rank_count, FILE* file) { |
| fprintf(file, "top functions by total dispatch time:\n"); |
| const iree_host_size_t top_function_count = |
| iree_min(function_rank_count, |
| (iree_host_size_t)IREE_PROFILE_EXPLAIN_TOP_FUNCTION_COUNT); |
| for (iree_host_size_t i = 0; i < top_function_count; ++i) { |
| const iree_profile_explain_function_rank_t* rank = &function_ranks[i]; |
| char numeric_buffer[128]; |
| iree_string_view_t key = iree_string_view_empty(); |
| IREE_RETURN_IF_ERROR(iree_profile_model_resolve_dispatch_key( |
| &dispatch_context->model, rank->physical_device_ordinal, |
| rank->executable_id, rank->function_ordinal, numeric_buffer, |
| sizeof(numeric_buffer), &key)); |
| fprintf(file, |
| " #%" PRIhsz " %.*s device=%u executable=%" PRIu64 |
| " function=%u count=%" PRIu64 " valid=%" PRIu64 " invalid=%" PRIu64 |
| " timing_source=%s", |
| i + 1, (int)key.size, key.data, rank->physical_device_ordinal, |
| rank->executable_id, rank->function_ordinal, rank->dispatch_count, |
| rank->valid_count, rank->invalid_count, |
| iree_profile_explain_timing_source_string(rank->timing_source)); |
| if (rank->timing_source == |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_DEVICE_DISPATCH) { |
| fprintf(file, " total_ticks=%" PRIu64 " max_ticks=%" PRIu64, |
| rank->total_ticks, rank->maximum_ticks); |
| } else { |
| fprintf(file, |
| " total_tile_count=%" PRIu64 |
| " total_tile_duration_sum_ns=%" PRId64, |
| rank->total_tile_count, rank->total_tile_duration_sum_ns); |
| } |
| if (rank->has_duration_ns) { |
| fprintf(file, " total_ns=%" PRId64 " avg_ns=%.3f max_ns=%" PRId64, |
| rank->total_ns, rank->average_ns, rank->maximum_ns); |
| } |
| fputc('\n', file); |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_print_text_top_dispatches( |
| const iree_profile_dispatch_context_t* dispatch_context, FILE* file) { |
| fprintf(file, "top individual dispatches:\n"); |
| if (dispatch_context->top_dispatch_count != 0) { |
| iree_profile_dispatch_top_event_t |
| top_dispatches[IREE_PROFILE_DISPATCH_TOP_EVENT_COUNT]; |
| iree_profile_explain_copy_sorted_top_dispatches(dispatch_context, |
| top_dispatches); |
| for (iree_host_size_t i = 0; i < dispatch_context->top_dispatch_count; |
| ++i) { |
| const iree_profile_dispatch_top_event_t* top_event = &top_dispatches[i]; |
| iree_profile_model_clock_fit_t clock_fit; |
| const bool has_clock_fit = |
| iree_profile_explain_try_fit_driver_host_cpu_clock( |
| dispatch_context, top_event->physical_device_ordinal, &clock_fit); |
| int64_t duration_ns = 0; |
| const bool has_duration_ns = |
| has_clock_fit && |
| iree_profile_model_clock_fit_scale_ticks_to_ns( |
| &clock_fit, top_event->duration_ticks, &duration_ns); |
| char numeric_buffer[128]; |
| iree_string_view_t key = iree_string_view_empty(); |
| IREE_RETURN_IF_ERROR(iree_profile_model_resolve_dispatch_key( |
| &dispatch_context->model, top_event->physical_device_ordinal, |
| top_event->event.executable_id, top_event->event.function_ordinal, |
| numeric_buffer, sizeof(numeric_buffer), &key)); |
| fprintf(file, |
| " #%" PRIhsz " event=%" PRIu64 " submission=%" PRIu64 |
| " %.*s device=%u queue=%u stream=%" PRIu64 |
| " timing_source=%s duration_ticks=%" PRIu64, |
| i + 1, top_event->event.event_id, top_event->event.submission_id, |
| (int)key.size, key.data, top_event->physical_device_ordinal, |
| top_event->queue_ordinal, top_event->stream_id, |
| iree_profile_explain_timing_source_string( |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_DEVICE_DISPATCH), |
| top_event->duration_ticks); |
| if (has_duration_ns) { |
| fprintf(file, " duration_ns=%" PRId64, duration_ns); |
| } |
| fputc('\n', file); |
| } |
| return iree_ok_status(); |
| } |
| |
| iree_profile_host_dispatch_top_event_t |
| top_dispatches[IREE_PROFILE_DISPATCH_TOP_EVENT_COUNT]; |
| iree_profile_explain_copy_sorted_top_host_dispatches(dispatch_context, |
| top_dispatches); |
| for (iree_host_size_t i = 0; i < dispatch_context->top_host_dispatch_count; |
| ++i) { |
| const iree_profile_host_dispatch_top_event_t* top_event = |
| &top_dispatches[i]; |
| char numeric_buffer[128]; |
| iree_string_view_t key = iree_string_view_empty(); |
| IREE_RETURN_IF_ERROR(iree_profile_model_resolve_dispatch_key( |
| &dispatch_context->model, top_event->physical_device_ordinal, |
| top_event->event.executable_id, top_event->event.function_ordinal, |
| numeric_buffer, sizeof(numeric_buffer), &key)); |
| fprintf(file, |
| " #%" PRIhsz " event=%" PRIu64 " submission=%" PRIu64 |
| " %.*s device=%u queue=%u stream=%" PRIu64 |
| " timing_source=%s duration_ns=%" PRId64 " tile_count=%" PRIu64 |
| " tile_duration_sum_ns=%" PRId64, |
| i + 1, top_event->event.event_id, top_event->event.submission_id, |
| (int)key.size, key.data, top_event->physical_device_ordinal, |
| top_event->queue_ordinal, top_event->stream_id, |
| iree_profile_explain_timing_source_string( |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_HOST_EXECUTION), |
| top_event->duration_ns, top_event->event.tile_count, |
| top_event->event.tile_duration_sum_ns); |
| if (top_event->event.command_buffer_id != 0) { |
| fprintf(file, " command_buffer=%" PRIu64 " command_index=%u", |
| top_event->event.command_buffer_id, |
| top_event->event.command_index); |
| } |
| fputc('\n', file); |
| } |
| return iree_ok_status(); |
| } |
| |
| static void iree_profile_explain_print_text_queue_operations( |
| const iree_profile_dispatch_context_t* dispatch_context, FILE* file) { |
| iree_profile_explain_queue_operation_totals_t operation_totals; |
| iree_profile_explain_compute_queue_operation_totals(dispatch_context, |
| &operation_totals); |
| const uint64_t* event_counts = operation_totals.event_counts; |
| const uint64_t* payload_bytes = operation_totals.payload_bytes; |
| const uint64_t* strategy_counts = operation_totals.strategy_counts; |
| |
| fprintf(file, |
| "queue operations: events=%" PRIhsz |
| " strategies none/inline/device_barrier/software_defer=%" PRIu64 |
| "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "\n", |
| dispatch_context->queue_query.queue_event_count, |
| strategy_counts[IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_NONE], |
| strategy_counts[IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_INLINE], |
| strategy_counts |
| [IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_DEVICE_BARRIER], |
| strategy_counts |
| [IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_SOFTWARE_DEFER]); |
| fprintf(file, |
| " transfer_payload_bytes copy/fill/update/read/write=%" PRIu64 |
| "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "\n", |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_COPY], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_FILL], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_UPDATE], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_READ], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_WRITE]); |
| fprintf( |
| file, |
| " operation_counts dispatch/execute/alloca/dealloca/host_call=%" PRIu64 |
| "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "\n", |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_DISPATCH], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_EXECUTE], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_ALLOCA], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_DEALLOCA], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_HOST_CALL]); |
| } |
| |
| static void iree_profile_explain_print_text_memory_pressure( |
| const iree_profile_memory_context_t* memory_context, FILE* file) { |
| fprintf(file, "memory pressure:\n"); |
| for (iree_host_size_t i = 0; i < memory_context->device_count; ++i) { |
| const iree_profile_memory_device_t* device = &memory_context->devices[i]; |
| fprintf(file, |
| " device[%u]: slab_high_water=%" PRIu64 |
| " pool_reserved_high_water=%" PRIu64 |
| " pool_materialized_high_water=%" PRIu64 |
| " queue_inflight_high_water=%" PRIu64 " buffer_high_water=%" PRIu64 |
| " pool_waits=%" PRIu64 "\n", |
| device->physical_device_ordinal, |
| device->slab_allocation_balance.high_water_bytes, |
| device->pool_reservation_balance.high_water_bytes, |
| device->pool_materialization_balance.high_water_bytes, |
| device->queue_inflight_balance.high_water_bytes, |
| device->buffer_allocation_balance.high_water_bytes, |
| device->pool_wait_count); |
| } |
| } |
| |
| static void iree_profile_explain_print_text_hints( |
| const iree_profile_summary_t* summary, |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_memory_context_t* memory_context, FILE* file) { |
| fprintf(file, "hints:\n"); |
| if (summary->unknown_record_count != 0 || summary->unknown_chunk_count != 0) { |
| iree_profile_explain_print_hint_text( |
| "warning", "format", |
| "unknown records or chunks were present; newer producer data may be " |
| "missing from this analysis", |
| file); |
| } |
| if (summary->truncated_chunk_count != 0) { |
| iree_profile_explain_print_hint_text( |
| "error", "format", |
| "truncated chunks were present; timing and lifecycle totals may be " |
| "incomplete", |
| file); |
| } |
| if (summary->non_ok_session_end_count != 0) { |
| fprintf(file, |
| " [error] session: %" PRIu64 |
| " profiling session end record(s) reported non-OK status; first " |
| "session_id=%" PRIu64 " stream_id=%" PRIu64 " event_id=%" PRIu64 |
| " status=%s(%u)\n", |
| summary->non_ok_session_end_count, summary->first_non_ok_session_id, |
| summary->first_non_ok_stream_id, summary->first_non_ok_event_id, |
| iree_profile_status_code_name( |
| (uint32_t)summary->first_non_ok_session_status_code), |
| (uint32_t)summary->first_non_ok_session_status_code); |
| } |
| if (dispatch_context->invalid_dispatch_count != 0) { |
| iree_profile_explain_print_hint_text( |
| "warning", "dispatch", |
| "some dispatch events had missing or reversed timestamps and were " |
| "excluded from timing totals", |
| file); |
| } |
| if (dispatch_context->invalid_host_dispatch_count != 0) { |
| iree_profile_explain_print_hint_text( |
| "warning", "dispatch", |
| "some host execution dispatch spans had missing or reversed timestamps " |
| "and were excluded from timing totals", |
| file); |
| } |
| if (dispatch_context->queue_query.queue_event_count == 0) { |
| iree_profile_explain_print_hint_text( |
| "info", "queue", |
| "queue event records are absent; queue dependency and gap hints are " |
| "limited to dispatch timestamp intervals", |
| file); |
| } |
| if (iree_profile_explain_has_pool_waits(memory_context)) { |
| iree_profile_explain_print_hint_text( |
| "warning", "memory", |
| "pool wait events were observed; allocation readiness affected the " |
| "captured queue schedule", |
| file); |
| } |
| if (dispatch_context->valid_dispatch_count == 0 && |
| dispatch_context->valid_host_dispatch_count != 0) { |
| iree_profile_explain_print_hint_text( |
| "info", "dispatch", |
| "device dispatch events are absent; top dispatch analysis is using " |
| "host execution dispatch spans", |
| file); |
| } else if (dispatch_context->valid_dispatch_count == 0) { |
| iree_profile_explain_print_hint_text( |
| "info", "dispatch", |
| "no valid dispatch events were captured; enable queue profiling on a " |
| "producer that emits device timestamps", |
| file); |
| } |
| } |
| |
| static iree_status_t iree_profile_explain_print_text( |
| const iree_profile_summary_t* summary, |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_memory_context_t* memory_context, |
| iree_allocator_t host_allocator, FILE* file) { |
| iree_profile_explain_function_rank_t* function_ranks = NULL; |
| iree_host_size_t function_rank_count = 0; |
| iree_status_t status = iree_profile_explain_collect_function_ranks( |
| dispatch_context, host_allocator, &function_ranks, &function_rank_count); |
| |
| if (iree_status_is_ok(status)) { |
| iree_profile_explain_print_text_header(summary, dispatch_context, |
| memory_context, file); |
| status = iree_profile_explain_print_text_devices(dispatch_context, file); |
| } |
| if (iree_status_is_ok(status)) { |
| status = iree_profile_explain_print_text_queues(dispatch_context, |
| host_allocator, file); |
| } |
| if (iree_status_is_ok(status)) { |
| status = iree_profile_explain_print_text_top_functions( |
| dispatch_context, function_ranks, function_rank_count, file); |
| } |
| if (iree_status_is_ok(status)) { |
| status = |
| iree_profile_explain_print_text_top_dispatches(dispatch_context, file); |
| } |
| if (iree_status_is_ok(status)) { |
| iree_profile_explain_print_text_queue_operations(dispatch_context, file); |
| iree_profile_explain_print_text_memory_pressure(memory_context, file); |
| iree_profile_explain_print_text_hints(summary, dispatch_context, |
| memory_context, file); |
| } |
| |
| iree_allocator_free(host_allocator, function_ranks); |
| return status; |
| } |
| |
| static void iree_profile_explain_print_jsonl_summary( |
| const iree_profile_summary_t* summary, |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_memory_context_t* memory_context, FILE* file) { |
| fprintf(file, |
| "{\"type\":\"explain_summary\",\"file_records\":%" PRIu64 |
| ",\"chunk_records\":%" PRIu64 ",\"unknown_records\":%" PRIu64 |
| ",\"unknown_chunks\":%" PRIu64 ",\"truncated_chunks\":%" PRIu64 |
| ",\"dropped_records\":%" PRIu64 |
| ",\"non_ok_session_end_records\":%" PRIu64 ",\"dispatches\":%" PRIu64 |
| ",\"valid_dispatches\":%" PRIu64 ",\"invalid_dispatches\":%" PRIu64 |
| ",\"host_dispatches\":%" PRIu64 ",\"valid_host_dispatches\":%" PRIu64 |
| ",\"invalid_host_dispatches\":%" PRIu64 ",\"queues\":%" PRIhsz |
| ",\"queue_events\":%" PRIhsz ",\"command_buffers\":%" PRIhsz |
| ",\"command_operations\":%" PRIhsz ",\"memory_events\":%" PRIu64, |
| summary->file_record_count, summary->chunk_count, |
| summary->unknown_record_count, summary->unknown_chunk_count, |
| summary->truncated_chunk_count, summary->dropped_record_count, |
| summary->non_ok_session_end_count, |
| dispatch_context->matched_dispatch_count, |
| dispatch_context->valid_dispatch_count, |
| dispatch_context->invalid_dispatch_count, |
| dispatch_context->matched_host_dispatch_count, |
| dispatch_context->valid_host_dispatch_count, |
| dispatch_context->invalid_host_dispatch_count, |
| dispatch_context->model.queue_count, |
| dispatch_context->queue_query.queue_event_count, |
| dispatch_context->model.command_buffer_count, |
| dispatch_context->model.command_operation_count, |
| memory_context->matched_event_count); |
| if (summary->non_ok_session_end_count != 0) { |
| fprintf(file, |
| ",\"first_non_ok_session_id\":%" PRIu64 |
| ",\"first_non_ok_stream_id\":%" PRIu64 |
| ",\"first_non_ok_event_id\":%" PRIu64 |
| ",\"first_non_ok_status_code\":%u,\"first_non_ok_status\":\"%s\"", |
| summary->first_non_ok_session_id, summary->first_non_ok_stream_id, |
| summary->first_non_ok_event_id, |
| (uint32_t)summary->first_non_ok_session_status_code, |
| iree_profile_status_code_name( |
| (uint32_t)summary->first_non_ok_session_status_code)); |
| } |
| fputs("}\n", file); |
| } |
| |
| static iree_status_t iree_profile_explain_print_jsonl_devices( |
| const iree_profile_dispatch_context_t* dispatch_context, FILE* file) { |
| for (iree_host_size_t i = 0; i < dispatch_context->model.device_count; ++i) { |
| const iree_profile_model_device_t* device = |
| &dispatch_context->model.devices[i]; |
| iree_profile_explain_device_row_t row; |
| IREE_RETURN_IF_ERROR(iree_profile_explain_resolve_device_row( |
| dispatch_context, device, &row)); |
| fprintf(file, |
| "{\"type\":\"explain_device\",\"physical_device_ordinal\":%u" |
| ",\"clock_fit_available\":%s,\"clock_samples\":%" PRIu64 |
| ",\"visible_span_ticks\":%.3f" |
| ",\"active_dispatch_ticks\":%" PRIu64 |
| ",\"active_over_visible\":%.6f" |
| ",\"tick_frequency_hz\":%.3f,\"visible_span_ns\":%.3f" |
| ",\"active_dispatch_time_ns_available\":%s" |
| ",\"active_dispatch_ns\":%" PRId64 "}\n", |
| row.device->physical_device_ordinal, |
| row.has_clock_fit ? "true" : "false", |
| row.device->clock_sample_count, row.visible_span_ticks, |
| row.total_dispatch_ticks, row.active_over_visible_ratio, |
| row.has_clock_fit ? row.tick_frequency_hz : 0.0, |
| row.has_clock_fit |
| ? row.visible_span_ticks * row.nanoseconds_per_tick |
| : 0.0, |
| row.has_total_dispatch_time_ns ? "true" : "false", |
| row.has_total_dispatch_time_ns ? row.total_dispatch_time_ns : 0); |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_print_jsonl_queues( |
| const iree_profile_dispatch_context_t* dispatch_context, |
| iree_allocator_t host_allocator, FILE* file) { |
| for (iree_host_size_t i = 0; i < dispatch_context->model.queue_count; ++i) { |
| const iree_hal_profile_queue_record_t* queue = |
| &dispatch_context->model.queues[i].record; |
| iree_profile_explain_queue_row_t row; |
| IREE_RETURN_IF_ERROR(iree_profile_explain_resolve_queue_row( |
| dispatch_context, queue, host_allocator, &row)); |
| fprintf( |
| file, |
| "{\"type\":\"explain_queue\",\"physical_device_ordinal\":%u" |
| ",\"queue_ordinal\":%u,\"stream_id\":%" PRIu64 |
| ",\"submissions\":%" PRIu64 ",\"valid_submissions\":%" PRIu64 |
| ",\"invalid_submissions\":%" PRIu64 |
| ",\"busy_ticks\":%.3f,\"total_dispatch_ticks\":%" PRIu64 |
| ",\"clock_fit_available\":%s,\"busy_ns\":%.3f" |
| ",\"total_dispatch_time_ns_available\":%s" |
| ",\"total_dispatch_ns\":%" PRId64 |
| ",\"gap_analysis_available\":%s,\"gaps\":%" PRIu64 |
| ",\"total_gap_ticks\":%.3f,\"max_gap_ticks\":%.3f" |
| ",\"total_gap_ns\":%.3f,\"max_gap_ns\":%.3f}\n", |
| row.queue->physical_device_ordinal, row.queue->queue_ordinal, |
| row.queue->stream_id, row.summary.submission_count, |
| row.summary.valid_submission_count, |
| row.summary.invalid_submission_count, row.summary.busy_ticks, |
| row.summary.total_dispatch_ticks, row.has_clock_fit ? "true" : "false", |
| row.has_clock_fit ? row.summary.busy_ticks * row.nanoseconds_per_tick |
| : 0.0, |
| row.has_total_dispatch_time_ns ? "true" : "false", |
| row.has_total_dispatch_time_ns ? row.total_dispatch_time_ns : 0, |
| row.gap_analysis_available ? "true" : "false", |
| row.gap_analysis_available ? row.summary.gap_count : 0, |
| row.gap_analysis_available ? row.summary.total_gap_ticks : 0.0, |
| row.gap_analysis_available ? row.summary.max_gap_ticks : 0.0, |
| row.gap_analysis_available && row.has_clock_fit |
| ? row.summary.total_gap_ticks * row.nanoseconds_per_tick |
| : 0.0, |
| row.gap_analysis_available && row.has_clock_fit |
| ? row.summary.max_gap_ticks * row.nanoseconds_per_tick |
| : 0.0); |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_print_jsonl_top_functions( |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_explain_function_rank_t* function_ranks, |
| iree_host_size_t function_rank_count, FILE* file) { |
| const iree_host_size_t top_function_count = |
| iree_min(function_rank_count, |
| (iree_host_size_t)IREE_PROFILE_EXPLAIN_TOP_FUNCTION_COUNT); |
| for (iree_host_size_t i = 0; i < top_function_count; ++i) { |
| const iree_profile_explain_function_rank_t* rank = &function_ranks[i]; |
| char numeric_buffer[128]; |
| iree_string_view_t key = iree_string_view_empty(); |
| IREE_RETURN_IF_ERROR(iree_profile_model_resolve_dispatch_key( |
| &dispatch_context->model, rank->physical_device_ordinal, |
| rank->executable_id, rank->function_ordinal, numeric_buffer, |
| sizeof(numeric_buffer), &key)); |
| fprintf(file, |
| "{\"type\":\"explain_top_function\",\"rank\":%" PRIhsz |
| ",\"physical_device_ordinal\":%u" |
| ",\"executable_id\":%" PRIu64 |
| ",\"function_ordinal\":%u" |
| ",\"key\":", |
| i + 1, rank->physical_device_ordinal, rank->executable_id, |
| rank->function_ordinal); |
| iree_profile_fprint_json_string(file, key); |
| fprintf(file, |
| ",\"dispatches\":%" PRIu64 ",\"valid\":%" PRIu64 |
| ",\"invalid\":%" PRIu64 ",\"total_ticks\":%" PRIu64 |
| ",\"max_ticks\":%" PRIu64 |
| ",\"timing_source\":\"%s\"" |
| ",\"duration_ns_available\":%s" |
| ",\"total_ns\":%" PRId64 |
| ",\"avg_ns\":%.3f" |
| ",\"max_ns\":%" PRId64 ",\"total_tile_count\":%" PRIu64 |
| ",\"total_tile_duration_sum_ns\":%" PRId64 "}\n", |
| rank->dispatch_count, rank->valid_count, rank->invalid_count, |
| rank->total_ticks, rank->maximum_ticks, |
| iree_profile_explain_timing_source_string(rank->timing_source), |
| rank->has_duration_ns ? "true" : "false", rank->total_ns, |
| rank->average_ns, rank->maximum_ns, rank->total_tile_count, |
| rank->total_tile_duration_sum_ns); |
| } |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_profile_explain_print_jsonl_top_dispatches( |
| const iree_profile_dispatch_context_t* dispatch_context, FILE* file) { |
| if (dispatch_context->top_dispatch_count != 0) { |
| iree_profile_dispatch_top_event_t |
| top_dispatches[IREE_PROFILE_DISPATCH_TOP_EVENT_COUNT]; |
| iree_profile_explain_copy_sorted_top_dispatches(dispatch_context, |
| top_dispatches); |
| for (iree_host_size_t i = 0; i < dispatch_context->top_dispatch_count; |
| ++i) { |
| const iree_profile_dispatch_top_event_t* top_event = &top_dispatches[i]; |
| iree_profile_model_clock_fit_t clock_fit; |
| const bool has_clock_fit = |
| iree_profile_explain_try_fit_driver_host_cpu_clock( |
| dispatch_context, top_event->physical_device_ordinal, &clock_fit); |
| int64_t duration_ns = 0; |
| const bool has_duration_ns = |
| has_clock_fit && |
| iree_profile_model_clock_fit_scale_ticks_to_ns( |
| &clock_fit, top_event->duration_ticks, &duration_ns); |
| char numeric_buffer[128]; |
| iree_string_view_t key = iree_string_view_empty(); |
| IREE_RETURN_IF_ERROR(iree_profile_model_resolve_dispatch_key( |
| &dispatch_context->model, top_event->physical_device_ordinal, |
| top_event->event.executable_id, top_event->event.function_ordinal, |
| numeric_buffer, sizeof(numeric_buffer), &key)); |
| fprintf(file, |
| "{\"type\":\"explain_top_dispatch\",\"rank\":%" PRIhsz |
| ",\"event_id\":%" PRIu64 ",\"submission_id\":%" PRIu64 |
| ",\"physical_device_ordinal\":%u,\"queue_ordinal\":%u" |
| ",\"stream_id\":%" PRIu64 ",\"key\":", |
| i + 1, top_event->event.event_id, top_event->event.submission_id, |
| top_event->physical_device_ordinal, top_event->queue_ordinal, |
| top_event->stream_id); |
| iree_profile_fprint_json_string(file, key); |
| fprintf(file, |
| ",\"timing_source\":\"%s\"" |
| ",\"duration_ticks\":%" PRIu64 |
| ",\"duration_ns_available\":%s" |
| ",\"duration_ns\":%" PRId64 |
| ",\"tile_count\":0,\"tile_duration_sum_ns\":0}\n", |
| iree_profile_explain_timing_source_string( |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_DEVICE_DISPATCH), |
| top_event->duration_ticks, has_duration_ns ? "true" : "false", |
| has_duration_ns ? duration_ns : 0); |
| } |
| return iree_ok_status(); |
| } |
| |
| iree_profile_host_dispatch_top_event_t |
| top_dispatches[IREE_PROFILE_DISPATCH_TOP_EVENT_COUNT]; |
| iree_profile_explain_copy_sorted_top_host_dispatches(dispatch_context, |
| top_dispatches); |
| for (iree_host_size_t i = 0; i < dispatch_context->top_host_dispatch_count; |
| ++i) { |
| const iree_profile_host_dispatch_top_event_t* top_event = |
| &top_dispatches[i]; |
| char numeric_buffer[128]; |
| iree_string_view_t key = iree_string_view_empty(); |
| IREE_RETURN_IF_ERROR(iree_profile_model_resolve_dispatch_key( |
| &dispatch_context->model, top_event->physical_device_ordinal, |
| top_event->event.executable_id, top_event->event.function_ordinal, |
| numeric_buffer, sizeof(numeric_buffer), &key)); |
| fprintf(file, |
| "{\"type\":\"explain_top_dispatch\",\"rank\":%" PRIhsz |
| ",\"event_id\":%" PRIu64 ",\"submission_id\":%" PRIu64 |
| ",\"physical_device_ordinal\":%u,\"queue_ordinal\":%u" |
| ",\"stream_id\":%" PRIu64 ",\"command_buffer_id\":%" PRIu64 |
| ",\"command_index\":%u,\"key\":", |
| i + 1, top_event->event.event_id, top_event->event.submission_id, |
| top_event->physical_device_ordinal, top_event->queue_ordinal, |
| top_event->stream_id, top_event->event.command_buffer_id, |
| top_event->event.command_index); |
| iree_profile_fprint_json_string(file, key); |
| fprintf(file, |
| ",\"timing_source\":\"%s\"" |
| ",\"duration_ticks\":0" |
| ",\"duration_ns_available\":true" |
| ",\"duration_ns\":%" PRId64 ",\"tile_count\":%" PRIu64 |
| ",\"tile_duration_sum_ns\":%" PRId64 "}\n", |
| iree_profile_explain_timing_source_string( |
| IREE_PROFILE_EXPLAIN_TIMING_SOURCE_HOST_EXECUTION), |
| top_event->duration_ns, top_event->event.tile_count, |
| top_event->event.tile_duration_sum_ns); |
| } |
| return iree_ok_status(); |
| } |
| |
| static void iree_profile_explain_print_jsonl_queue_operations( |
| const iree_profile_dispatch_context_t* dispatch_context, FILE* file) { |
| iree_profile_explain_queue_operation_totals_t operation_totals; |
| iree_profile_explain_compute_queue_operation_totals(dispatch_context, |
| &operation_totals); |
| const uint64_t* event_counts = operation_totals.event_counts; |
| const uint64_t* payload_bytes = operation_totals.payload_bytes; |
| const uint64_t* strategy_counts = operation_totals.strategy_counts; |
| |
| fprintf(file, |
| "{\"type\":\"explain_queue_operations\"" |
| ",\"events\":%" PRIhsz ",\"strategy_none\":%" PRIu64 |
| ",\"strategy_inline\":%" PRIu64 |
| ",\"strategy_device_barrier\":%" PRIu64 |
| ",\"strategy_software_defer\":%" PRIu64 ",\"copy_bytes\":%" PRIu64 |
| ",\"fill_bytes\":%" PRIu64 ",\"update_bytes\":%" PRIu64 |
| ",\"read_bytes\":%" PRIu64 ",\"write_bytes\":%" PRIu64 |
| ",\"dispatches\":%" PRIu64 ",\"executes\":%" PRIu64 |
| ",\"allocas\":%" PRIu64 ",\"deallocas\":%" PRIu64 |
| ",\"host_calls\":%" PRIu64 "}\n", |
| dispatch_context->queue_query.queue_event_count, |
| strategy_counts[IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_NONE], |
| strategy_counts[IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_INLINE], |
| strategy_counts |
| [IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_DEVICE_BARRIER], |
| strategy_counts |
| [IREE_HAL_PROFILE_QUEUE_DEPENDENCY_STRATEGY_SOFTWARE_DEFER], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_COPY], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_FILL], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_UPDATE], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_READ], |
| payload_bytes[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_WRITE], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_DISPATCH], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_EXECUTE], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_ALLOCA], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_DEALLOCA], |
| event_counts[IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_HOST_CALL]); |
| } |
| |
| static void iree_profile_explain_print_jsonl_memory_pressure( |
| const iree_profile_memory_context_t* memory_context, FILE* file) { |
| for (iree_host_size_t i = 0; i < memory_context->device_count; ++i) { |
| const iree_profile_memory_device_t* device = &memory_context->devices[i]; |
| fprintf(file, |
| "{\"type\":\"explain_memory_pressure\"" |
| ",\"physical_device_ordinal\":%u" |
| ",\"slab_high_water_bytes\":%" PRIu64 |
| ",\"pool_reserved_high_water_bytes\":%" PRIu64 |
| ",\"pool_materialized_high_water_bytes\":%" PRIu64 |
| ",\"queue_inflight_high_water_bytes\":%" PRIu64 |
| ",\"buffer_high_water_bytes\":%" PRIu64 ",\"pool_waits\":%" PRIu64 |
| "}\n", |
| device->physical_device_ordinal, |
| device->slab_allocation_balance.high_water_bytes, |
| device->pool_reservation_balance.high_water_bytes, |
| device->pool_materialization_balance.high_water_bytes, |
| device->queue_inflight_balance.high_water_bytes, |
| device->buffer_allocation_balance.high_water_bytes, |
| device->pool_wait_count); |
| } |
| } |
| |
| static void iree_profile_explain_print_jsonl_hints( |
| const iree_profile_summary_t* summary, |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_memory_context_t* memory_context, FILE* file) { |
| if (summary->unknown_record_count != 0 || summary->unknown_chunk_count != 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "warning", "format", |
| "unknown records or chunks were present; newer producer data may be " |
| "missing from this analysis", |
| file); |
| } |
| if (summary->truncated_chunk_count != 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "error", "format", |
| "truncated chunks were present; timing and lifecycle totals may be " |
| "incomplete", |
| file); |
| } |
| if (summary->non_ok_session_end_count != 0) { |
| fprintf(file, |
| "{\"type\":\"explain_hint\",\"severity\":\"error\"" |
| ",\"category\":\"session\",\"message\":\"profiling session end " |
| "records reported non-OK status\"" |
| ",\"non_ok_session_end_records\":%" PRIu64 |
| ",\"first_non_ok_session_id\":%" PRIu64 |
| ",\"first_non_ok_stream_id\":%" PRIu64 |
| ",\"first_non_ok_event_id\":%" PRIu64 |
| ",\"first_non_ok_status_code\":%u,\"first_non_ok_status\":\"%s\"" |
| "}\n", |
| summary->non_ok_session_end_count, summary->first_non_ok_session_id, |
| summary->first_non_ok_stream_id, summary->first_non_ok_event_id, |
| (uint32_t)summary->first_non_ok_session_status_code, |
| iree_profile_status_code_name( |
| (uint32_t)summary->first_non_ok_session_status_code)); |
| } |
| if (dispatch_context->invalid_dispatch_count != 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "warning", "dispatch", |
| "some dispatch events had missing or reversed timestamps and were " |
| "excluded from timing totals", |
| file); |
| } |
| if (dispatch_context->invalid_host_dispatch_count != 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "warning", "dispatch", |
| "some host execution dispatch spans had missing or reversed timestamps " |
| "and were excluded from timing totals", |
| file); |
| } |
| if (dispatch_context->queue_query.queue_event_count == 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "info", "queue", |
| "queue event records are absent; queue dependency and gap hints are " |
| "limited to dispatch timestamp intervals", |
| file); |
| } |
| if (iree_profile_explain_has_pool_waits(memory_context)) { |
| iree_profile_explain_print_hint_jsonl( |
| "warning", "memory", |
| "pool wait events were observed; allocation readiness affected the " |
| "captured queue schedule", |
| file); |
| } |
| if (dispatch_context->valid_dispatch_count == 0 && |
| dispatch_context->valid_host_dispatch_count != 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "info", "dispatch", |
| "device dispatch events are absent; top dispatch analysis is using " |
| "host execution dispatch spans", |
| file); |
| } else if (dispatch_context->valid_dispatch_count == 0) { |
| iree_profile_explain_print_hint_jsonl( |
| "info", "dispatch", |
| "no valid dispatch events were captured; enable queue profiling on a " |
| "producer that emits device timestamps", |
| file); |
| } |
| } |
| |
| static iree_status_t iree_profile_explain_print_jsonl( |
| const iree_profile_summary_t* summary, |
| const iree_profile_dispatch_context_t* dispatch_context, |
| const iree_profile_memory_context_t* memory_context, |
| iree_allocator_t host_allocator, FILE* file) { |
| iree_profile_explain_function_rank_t* function_ranks = NULL; |
| iree_host_size_t function_rank_count = 0; |
| iree_status_t status = iree_profile_explain_collect_function_ranks( |
| dispatch_context, host_allocator, &function_ranks, &function_rank_count); |
| |
| if (iree_status_is_ok(status)) { |
| iree_profile_explain_print_jsonl_summary(summary, dispatch_context, |
| memory_context, file); |
| status = iree_profile_explain_print_jsonl_devices(dispatch_context, file); |
| } |
| if (iree_status_is_ok(status)) { |
| status = iree_profile_explain_print_jsonl_queues(dispatch_context, |
| host_allocator, file); |
| } |
| if (iree_status_is_ok(status)) { |
| status = iree_profile_explain_print_jsonl_top_functions( |
| dispatch_context, function_ranks, function_rank_count, file); |
| } |
| if (iree_status_is_ok(status)) { |
| status = |
| iree_profile_explain_print_jsonl_top_dispatches(dispatch_context, file); |
| } |
| if (iree_status_is_ok(status)) { |
| iree_profile_explain_print_jsonl_queue_operations(dispatch_context, file); |
| iree_profile_explain_print_jsonl_memory_pressure(memory_context, file); |
| iree_profile_explain_print_jsonl_hints(summary, dispatch_context, |
| memory_context, file); |
| } |
| |
| iree_allocator_free(host_allocator, function_ranks); |
| return status; |
| } |
| |
| typedef struct iree_profile_explain_parse_context_t { |
| // File-level summary populated during the metadata pass. |
| iree_profile_summary_t* summary; |
| // Dispatch model state and event aggregates. |
| iree_profile_dispatch_context_t* dispatch_context; |
| // Memory event aggregates. |
| iree_profile_memory_context_t* memory_context; |
| // Optional glob filter applied to dispatch/event keys. |
| iree_string_view_t filter; |
| // Optional entity identifier filter, or -1 when disabled. |
| int64_t id_filter; |
| } iree_profile_explain_parse_context_t; |
| |
| static iree_status_t iree_profile_explain_metadata_record( |
| void* user_data, const iree_hal_profile_file_record_t* record, |
| iree_host_size_t record_index) { |
| (void)record_index; |
| iree_profile_explain_parse_context_t* context = |
| (iree_profile_explain_parse_context_t*)user_data; |
| IREE_RETURN_IF_ERROR( |
| iree_profile_summary_process_record(context->summary, record)); |
| return iree_profile_model_process_metadata_record( |
| &context->dispatch_context->model, record); |
| } |
| |
| static iree_status_t iree_profile_explain_event_record( |
| void* user_data, const iree_hal_profile_file_record_t* record, |
| iree_host_size_t record_index) { |
| (void)record_index; |
| iree_profile_explain_parse_context_t* context = |
| (iree_profile_explain_parse_context_t*)user_data; |
| const iree_profile_dispatch_event_callback_t dispatch_event_callback = {0}; |
| IREE_RETURN_IF_ERROR(iree_profile_dispatch_process_events_record( |
| context->dispatch_context, record, context->filter, |
| IREE_PROFILE_PROJECTION_MODE_DISPATCH, context->id_filter, |
| /*aggregate_events=*/true, dispatch_event_callback)); |
| if (record->header.record_type == IREE_HAL_PROFILE_FILE_RECORD_TYPE_CHUNK && |
| iree_string_view_equal(record->content_type, |
| IREE_HAL_PROFILE_CONTENT_TYPE_QUEUE_EVENTS)) { |
| IREE_RETURN_IF_ERROR(iree_profile_queue_event_query_process_record( |
| &context->dispatch_context->queue_query, |
| &context->dispatch_context->model, record, IREE_SV("*"), |
| /*id_filter=*/-1)); |
| } |
| const iree_profile_memory_event_callback_t memory_event_callback = {0}; |
| return iree_profile_memory_context_accumulate_record( |
| context->memory_context, record, IREE_SV("*"), /*id_filter=*/-1, |
| memory_event_callback); |
| } |
| |
| iree_status_t iree_profile_explain_file(iree_string_view_t path, |
| iree_string_view_t format, |
| iree_string_view_t filter, |
| int64_t id_filter, FILE* file, |
| iree_allocator_t host_allocator) { |
| bool is_text = iree_string_view_equal(format, IREE_SV("text")); |
| bool is_jsonl = iree_string_view_equal(format, IREE_SV("jsonl")); |
| if (!is_text && !is_jsonl) { |
| return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "unsupported profile output format '%.*s'", |
| (int)format.size, format.data); |
| } |
| |
| iree_profile_file_t profile_file; |
| IREE_RETURN_IF_ERROR( |
| iree_profile_file_open(path, host_allocator, &profile_file)); |
| |
| iree_profile_summary_t summary; |
| iree_profile_summary_initialize(host_allocator, &summary); |
| iree_profile_dispatch_context_t dispatch_context; |
| iree_profile_dispatch_context_initialize(host_allocator, &dispatch_context); |
| iree_profile_memory_context_t memory_context; |
| iree_profile_memory_context_initialize(host_allocator, &memory_context); |
| iree_profile_explain_parse_context_t parse_context = { |
| .summary = &summary, |
| .dispatch_context = &dispatch_context, |
| .memory_context = &memory_context, |
| .filter = filter, |
| .id_filter = id_filter, |
| }; |
| iree_profile_file_record_callback_t record_callback = { |
| .fn = iree_profile_explain_metadata_record, |
| .user_data = &parse_context, |
| }; |
| iree_status_t status = |
| iree_profile_file_for_each_record(&profile_file, record_callback); |
| if (iree_status_is_ok(status)) { |
| record_callback.fn = iree_profile_explain_event_record; |
| status = iree_profile_file_for_each_record(&profile_file, record_callback); |
| } |
| |
| if (iree_status_is_ok(status)) { |
| if (is_text) { |
| status = iree_profile_explain_print_text( |
| &summary, &dispatch_context, &memory_context, host_allocator, file); |
| } else { |
| status = iree_profile_explain_print_jsonl( |
| &summary, &dispatch_context, &memory_context, host_allocator, file); |
| } |
| } |
| |
| iree_profile_memory_context_deinitialize(&memory_context); |
| iree_profile_dispatch_context_deinitialize(&dispatch_context); |
| iree_profile_summary_deinitialize(&summary); |
| iree_profile_file_close(&profile_file); |
| return status; |
| } |