Skip to content

Add Halidoscope for interactive trace and profiling visualization. - #9356

Draft
parkerziegler wants to merge 62 commits into
mainfrom
parkerziegler/halidoscope
Draft

Add Halidoscope for interactive trace and profiling visualization.#9356
parkerziegler wants to merge 62 commits into
mainfrom
parkerziegler/halidoscope

Conversation

@parkerziegler

Copy link
Copy Markdown

This PR introduces Halidoscope, a new tool for interactively visualizing Halide traces and performance information captured by the Halide profiler.

Trace View Profile View
A screenshot of the Trace view in the Halidoscope UI. A screenshot of the Profile view in the Halidoscope UI.

Motivation

Understanding the performance characteristics of a Halide program (and, specifically, its schedule) can be tricky for experts and downright daunting for newer Halide developers. Today, the primary developer tooling we have to aid folks in understanding their Halide programs is HalideTraceViz. While HalideTraceViz provides good visual intuition for how a schedule executes, it doesn't provide much insight into how effectively a given schedule balances trade-offs in cache locality, redundant recomputation, and parallelism. Halidoscope attempts to fill this gap by:

  • Interactively visualizing key metrics (called "render modes") like per-Func Store / Load Frequency, Redundant Stores, Reuse Distance, and Thread Coverage
  • Integrating directly with the Halide profiler
  • Allowing users to see myriad useful bits of information, such as pipeline dataflow, NaNs / Infs, and real-time liveness information (e.g., Func buffer liveness, active producer-consumer relationships)

High-Level API

Halidoscope ships as both a GUI and CLI that can be invoked through a new Pipeline::halidoscope member function or directly from the command line.

Calling Halidoscope via Pipeline::halidoscope

Halide developers can launch Halidoscope via a call to Pipeline::halidoscope (either in C++ or via the Python bindings) like so:

// Normal algorithm definition and scheduling code.

// Create the pipeline.
Pipeline pipeline(output);

// Explicitly passing input buffer dimensions.
std::vector<int32_t> sizes = {input.width(), input.height(), input.channels()};

// Call .halidoscope!
 pipeline.halidoscope(sizes);

Under the hood, the Pipeline::halidoscope member function will:

  1. Serialize the pipeline (to obtain a fresh copy)
  2. Deserialize it and execute it once with tracing enabled
  3. Deserialize it a second time and execute it with profiling enabled

Users can specify a HalidoscopeOptions struct to control how Halidoscope executes with the following fields.

Field Type Description
halidoscope_path std::string The path to the Halidoscope binary on disk. By default, the call will look for Halidoscope on the user's $PATH and error if not found.
halidoscope_output_dir std::optional<std::string> (Optional.) A path to a non-volatile directory for storing Halidoscope-generated trace binaries and profiler output. By default, Halidoscope will write recorded .hltrace and profile JSON files to a temporary directory that is destroyed on process exit.
halidoscope_profile_runs int The number of profiling runs for the Halide profiler to execute on the pipeline. Defaults to 1. Users can opt out of profiling altogether by specifying 0.

Calling Halidoscope from the command line

Users can also call launch Halidoscope directly from the command line, pointing it at a pre-recorded Halide trace binary and (optionally) a profile JSON file.

halidoscope --trace <path/to/recorded.hltrace> [--profile <path/to/recorded-profile.json>]

The Halidoscope CLI also comes with several commands (documented in the README.md and printable via halidoscope --help) that can provide useful, high-level information on a trace (for both humans and agents).

  • halidoscope list — List the Funcs in a trace, along with their dimensionality.
  • halidoscope stats — Print statistics (minimum/maximum coordinates, minimum/maximum value, maximum store/load counts, and thread count) for one or all Funcs in a trace.
  • halidoscope dot — Generate a Graphviz DOT representation of the pipeline's dataflow graph.
  • halidoscope snapshot — Snapshot a Func's values at a given packet index for a given render mode, writing the underlying data to a JSON file.

Stack and High-Level Architecture

Halidoscope is a completely standalone Tauri application in tools/halidoscope — it has no dependency on the Halide runtime.

Warning

The only slight exception to this is our use of bindgen to derive Rust bindings for halide_trace_packet_t, which ensures that our Rust packet parser always reads the correct offsets for Halide's packet format. bindgen is run at build-time against a user's local version of the Halide source. However, note that there is no actual runtime FFI between Halidoscope's Rust parser and the Halide runtime or C ABI.

Backend

