Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEA] Support GPU execution engine in LazyFrame profiler #16224

Open
Tracked by #17458
beckernick opened this issue Jul 8, 2024 · 1 comment · May be fixed by #17723
Open
Tracked by #17458

[FEA] Support GPU execution engine in LazyFrame profiler #16224

beckernick opened this issue Jul 8, 2024 · 1 comment · May be fixed by #17723
Assignees
Labels
cudf.polars Issues specific to cudf.polars feature request New feature or request Python Affects Python cuDF API.

Comments

@beckernick
Copy link
Member

We should instrument the GPU physical execution engine so that it is compatible with the built-in Polars LazyFrame profiler (or similar).

# !wget https://raw.githubusercontent.com/mwaskom/seaborn-data/master/titanic.csv
ldf = (
    pl.scan_csv("titanic.csv")
    .group_by(pl.col("age").round(0))
    .agg(pl.col("embark_town").count())
    .sort("age")
)
ldf.profile(show_plot=True)
(shape: (72, 2)
 ┌──────┬─────────────┐
 │ ageembark_town │
 │ ------         │
 │ f64u32         │
 ╞══════╪═════════════╡
 │ null177         │
 │ 0.01           │
 │ 1.013          │
 │ 2.010          │
 │ 3.06           │
 │ …    ┆ …           │
 │ 66.01           │
 │ 70.02           │
 │ 71.03           │
 │ 74.01           │
 │ 80.01           │
 └──────┴─────────────┘,
 shape: (4, 3)
 ┌──────────────────┬───────┬──────┐
 │ nodestartend  │
 │ ---------  │
 │ stru64u64  │
 ╞══════════════════╪═══════╪══════╡
 │ optimization016   │
 │ csv(titanic.csv) ┆ 161330 │
 │ group_by(age)    ┆ 13472141 │
 │ sort(age)        ┆ 21526140 │
 └──────────────────┴───────┴──────┘)
Screen Shot 2024-04-04 at 10 23 06 AM
@Matt711
Copy link
Contributor

Matt711 commented Nov 27, 2024

Notes:
We want to be able to profile GPU-accelerated queries with the Polars LazyFrame profiler. I imagine the API should look like how we execute a query using collect (ie. .collect(engine="gpu")) So API would look like

lf = pl.LazyFrame(
    {
        "a": ["a", "b", "a", "b", "b", "c"],
        "b": [1, 2, 3, 4, 5, 6],
        "c": [6, 5, 4, 3, 2, 1],
    }
)
q = lf.group_by("a", maintain_order=True).agg(pl.all().sum()).sort(
    "a"
)
df, df_times = lf.profile(engine="gpu")

Currently the Polars LazyFrame profiler works like this:

  1. Logical Plan Creation: Polars builds a logical plan representing the query.
  2. Optimization: Lots of optimizations Logical plan --> Physical Plan
  3. Execution with Timing: The physical plan is executed, and execution time for each node is recorded.
  4. Result: Finally, a tuple of DataFrames is returned one with the query result and the other with the timing information

I think if we allow the profile functions in the Polars rust layer to accept a callback (in the same way we do for collect), we can get the timing information from step 3. We'd need to change these functions (copied from https://github.com/pola-rs/polars/tree/main/crates/polars-python/src/lazyframe)

// LazyFrame::profile (Can this remain unchanged?)
pub fn profile(self) -> PolarsResult<(DataFrame, DataFrame)> {
    let (mut state, mut physical_plan, _) = self.prepare_collect(false)?;
    state.time_nodes();
    let out = physical_plan.execute(&mut state)?;
    let timer_df = state.finish_timer()?;
    Ok((out, timer_df))
}
// PyLazyFrame::profile
fn profile(&self, py: Python, lambda_post_opt: Option<PyObject>) -> PyResult<(PyDataFrame, PyDataFrame)> {
    // follow the logic in collect
}

And we'd need to add an engine kwarg to profile function in polars

def profile(
    self,
    *,
    ...
    engine: EngineType = "cpu",
) -> tuple[DataFrame, DataFrame]:
    ...
    # Following the logic in collect
    callback = None
    if engine == "gpu":
        cudf_polars = import_optional(
            "cudf_polars",
            ...
        )
        if not isinstance(engine, GPUEngine):
            engine = GPUEngine()
        callback = partial(cudf_polars.execute_with_cudf, config=engine)
    
    df, timings = self._ldf.profile(callback)
    df, timings = wrap_df(df), wrap_df(timings)
    
    ...
    
    return df, timings

Finally, we should do a docs refresh.

@Matt711 Matt711 linked a pull request Jan 13, 2025 that will close this issue
3 tasks
@GPUtester GPUtester moved this from Todo to In Progress in cuDF Python Jan 13, 2025
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 feature request New feature or request Python Affects Python cuDF API.
Projects
Status: In Progress
Development

Successfully merging a pull request may close this issue.

3 participants