Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Level zero loader changelog
## Unreleased
* feature: Add validation layer Timing Checker (`ZEL_ENABLE_TIMING_CHECKER`) for host-side per-API timing with summary, CSV and live-log output modes
## v1.30.0
* Remove SPDLog from project entirely
* Add New custom C++ smaller Logger, to replace SPDLog
Expand Down
1 change: 1 addition & 0 deletions scripts/generate_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def _mako_loader_cpp(path, namespace, tags, version, specs, meta):
'handle_lifetime.h.mako' : ('handle_lifetime_tracking', 'handle_lifetime.h'),
'handle_lifetime.cpp.mako' : ('handle_lifetime_tracking', 'handle_lifetime.cpp'),
'certification.h.mako' : ('checkers/certification/generated', 'certification.h'),
'timing.h.mako' : ('checkers/timing/generated', 'timing.h'),
'to_string.h.mako' : ('../../utils', 'to_string.h'),
}

Expand Down
63 changes: 63 additions & 0 deletions scripts/templates/validation/timing.h.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<%!
import re
from templates import helper as th
%><%
n=namespace
N=n.upper()
%>/*
* ***THIS FILE IS GENERATED. ***
* See timing.h.mako for modifications
*
* Copyright (C) 2026 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
* @file ${name}
*
*/
#pragma once
#include "../zel_global_timing_state.h"
#include "${n}_entry_points.h"

namespace validation_layer {
class ${N}timingCheckerGenerated : public ${N}ValidationEntryPoints {
public:
%for obj in th.extract_objs(specs, r"function"):
<%
param_lines = [line for line in th.make_param_lines(n, tags, obj, format=["type", "name", "delim"])]
is_void_params = len(param_lines) == 1 and "void" in param_lines[0]
func_name = th.make_func_name(n, tags, obj)
%>virtual ze_result_t ${func_name}Prologue( \
%for line in param_lines:
${line} \
%endfor
) override {
GlobalTimingState::getInstance().recordStart();
return ZE_RESULT_SUCCESS;
}
virtual ze_result_t ${func_name}Epilogue( \
%if not is_void_params:
%for line in param_lines:
${line} \
%endfor
, ${obj['return_type']} result) override {
%else:
${obj['return_type']} result ) override {
%endif
GlobalTimingState::getInstance().recordEnd("${func_name}");
return ZE_RESULT_SUCCESS;
}
%endfor
%if n == 'ze':
// Experimental Intel extension for counter-based events
virtual ze_result_t zexCounterBasedEventCreate2Prologue( ze_context_handle_t hContext, ze_device_handle_t hDevice, const void* desc, ze_event_handle_t* phEvent ) override {
GlobalTimingState::getInstance().recordStart();
return ZE_RESULT_SUCCESS;
}
virtual ze_result_t zexCounterBasedEventCreate2Epilogue( ze_context_handle_t hContext, ze_device_handle_t hDevice, const void* desc, ze_event_handle_t* phEvent , ze_result_t result) override {
GlobalTimingState::getInstance().recordEnd("zexCounterBasedEventCreate2");
return ZE_RESULT_SUCCESS;
}
%endif
};
} // namespace validation_layer
37 changes: 37 additions & 0 deletions source/layers/validation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ By default, no validation modes will be enabled. The individual validation modes
- `ZE_ENABLE_THREADING_VALIDATION` (Not yet Implemented)
- `ZEL_ENABLE_CERTIFICATION_CHECKER`
- `ZEL_ENABLE_SYSTEM_RESOURCE_TRACKER_CHECKER`
- `ZEL_ENABLE_TIMING_CHECKER`

## Validation Modes

Expand Down Expand Up @@ -141,6 +142,42 @@ export ZEL_LOADER_LOGGING_LEVEL=debug

See [System Resource Tracker documentation](checkers/system_resource_tracker/system_resource_tracker.md) for detailed usage and CSV format.

### `ZEL_ENABLE_TIMING_CHECKER`

The Timing Checker measures the host-side (CPU) duration of every Level Zero API
call and aggregates per-API statistics (call count, total, min, max and average
time in nanoseconds). It is inspired by the host-function timing in Intel's
unitrace profiler, ported into the validation layer.

For each API call the checker stamps a high-resolution monotonic timestamp in the
Prologue and reads it again in the Epilogue. The clock is
`QueryPerformanceCounter` on Windows and `clock_gettime(CLOCK_MONOTONIC_RAW)`
elsewhere. The measured span is dominated by the underlying driver call and is
consistent across calls, making it suitable for relative host-cost analysis.

> **Note:** Device-side (GPU execution) timing is not provided. The validation
> layer receives handles by value and the Epilogue runs after the driver call, so
> it cannot inject the timestamp events that GPU timing requires.

To enable, set:
```bash
export ZE_ENABLE_VALIDATION_LAYER=1
export ZEL_ENABLE_TIMING_CHECKER=1
export ZEL_ENABLE_LOADER_LOGGING=1
export ZEL_LOADER_LOG_CONSOLE=1 # optional: log to stderr instead of a file
```

Output modes (each independently controlled):

| Environment Variable | Default | Description |
|---|---|---|
| `ZEL_ENABLE_TIMING_CHECKER` | `0` | Enable the checker; a per-API summary table is logged at teardown |
| `ZEL_TIMING_CHECKER_CSV` | (unset) | Also export the per-API statistics to a CSV file (the process id is appended to the filename) |
| `ZEL_TIMING_CHECKER_LIVE` | `0` | Also log each call's duration as it happens (verbose) |

The summary and live output are emitted through the loader logger, so logging
must be enabled as shown above for them to be visible.

## Testing

There is a small set of negative test cases designed to test the validation layer in the [level zero tests repo](https://github.com/oneapi-src/level-zero-tests/tree/master/negative_tests).
Expand Down
1 change: 1 addition & 0 deletions source/layers/validation/checkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ add_subdirectory(performance)
add_subdirectory(parameter_validation)
add_subdirectory(template)
add_subdirectory(system_resource_tracker)
add_subdirectory(timing)
13 changes: 13 additions & 0 deletions source/layers/validation/checkers/timing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (C) 2026 Intel Corporation
# SPDX-License-Identifier: MIT

target_sources(${TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/zel_timing_checker.h
${CMAKE_CURRENT_LIST_DIR}/zel_timing_checker.cpp
${CMAKE_CURRENT_LIST_DIR}/zel_global_timing_state.h
${CMAKE_CURRENT_LIST_DIR}/generated/ze_timing.h
${CMAKE_CURRENT_LIST_DIR}/generated/zes_timing.h
${CMAKE_CURRENT_LIST_DIR}/generated/zet_timing.h
${CMAKE_CURRENT_LIST_DIR}/generated/zer_timing.h
)
Loading