The Halidoscope backend is implemented in Rust and contains the following main modules:

  • trace.rs — Responsible for parsing a .hltrace file and accumulating trace-level statistics and metadata.
  • render.rs — Computation supporting Halidoscope's render modes, where trace data is converted to RGBA Vec<u8> buffers (for display) and binary payloads for histogram data and NaN / Inf overlays
  • commands.rs — Sets up the Tauri IPC API for communication with the frontend.
  • cli.rs — Handles calls to the Halidoscope CLI.

Communication is done entirely over IPC using Tauri commands.

Frontend

The frontend is implemented in TypeScript and React, using Jotai for state management and Tailwind for styling and CSS management. We use Vite as our frontend build system. This is about as standard a frontend stack as you'll find in modern web dev.

By default, Tauri uses a system's native WebView for rendering, which allows us to avoid bundling a full browser engine with the Halidoscope binary. Today, the Halidoscope binary weighs in at 10.5 MB uncompressed and 3.7 MB gzipped.

Breaking changes

No changes here are breaking; all changes are purely additive. Still, it is likely worth reviewing the additions to the Pipeline API a bit more carefully to confirm.

Checklist

  • Tests added or updated (not required for docs, CI config, or typo fixes)
  • Documentation updated (if public API changed)
  • Python bindings updated (if public API changed)
  • Benchmarks are included here if the change is intended to affect performance.
  • Commits include AI attribution where applicable (see Code of Conduct)

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 1.77515% with 166 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.84%. Comparing base (e627fbe) to head (815a7ef).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/Pipeline.cpp 1.77% 165 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9356      +/-   ##
==========================================
- Coverage   69.97%   69.84%   -0.14%     
==========================================
  Files         259      259              
  Lines       79158    79323     +165     
  Branches    19293    19329      +36     
==========================================
+ Hits        55394    55404      +10     
- Misses      17898    18079     +181     
+ Partials     5866     5840      -26     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@alexreinking

Copy link
Copy Markdown
Member

@parkerziegler — some quick-to-fix clang-tidy diagnostics:

[ 12/389][62.1s] /home/runner/work/Halide/Halide/tools/clang-tidy-filter.sh -p=/tmp/tmp.vaf2F3qPO4 -quiet /home/runner/work/Halide/Halide/src/Pipeline.cpp
/home/runner/work/Halide/Halide/src/Pipeline.cpp:997:9: error: use 'std::scoped_lock' instead of 'std::lock_guard' [modernize-use-scoped-lock,-warnings-as-errors]
  997 |         std::lock_guard<std::mutex> lock(halidoscope_trace_mutex);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
      |         std::scoped_lock
/home/runner/work/Halide/Halide/src/Pipeline.cpp:1191:26: error: use emplace_back instead of push_back [hicpp-use-emplace,modernize-use-emplace,-warnings-as-errors]
 1191 |         halidoscope_args.push_back("--profile");
      |                          ^~~~~~~~~~
      |                          emplace_back(
/home/runner/work/Halide/Halide/src/Pipeline.cpp:1217:75: error: the parameter 'options' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param,-warnings-as-errors]
 1217 | void Pipeline::halidoscope(std::vector<int32_t> sizes, HalidoscopeOptions options, const Target &target) {
      |                                                                           ^
      |                                                        const             &
/home/runner/work/Halide/Halide/src/Pipeline.cpp:1221:70: error: the parameter 'options' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param,-warnings-as-errors]
 1221 | void Pipeline::halidoscope(RealizationArg output, HalidoscopeOptions options, const Target &target) {
      |                                                                      ^
      |                                                   const             &
Error: use 'std%3A%3Ascoped_lock' instead of 'std%3A%3Alock_guard'
Error: use emplace_back instead of push_back
Error: the parameter 'options' is copied for each invocation but only used as a const reference; consider making it a const reference
Error: the parameter 'options' is copied for each invocation but only used as a const reference; consider making it a const reference

@parkerziegler
parkerziegler force-pushed the parkerziegler/halidoscope branch from f3ae8ba to e6799ee Compare August 18, 2026 17:06
alexreinking and others added 27 commits August 18, 2026 12:30
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… Funcs on playback.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…th mode-specific renderers.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…the notion of producer-consumer in the original Halide paper.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… there are no intervening loads.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
parkerziegler and others added 25 commits August 18, 2026 12:30
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ation into 2 (instead of 5) passes over store and load indices.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…pecify a path to the halidoscope binary on disk.
…ctory for persisting generated Halide trace and profile.
…GUI controls into RenderModeParameters component.
… from HalideRuntime.h.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…cope's JIT profiling runs.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…s UI.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthorpic.com>
…efore exit of DeferProfilerFlush.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@parkerziegler
parkerziegler force-pushed the parkerziegler/halidoscope branch from e6799ee to c0703b4 Compare August 19, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants