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

Computed collections: optimize simple mappings with preludes #17067

Merged
merged 16 commits into from May 7, 2024

Conversation

brianrourkeboll
Copy link
Contributor

@brianrourkeboll brianrourkeboll commented Apr 18, 2024

Description

Checklist

  • Test cases added.
  • Release notes entry updated.

Note to reviewers

When looking at the baselines, it is useful to compare each function's IL with its source, paying specific attention to the order in which things happen: e.g., if two functions f and g are passed in and invoked in that order, ensure that you see the ldarg.0 instruction (for f) before the ldarg.1 instruction (for g), etc. If these happen outside of the for-loop in the source, likewise ensure that they do so in the IL: e.g., if f and g are called before the loop in the source, and if the loop ends in blt.un.s IL_0022 in the IL, ensure that f and g are loaded and called before IL_0022 (or before the call to map, depending).

(I have looked, and it looks right to me.)

* E.g.,

  ```fsharp
  [let y = f () in for … in … -> …]
  ```

  and

  ```fsharp
  [f (); g (); for … in … -> …]
  ```

  are transformed into something like

  ```fsharp
  let y = f () in [for … in … -> …]
  ```

  and

  ```fsharp
  f (); g (); [for … in … -> …]
  ```

  so that the existing pattern recognition of simple mappings and
  integral ranges may succeed.
Copy link
Contributor

github-actions bot commented Apr 18, 2024

❗ Release notes required


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/8.0.400.md

@brianrourkeboll brianrourkeboll changed the title Optimize simple mappings with preludes Computed collections: optimize simple mappings with preludes Apr 18, 2024
@brianrourkeboll brianrourkeboll marked this pull request as ready for review April 18, 2024 17:06
@brianrourkeboll brianrourkeboll requested a review from a team as a code owner April 18, 2024 17:06
@T-Gro
Copy link
Member

T-Gro commented Apr 19, 2024

Did you manage to VS-breakpoint capabilities and looking into the values, such as seeing the value of z after let z = g() in your samples?

@brianrourkeboll
Copy link
Contributor Author

@T-Gro

Did you manage to VS-breakpoint capabilities and looking into the values, such as seeing the value of z after let z = g() in your samples?

Thanks for the reminder, I had been meaning to do that. I'll add something to TheBigFileOfDebugStepping.fsx and make sure it behaves as expected.

@brianrourkeboll
Copy link
Contributor Author

@T-Gro

Did you manage to VS-breakpoint capabilities and looking into the values, such as seeing the value of z after let z = g() in your samples?

This particular part is fine, but it seems like the non-inlined call to map from #16948 causes the debug points in the mapping body not to be hit.

Maybe I'll just go ahead and emit loops directly for that instead. Since that will affect more baselines, would you rather I did that in a separate PR?

@T-Gro
Copy link
Member

T-Gro commented Apr 29, 2024

@T-Gro

Did you manage to VS-breakpoint capabilities and looking into the values, such as seeing the value of z after let z = g() in your samples?

This particular part is fine, but it seems like the non-inlined call to map from #16948 causes the debug points in the mapping body not to be hit.

Maybe I'll just go ahead and emit loops directly for that instead. Since that will affect more baselines, would you rather I did that in a separate PR?

That is fine, diff in baselines is expected.
Please, do it here in this PR.

* Now emit fast map loops directly (instead of calling Array.map and
  List.map).

* Use better ranges for debugging. I don't think these are perfect, but
  the ranges used for loops are already inconsistent, so I am not
  exactly sure what perfect would look like. The emission of inlined
  loops does make stepping through the mapping body work right, though.
@psfinaki
Copy link
Member

Hey Brian, this looks promising - do you have any benchmarks for this also?

@brianrourkeboll
Copy link
Contributor Author

Hmm @T-Gro, it seems like the debugging experience for for-loops and computed collection expressions is already pretty inconsistent, which makes it tough to be sure that I'm not regressing anything here.

These reproduce for me if I compile an fsx file using the following command (and in a console app) using the shipped 8.0.204 SDK:

dotnet "C:\Program Files\dotnet\sdk\8.0.204\FSharp\fsc.dll" --debug+ --tailcalls- --optimize- .\tests\walkthroughs\DebugStepping\Comparison.fsx

For example, stepping through a simple, top-level for-loop already behaves differently depending on whether the thing being enumerated over is a range, a sequence, an array, or a list:

bare_loop_debug_comparison.mp4

There are also some scenarios where breakpoints already cannot be set in computed collections, depending on the combination of comprehension type (list, array, seq), enumerable type (list, array, seq, range), simple body versus sequential expression body, explicit yield versus implicit yield, etc.:

unsettable_breakpoints_2.mp4

I think the best I can do is try to keep the experience with this update as close as possible to the status quo. I'll let you know when I think it's close enough.

@brianrourkeboll
Copy link
Contributor Author

@psfinaki

do you have any benchmarks for this also?

I reran the benchmarks from #16948, and the results were the same within the margin of error. Those benchmarks were just testing an identity mapping; I'm sure it's possible to come up with a benchmark where you could notice the difference between the inlined loop and the closure invocation in List.map/Array.map, but I think the difference between those two is going to be dwarfed by the difference between either of them and the original IEnumerable<_>/ArrayCollector-based implementation anyway.

@T-Gro
Copy link
Member

T-Gro commented May 2, 2024

I think the best I can do is try to keep the experience with this update as close as possible to the status quo. I'll let you know when I think it's close enough.

Thanks for looking into this and checking it Brian. Keeping the status quo working is a good goal :-).

@psfinaki
Copy link
Member

psfinaki commented May 2, 2024

@psfinaki

do you have any benchmarks for this also?

I reran the benchmarks from #16948, and the results were the same within the margin of error. Those benchmarks were just testing an identity mapping; I'm sure it's possible to come up with a benchmark where you could notice the difference between the inlined loop and the closure invocation in List.map/Array.map, but I think the difference between those two is going to be dwarfed by the difference between either of them and the original IEnumerable<_>/ArrayCollector-based implementation anyway.

Alright, yeah that makes sense.

@brianrourkeboll
Copy link
Contributor Author

FYI, I probably won't have a chance to finish this up till next week.

@brianrourkeboll brianrourkeboll mentioned this pull request May 6, 2024
@brianrourkeboll
Copy link
Contributor Author

@T-Gro @psfinaki I believe this is ready now.

Copy link
Member

@psfinaki psfinaki left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for restoring the walkthrough also.
Good job 🚀

@T-Gro T-Gro merged commit fc42ec9 into dotnet:main May 7, 2024
32 checks passed
@brianrourkeboll brianrourkeboll deleted the computed-collections-prelude branch May 7, 2024 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

3 participants