Skip to content

Capture values read from outside list_transform lambdas - #10702

Open
mhk197 wants to merge 1 commit into
developfrom
mk/list-transform-captures
Open

mhk197 wants to merge 1 commit into
developfrom
mk/list-transform-captures

Conversation

@mhk197

@mhk197 mhk197 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Issue: #9249

Summary

A lambda passed to list_transform can now read values from outside it, such as a column or a parameter of an enclosing lambda. The builder moves each such value out of the body into an extra input of the call and an extra parameter of the lambda. Every element of a row's list sees that row's value.

Stacked on #10503.

list_transform(col("l"), Lambda::new(["x"], checked_add(var("x"), col("a")))?)
// vortex.list.transform($.l, $.a, (x, a) -> (@x + @a))

Changes

  • Lambda::extract_captures turns each outside read ($, a path like $.s.a, another lambda's parameter, row_idx(), row_count()) into a new parameter. Lambda::new accepts them; binding still rejects them.
  • list_transform(list, lambda) calls it, so the values are ordinary children: replace, field pruning and the scan's row_idx() see them.
  • Execution repeats each value once per element of its list with take, using the index of the list that holds each element.
  • A bound lambda is rebound instead of erroring when a rebuilt node's inputs change dtype (BoundLambda::rebind).
  • With captures the function is not strict: a null captured value keeps its list valid.

Testing

  • New tests: a captured column, null and constant captured values, captures in nested lambdas (a column, the outer parameter), rebinding, extract_captures naming, and a scan reading row_idx() inside a lambda (vortex-layout).
  • Not run locally yet; CI only.

Follow-up

  • Repeat captured values with RunEnd instead of take once RunEnd lives in vortex-array.

@mhk197 mhk197 added the changelog/feature A new feature label Oct 22, 2026
@mhk197
mhk197 added this pull request to stack #10614 October 22, 2026 19:28
@codspeed

codspeed Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 6.01%

4 warnings, 1 note

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ Unable to measure the execution of 2 benchmarks

These benchmarks measured no execution time, so their results are not comparable and count as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

⚠️ 24 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

ℹ️ 40 benchmarks have no flamegraph

The flamegraph wasn't generated: the symbols of the profiling trace could not be resolved, because no information about the language frames was collected.

See affected benchmarks

⚡ 6 improved benchmarks
❌ 12 regressed benchmarks
✅ 2421 untouched benchmarks
⏩ 166 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Walltime filter_neon[8] 865 ns 1,613 ns -46.37%
❌ Walltime threshold_neon[8] 1.4 µs 1.9 µs -27.52%
❌ Walltime filter_neon[64] 1.7 µs 2.4 µs -27.46%
❌ CPU Simulation encode_primitives[f32, (1000, 2)] 116.4 µs 147.4 µs -21.01%
❌ Walltime filter_neon[80] 2.5 µs 3 µs -17.47%
❌ Walltime threshold_neon[64] 2.6 µs 3.1 µs -15.43%
❌ Walltime threshold_neon[80] 3.4 µs 4.1 µs -15.13%
❌ CPU Simulation test_scan_filter_to_arrow_array[int_float] 3.5 ms 4 ms -13.9%
❌ CPU Simulation test_repeated_scan_filter[int_float] 3.4 ms 3.8 ms -11.2%
❌ Walltime starts_with_avx2[8, VarBinView] 68.5 µs 76.3 µs -10.21%
❌ CPU Simulation test_repeated_scan_filter[int] 2.4 ms 2.7 ms -10.19%
❌ CPU Simulation test_scan_filter_to_arrow_array[int] 2.5 ms 2.8 ms -10.17%
⚡ Walltime mul_u64_nonnull_neon 39.4 µs 20.5 µs +92.49%
⚡ CPU Simulation chunked_dict_primitive_canonical_into[f64, (1000, 1000, 10)] 186.7 µs 147.3 µs +26.77%
⚡ CPU Simulation missing_stats[16] 84.4 µs 70.6 µs +19.54%
⚡ Walltime starts_with_avx2[8, VarBin] 46.6 µs 39.9 µs +16.8%
⚡ CPU Simulation take_filter_primitive_small_uncached_random_mask_random_indices[4096, 10] 22.7 µs 19.6 µs +16.01%
⚡ CPU Simulation random_i8[0.01] 115.3 µs 101.6 µs +13.43%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing mk/list-transform-captures (c319853) with develop (f62c12f)

Open in CodSpeed

Footnotes

  1. 166 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@mhk197
mhk197 force-pushed the mk/list-transform-captures branch from 412ba6c to 12459ae Compare October 23, 2026 14:02
@mhk197
mhk197 removed this pull request from stack #10614 October 23, 2026 18:14
@mhk197
mhk197 added this pull request to stack #10797 October 26, 2026 12:23
@mhk197
mhk197 force-pushed the mk/list-transform-captures branch from 12459ae to 2cf8fe8 Compare October 26, 2026 16:48
@mhk197
mhk197 force-pushed the mk/list-transform-captures branch from 2cf8fe8 to 776fb89 Compare October 26, 2026 17:01
@mhk197
mhk197 force-pushed the mk/list-transform-captures branch 2 times, most recently from ab2c324 to 6f04986 Compare October 27, 2026 10:44
mhk197 added a commit that referenced this pull request Oct 27, 2026
## Summary

Moves the `vortex-runend` crate into `vortex-array` as
`vortex_array::arrays::runend`, the same way `vortex-dict` moved in
#5289. Once RunEnd is built in, vortex-array code can build run-end
arrays itself. One example is spreading a captured outer value across
each list's elements in `list_transform` (#10702).

## Changes

- `RunEnd` is now a built-in, like `Dict`. It is registered in
`ArraySession::default`, in the default kernel session, and in
`AggregateFnSession::default` (min_max, is_constant, is_sorted, sum).
- The `vortex-runend` crate is removed. `vortex::encodings::runend`
still re-exports the module.
- The benches move to `vortex-array/benches/run_end_*`. CodSpeed picks
them up in the array builders shard.

---------

Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197 mhk197 changed the title Capture values read from outside list_transform lambdas Capture values read from outside list_transform lambdas Oct 27, 2026
Base automatically changed from mk/list-transform-v3 to develop October 27, 2026 11:13
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197
mhk197 force-pushed the mk/list-transform-captures branch from 6f04986 to c319853 Compare October 27, 2026 11:13
@mhk197
mhk197 marked this pull request as ready for review October 27, 2026 11:17

This branch has not been deployed

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

Labels

changelog/feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant