Skip to content

Add Quent Resource and IR-node tracing to cudf-polars - #23179

Open
TomAugspurger wants to merge 82 commits into
NVIDIA:mainfrom
TomAugspurger:tom/quent-resources
Open

Add Quent Resource and IR-node tracing to cudf-polars#23179
TomAugspurger wants to merge 82 commits into
NVIDIA:mainfrom
TomAugspurger:tom/quent-resources

Conversation

@TomAugspurger

@TomAugspurger TomAugspurger commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

This PR builds on #22337 by

  • adding Quent Resources to cudf-polars' engine lifecycles
  • adding IR.do_evaluate level tracing to our emitted events
  • adding custom Statistics for Actor-level events (like join decisions)
  • adopting the new Quent output format (A ZIP archive directory of {run_id}/{type}/{uuid}.ndjson + a metadata file).

I've added resource definitions / usage for:

  1. Host CPU usage (the thread pool used in cudf-polars/rapisdmpf, and its threads)
  2. Device Memory
  3. Filesystem Memory
  4. A Channel between filesystem and device memory
  5. A Network (with links between workers)

These resources are used in the new Task, which wraps our individual IR.do_evaluate

image

See this gist for some traces captured with

CUDF_POLARS__EXECUTOR__QUENT_CONTEXT=1 POLARS_MAX_THREADS=1 CUDF_POLARS_LOG_TRACES_MEMORY=0 CUDF_POLARS__PARQUET_OPTIONS__PREFETCH_FILE_METADATA=1 CUDA_VISIBLE_DEVICES=0,1 CUDF_POLARS_LOG_TRACES=1 \
    python -m cudf_polars.streaming.benchmarks.pdsh --frontend ray --path /raid/toaugspurger/datasets/tpc-h/scale-100/scale-100/ --suffix "/" --iterations 1 --collect-traces --no-print-results --no-summarize --no-native-parquet --rapidsmpf-statistics --output quent.ndjson --explain-partition-plan all

Structurally, the biggest change is to IRExecutionContext. We'd like to emit some start/end traces in <IR>.do_evaluate. Those traces need to know what Quent IR node they're executing for, so I've added IRExecutionContext.quent_ir_execution_context, which contains the operator ID. When we build the IRExecutionContext for any given IR node, we make sure to set the Quent Operator this IR node is working on (see ir_context_for_node.

Aside from that, the biggest changes by lines of code are probably:

  1. The new export format
  2. The new Quent types for resources
  3. Worker setup/teardown logic

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@copy-pr-bot

copy-pr-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions github-actions Bot added Python Affects Python cuDF API. cudf-polars Issues specific to cudf-polars labels Jul 8, 2026
@GPUtester GPUtester moved this to In Progress in cuDF Python Jul 8, 2026
Comment thread python/cudf_polars/cudf_polars/dsl/tracing.py Outdated
Comment thread python/cudf_polars/cudf_polars/dsl/tracing.py Outdated
Comment thread python/cudf_polars/cudf_polars/dsl/tracing.py
Comment thread python/cudf_polars/cudf_polars/engine/core.py Outdated
Comment thread python/cudf_polars/cudf_polars/engine/core.py Outdated
Comment thread python/cudf_polars/cudf_polars/quent/_context.py Outdated
Comment thread python/cudf_polars/cudf_polars/quent/_context.py Outdated
Comment thread python/cudf_polars/cudf_polars/quent/_context.py Outdated
Comment thread python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py Outdated
Comment thread python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py Outdated
This PR builds on NVIDIA#22337 by adding
Quent Resources to cudf-polars.
@TomAugspurger
TomAugspurger force-pushed the tom/quent-resources branch from 86782f1 to 0efdf65 Compare July 8, 2026 15:42
@TomAugspurger TomAugspurger added improvement Improvement / enhancement to an existing function non-breaking Non-breaking change labels Jul 14, 2026
@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test 2fc4e74

@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test 9821ef7

@TomAugspurger
TomAugspurger marked this pull request as ready for review July 27, 2026 19:19
@Matt711
Matt711 self-requested a review August 3, 2026 16:32
Comment thread python/cudf_polars/cudf_polars/dsl/tracing.py Outdated
Comment on lines 171 to 175
# do this just once
from cudf_polars.quent._types import Task

pynvml.nvmlInit()
maybe_handle = get_device_handle()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment still applies, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should yeah. I think the original intent was to call each of these once (per IR node type) on import and then use the returned value on each call to IR.do_evaluate. I'll move these outside def wrapper(...) to achieve that.

execute_persisted_query,
)
from cudf_polars.quent._context import LocalQuentContext
from cudf_polars.quent._context import (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remind me why we have these "internal" packages in the quent subsystem. We don't do this anywhere else.

@TomAugspurger TomAugspurger Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to be extra careful with the public-facing API here: We hope to delete a bunch of what's under cudf_polars.quent, but we do have some user-facing pieces. Having cudf_polars/quent/__init__.py contain just user-facing things makes it clear what we can just delete and what we need to provide compatibility wrappers for.

Comment on lines +337 to +338
# Now that ``comm`` exists, declare the engine-scoped inter-rank network
# topology once (a no-op for single-rank runs).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to suggest that if there's only one GPU we wouldn't have quent events. But I think that is not right.

Comment on lines +518 to +531
def _integer_variant(value: int) -> str:
"""Return the narrowest Quent integer variant that can hold ``value``."""
for variant, lo, hi in _INT_VARIANTS:
if lo <= value <= hi:
return variant
raise ValueError(f"Integer value {value} does not fit any Quent integer type.")


def _common_integer_variant(values: list[int]) -> str:
"""Return the narrowest Quent integer variant that can hold all ``values``."""
for variant, lo, hi in _INT_VARIANTS:
if all(lo <= value <= hi for value in values):
return variant
raise ValueError("Integer list values do not fit any Quent integer type.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we care about this and not just encode as i64 for everything?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm, yeah let's just use u64 for unsigned values and i64 for signed values.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess some part of the rust backend for Quent does deserialize these when you're using the UI, and presumably it uses the size associated with the value. If it were just Python it wouldn't matter, but given that there's some runtime benefit to using smaller sizes here, maybe this code is worth keeping.

Comment thread python/cudf_polars/tests/containers/test_dataframe.py Outdated
# ids are derived per-collect and ``get_stable_plan_id`` is a deterministic
# function of the IR structure, so an un-namespaced plan id would collide
# across the two identical collects.
q = pl.LazyFrame({"x": [1, 2, 3]}).filter(pl.col("x") > 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just have a test-file-scoped engine fixture?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think because the test itself wants to assert some things that happen during engine initialization and shutdown. Previously, this was increasing the duration of the test quite a bit, but I'll see whether that's still true. Perhaps I was using the fixtures giving us cluster resources improperly.

@TomAugspurger TomAugspurger Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also some state that's cached on the Engine (_quent_events) and QuentContext (_query_group_cache) that makes it a pain to try to reuse any of those resources across tests. That state would need to be cleared for the assertions that depend on them to pass.

@TomAugspurger

Copy link
Copy Markdown
Contributor Author

8b14654 is one small change bringing us to Quent main, which means we get the basic data flow timeline Felipe added in rapidsai/quent#393.

I'm testing out some additional changes to better capture data as is flows through our DAG. One major gap right now is (pinned) host memory: we don't see it at all currently. We can easily define another Memory resource, but I think we'll need some help from rapidsmpf to accurately update it. I'll probably wait to do that in a separate PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cudf-polars Issues specific to cudf-polars improvement Improvement / enhancement to an existing function non-breaking Non-breaking change Python Affects Python cuDF API.

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants