Skip to content

Commit 468813d

Browse files
Speed up for … in xs -> … in computed collections (#16948)
* Speed up `for … in xs -> …` in computed colls * Use `Array.map` for [|for … in xs -> …|]` when `xs` is an array. * Use `List.map` for [for … in xs -> …]` when `xs` is a list. * Add emitted IL tests * Update release notes * Give it its own language feature * Update release notes * Fantomas --------- Co-authored-by: Vlad Zarytovskii <[email protected]>
1 parent 8e5813c commit 468813d

35 files changed

+2962
-0
lines changed

docs/release-notes/.FSharp.Compiler.Service/8.0.300.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,5 @@
5656
* Reverted [#16348](https://github.com/dotnet/fsharp/pull/16348) `ThreadStatic` `CancellationToken` changes to improve test stability and prevent potential unwanted cancellations. ([PR #16536](https://github.com/dotnet/fsharp/pull/16536))
5757
* Refactored parenthesization API. ([PR #16461])(https://github.com/dotnet/fsharp/pull/16461))
5858
* Optimize some interpolated strings by lowering to string concatenation. ([PR #16556](https://github.com/dotnet/fsharp/pull/16556))
59+
* Speed up `for x in xs -> …` in list & array comprehensions in certain scenarios. ([PR #16948](https://github.com/dotnet/fsharp/pull/16948))
5960
* Integral range optimizations. ([PR #16650](https://github.com/dotnet/fsharp/pull/16650), [PR #16832](https://github.com/dotnet/fsharp/pull/16832), [PR #16947](https://github.com/dotnet/fsharp/pull/16947))

docs/release-notes/.Language/preview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Added
22

3+
* Speed up `for x in xs -> …` in list & array comprehensions in certain scenarios. ([PR #16948](https://github.com/dotnet/fsharp/pull/16948))
34
* Lower integral ranges to fast loops in more cases and optimize list and array construction from ranges. ([PR #16650](https://github.com/dotnet/fsharp/pull/16650), [PR #16832](https://github.com/dotnet/fsharp/pull/16832))
45
* Better generic unmanaged structs handling. ([Language suggestion #692](https://github.com/fsharp/fslang-suggestions/issues/692), [PR #12154](https://github.com/dotnet/fsharp/pull/12154))
56
* Bidirectional F#/C# interop for 'unmanaged' constraint. ([PR #12154](https://github.com/dotnet/fsharp/pull/12154))

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,7 @@ featureBooleanReturningAndReturnTypeDirectedPartialActivePattern,"Boolean-return
15961596
featureEnforceAttributeTargets,"Enforce AttributeTargets"
15971597
featureLowerInterpolatedStringToConcat,"Optimizes interpolated strings in certain cases, by lowering to concatenation"
15981598
featureLowerIntegralRangesToFastLoops,"Optimizes certain uses of the integral range (..) and range-step (.. ..) operators to fast while-loops."
1599+
featureLowerSimpleMappingsInComprehensionsToDirectCallsToMap,"Lowers [for x in xs -> f x] and [|for x in xs -> f x|] to calls to List.map and Array.map when xs is a list or an array, respectively."
15991600
3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
16001601
3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
16011602
3355,tcNotAnIndexerNamedIndexingNotYetEnabled,"The value '%s' is not a function and does not support index notation."

src/Compiler/Facilities/LanguageFeatures.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type LanguageFeature =
8888
| EnforceAttributeTargets
8989
| LowerInterpolatedStringToConcat
9090
| LowerIntegralRangesToFastLoops
91+
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
9192

9293
/// LanguageVersion management
9394
type LanguageVersion(versionText) =
@@ -203,6 +204,7 @@ type LanguageVersion(versionText) =
203204
LanguageFeature.EnforceAttributeTargets, previewVersion
204205
LanguageFeature.LowerInterpolatedStringToConcat, previewVersion
205206
LanguageFeature.LowerIntegralRangesToFastLoops, previewVersion
207+
LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap, previewVersion
206208
]
207209

208210
static let defaultLanguageVersion = LanguageVersion("default")
@@ -349,6 +351,8 @@ type LanguageVersion(versionText) =
349351
| LanguageFeature.EnforceAttributeTargets -> FSComp.SR.featureEnforceAttributeTargets ()
350352
| LanguageFeature.LowerInterpolatedStringToConcat -> FSComp.SR.featureLowerInterpolatedStringToConcat ()
351353
| LanguageFeature.LowerIntegralRangesToFastLoops -> FSComp.SR.featureLowerIntegralRangesToFastLoops ()
354+
| LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap ->
355+
FSComp.SR.featureLowerSimpleMappingsInComprehensionsToDirectCallsToMap ()
352356

353357
/// Get a version string associated with the given feature.
354358
static member GetFeatureVersionString feature =

src/Compiler/Facilities/LanguageFeatures.fsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type LanguageFeature =
7979
| EnforceAttributeTargets
8080
| LowerInterpolatedStringToConcat
8181
| LowerIntegralRangesToFastLoops
82+
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
8283

8384
/// LanguageVersion management
8485
type LanguageVersion =

src/Compiler/Optimize/LowerComputedCollections.fs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,13 +466,29 @@ let (|SimpleMapping|_|) g expr =
466466

467467
| _ -> ValueNone
468468

469+
[<return: Struct>]
470+
let (|Array|_|) g (OptionalCoerce expr) =
471+
if isArray1DTy g (tyOfExpr g expr) then ValueSome expr
472+
else ValueNone
473+
474+
[<return: Struct>]
475+
let (|List|_|) g (OptionalCoerce expr) =
476+
if isListTy g (tyOfExpr g expr) then ValueSome expr
477+
else ValueNone
478+
469479
let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap ilTyForTy overallExpr =
470480
// If ListCollector is in FSharp.Core then this optimization kicks in
471481
if g.ListCollector_tcr.CanDeref then
472482
match overallExpr with
473483
// […]
474484
| SeqToList g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
475485
match overallSeqExpr with
486+
// [for … in xs -> …] (* When xs is a list. *)
487+
| SimpleMapping g (ty1, ty2, List g list, mapping, _, _) when
488+
g.langVersion.SupportsFeature LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap
489+
->
490+
Some (mkCallListMap g m ty1 ty2 mapping list)
491+
476492
// [start..finish]
477493
// [start..step..finish]
478494
| IntegralRange g (rangeTy, (start, step, finish)) when
@@ -495,6 +511,12 @@ let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap ilTyForTy overallExpr
495511
// [|…|]
496512
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
497513
match overallSeqExpr with
514+
// [|for … in xs -> …|] (* When xs is an array. *)
515+
| SimpleMapping g (ty1, ty2, Array g array, mapping, _, _) when
516+
g.langVersion.SupportsFeature LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap
517+
->
518+
Some (mkCallArrayMap g m ty1 ty2 mapping array)
519+
498520
// [|start..finish|]
499521
// [|start..step..finish|]
500522
| IntegralRange g (rangeTy, (start, step, finish)) when

src/Compiler/TypedTree/TcGlobals.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,7 @@ type TcGlobals(
824824
let v_range_step_generic_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeStepGeneric" , None , None , [vara;varb], ([[varaTy];[varbTy];[varaTy]], mkSeqTy varaTy))
825825

826826
let v_array_length_info = makeIntrinsicValRef(fslib_MFArrayModule_nleref, "length" , None , Some "Length" , [vara], ([[mkArrayType 1 varaTy]], v_int_ty))
827+
let v_array_map_info = makeIntrinsicValRef(fslib_MFArrayModule_nleref, "map" , None , Some "Map" , [vara; varb], ([[varaTy --> varbTy]; [mkArrayType 1 varaTy]], mkArrayType 1 varbTy))
827828
let v_array_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray" , None , None , [vara], ([[mkArrayType 1 varaTy]; [v_int_ty]], varaTy))
828829
let v_array2D_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray2D" , None , None , [vara], ([[mkArrayType 2 varaTy];[v_int_ty]; [v_int_ty]], varaTy))
829830
let v_array3D_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray3D" , None , None , [vara], ([[mkArrayType 3 varaTy];[v_int_ty]; [v_int_ty]; [v_int_ty]], varaTy))
@@ -833,6 +834,8 @@ type TcGlobals(
833834
let v_array3D_set_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "SetArray3D" , None , None , [vara], ([[mkArrayType 3 varaTy];[v_int_ty]; [v_int_ty]; [v_int_ty]; [varaTy]], v_unit_ty))
834835
let v_array4D_set_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "SetArray4D" , None , None , [vara], ([[mkArrayType 4 varaTy];[v_int_ty]; [v_int_ty]; [v_int_ty]; [v_int_ty]; [varaTy]], v_unit_ty))
835836

837+
let v_list_map_info = makeIntrinsicValRef(fslib_MFListModule_nleref, "map" , None , Some "Map" , [vara; varb], ([[varaTy --> varbTy]; [mkListTy varaTy]], mkListTy varbTy))
838+
836839
let v_option_toNullable_info = makeIntrinsicValRef(fslib_MFOptionModule_nleref, "toNullable" , None , Some "ToNullable" , [vara], ([[mkOptionTy varaTy]], mkNullableTy varaTy))
837840
let v_option_defaultValue_info = makeIntrinsicValRef(fslib_MFOptionModule_nleref, "defaultValue" , None , Some "DefaultValue" , [vara], ([[varaTy]; [mkOptionTy varaTy]], varaTy))
838841

@@ -1715,9 +1718,11 @@ type TcGlobals(
17151718
member val range_generic_op_vref = ValRefForIntrinsic v_range_generic_op_info
17161719
member val range_step_generic_op_vref = ValRefForIntrinsic v_range_step_generic_op_info
17171720
member val array_get_vref = ValRefForIntrinsic v_array_get_info
1721+
member val array_map_vref = ValRefForIntrinsic v_array_map_info
17181722
member val array2D_get_vref = ValRefForIntrinsic v_array2D_get_info
17191723
member val array3D_get_vref = ValRefForIntrinsic v_array3D_get_info
17201724
member val array4D_get_vref = ValRefForIntrinsic v_array4D_get_info
1725+
member val list_map_vref = ValRefForIntrinsic v_list_map_info
17211726
member val seq_singleton_vref = ValRefForIntrinsic v_seq_singleton_info
17221727
member val seq_collect_vref = ValRefForIntrinsic v_seq_collect_info
17231728
member val nativeptr_tobyref_vref = ValRefForIntrinsic v_nativeptr_tobyref_info
@@ -1778,6 +1783,7 @@ type TcGlobals(
17781783
member _.seq_to_array_info = v_seq_to_array_info
17791784

17801785
member _.array_length_info = v_array_length_info
1786+
member _.array_map_info = v_array_map_info
17811787
member _.array_get_info = v_array_get_info
17821788
member _.array2D_get_info = v_array2D_get_info
17831789
member _.array3D_get_info = v_array3D_get_info
@@ -1787,6 +1793,8 @@ type TcGlobals(
17871793
member _.array3D_set_info = v_array3D_set_info
17881794
member _.array4D_set_info = v_array4D_set_info
17891795

1796+
member _.list_map_info = v_list_map_info
1797+
17901798
member val option_toNullable_info = v_option_toNullable_info
17911799
member val option_defaultValue_info = v_option_defaultValue_info
17921800

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7795,6 +7795,8 @@ let mkCallArrayLength (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g
77957795

77967796
let mkCallArrayGet (g: TcGlobals) m ty e1 idx1 = mkApps g (typedExprForIntrinsic g m g.array_get_info, [[ty]], [ e1 ; idx1 ], m)
77977797

7798+
let mkCallArrayMap (g: TcGlobals) m ty1 ty2 e1 e2 = mkApps g (typedExprForIntrinsic g m g.array_map_info, [[ty1; ty2]], [ e1 ; e2 ], m)
7799+
77987800
let mkCallArray2DGet (g: TcGlobals) m ty e1 idx1 idx2 = mkApps g (typedExprForIntrinsic g m g.array2D_get_info, [[ty]], [ e1 ; idx1; idx2 ], m)
77997801

78007802
let mkCallArray3DGet (g: TcGlobals) m ty e1 idx1 idx2 idx3 = mkApps g (typedExprForIntrinsic g m g.array3D_get_info, [[ty]], [ e1 ; idx1; idx2; idx3 ], m)
@@ -7809,6 +7811,8 @@ let mkCallArray3DSet (g: TcGlobals) m ty e1 idx1 idx2 idx3 v = mkApps g (typedEx
78097811

78107812
let mkCallArray4DSet (g: TcGlobals) m ty e1 idx1 idx2 idx3 idx4 v = mkApps g (typedExprForIntrinsic g m g.array4D_set_info, [[ty]], [ e1 ; idx1; idx2; idx3; idx4; v ], m)
78117813

7814+
let mkCallListMap (g: TcGlobals) m ty1 ty2 e1 e2 = mkApps g (typedExprForIntrinsic g m g.list_map_info, [[ty1; ty2]], [ e1 ; e2 ], m)
7815+
78127816
let mkCallHash (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g m g.hash_info, [[ty]], [ e1 ], m)
78137817

78147818
let mkCallBox (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g m g.box_info, [[ty]], [ e1 ], m)

src/Compiler/TypedTree/TypedTreeOps.fsi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,6 +2036,8 @@ val mkCallArrayLength: TcGlobals -> range -> TType -> Expr -> Expr
20362036

20372037
val mkCallArrayGet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr
20382038

2039+
val mkCallArrayMap: g: TcGlobals -> m: range -> ty1: TType -> ty2: TType -> e1: Expr -> e2: Expr -> Expr
2040+
20392041
val mkCallArray2DGet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Expr
20402042

20412043
val mkCallArray3DGet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Expr -> Expr
@@ -2050,6 +2052,8 @@ val mkCallArray3DSet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Exp
20502052

20512053
val mkCallArray4DSet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Expr -> Expr -> Expr -> Expr
20522054

2055+
val mkCallListMap: g: TcGlobals -> m: range -> ty1: TType -> ty2: TType -> e1: Expr -> e2: Expr -> Expr
2056+
20532057
val mkCallHash: TcGlobals -> range -> TType -> Expr -> Expr
20542058

20552059
val mkCallBox: TcGlobals -> range -> TType -> Expr -> Expr

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.ja.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.ko.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)