Skip to content

Read values from outside list_transform lambdas through an environment - #10853

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

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

Conversation

@mhk197

@mhk197 mhk197 commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Summary

An alternative to #10702, to compare against it. A lambda passed to list_transform can read values from outside it: a column, another lambda's parameter, or row_idx(). Unlike #10702, the lambda is never rewritten. The call takes what the body reads as one more child, the lambda's environment, and binding resolves $ and outer parameters against it.

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

Changes

  • Lambda::environment() packs what the body reads from outside into one struct, cut down to the fields it reads: $ for the row, an outer parameter under its own name, and row_idx() or row_count() under its id.
  • list_transform(list, lambda) passes the environment as a second child, so replace, field pruning and the scan's row_idx() see it.
  • Lambda::bind and return_dtype take the environment's fields. The lambda's inputs are its parameters followed by those fields, so @p still binds as $.p, and $ binds as $.$.
  • Lambda::new accepts outside reads; binding without the environment is the error. A parameter can't be named $.
  • Execution repeats each row's environment for every element of its list with take. A list view with an environment is copied apart into a list first, since overlapping views share elements between rows.
  • list_transform stays strict: the environment is a non-nullable struct, so it is never null.

Compared with #10702

#10702 This PR
Lambda as stored and printed rewritten: (x, a) -> (@x + @a) as written: (x) -> (@x + $.a)
Extra children one per value read one struct
Parameter names it invents a, x_2, … none
Strict only without outside reads always
Rebinds when a rewrite changes an input's dtype yes (BoundLambda::rebind) no: errors, as list_transform does today
Non-test lines +271 −71, about 35 of them for rebind +313 −80

Testing

  • Lambda: the environment for a column, a nested field, a struct and its field, $, an outer parameter and row_count(). Binding without the environment is an error.
  • list_transform: Capture values read from outside list_transform lambdas #10702's cases (a column, null and constant values, overlapping list views, nested lambdas reading a column or the outer parameter), plus a field of a nullable struct.
  • replace($, $.s) rewrites the environment and leaves the body alone (vortex-expr). A scan reads row_idx() inside a lambda (vortex-layout).
  • Locally: cargo nextest run -p vortex-array -p vortex-layout -p vortex-expr (5,459 passed), clippy, fmt, and the new doc examples.

Follow-up

@codspeed

codspeed Bot commented Oct 27, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 13.29%

3 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

⚠️ 20 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

ℹ️ 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

⚡ 3 improved benchmarks
❌ 9 regressed benchmarks
✅ 2427 untouched benchmarks
⏩ 116 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Walltime filter_neon[8] 867 ns 1,779 ns -51.26%
❌ Walltime threshold_neon[8] 1.3 µs 2.3 µs -44.13%
❌ Walltime threshold_neon[64] 2.5 µs 3.5 µs -28.51%
❌ Walltime filter_neon[64] 1.8 µs 2.3 µs -22.33%
❌ CPU Simulation encode_primitives[f32, (1000, 2)] 116.4 µs 146.9 µs -20.73%
❌ Walltime threshold_neon[80] 3.3 µs 4.2 µs -20.66%
❌ CPU Simulation missing_stats[16] 70.8 µs 84.8 µs -16.47%
❌ Walltime filter_neon[80] 2.6 µs 3 µs -13.7%
❌ Walltime add_constant_shapes_neon[(32768, ConstantPerRow)] 14.3 µs 16.1 µs -11.49%
⚡ Walltime mul_u64_nonnull_neon 39.1 µs 20.6 µs +89.27%
⚡ CPU Simulation chunked_dict_primitive_canonical_into[f64, (1000, 1000, 10)] 200.6 µs 146.3 µs +37.16%
⚡ CPU Simulation random_i8[0.01] 115.4 µs 100.7 µs +14.64%

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-env (77cddee) with develop (cd45524)

Open in CodSpeed

Footnotes

  1. 116 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. ↩

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant