Skip to content

Commit de59365

Browse files
authored
Optimize Array Slicing Performance (dotnet#18778)
1 parent e7abd44 commit de59365

5 files changed

Lines changed: 47 additions & 10 deletions

File tree

docs/release-notes/.FSharp.Core/10.0.100.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
### Changed
88

99
* Random functions support for zero element chosen/sampled ([PR #18568](https://github.com/dotnet/fsharp/pull/18568))
10+
* Optimize array slicing performance. ([PR #18778](https://github.com/dotnet/fsharp/pull/18778))
1011

1112
### Breaking Changes
13+
14+
* 1D array slicing now returns an empty array singleton instead of allocating a new array when the result is empty. ([PR #18778](https://github.com/dotnet/fsharp/pull/18778))

src/FSharp.Core/prim-types.fs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -779,17 +779,17 @@ namespace Microsoft.FSharp.Core
779779

780780
let inline SetArray (target: 'T array) (index:int) (value:'T) = (# "stelem.any !0" type ('T) target index value #)
781781

782-
let inline GetArraySub arr (start:int) (len:int) =
783-
let len = if len < 0 then 0 else len
784-
let dst = zeroCreate len
785-
for i = 0 to len - 1 do
786-
SetArray dst i (GetArray arr (start + i))
787-
dst
788-
789-
let inline SetArraySub arr (start:int) (len:int) (src:_ array) =
790-
for i = 0 to len - 1 do
791-
SetArray arr (start+i) (GetArray src i)
782+
let inline GetArraySub (arr: 'a array) (start:int) (len:int) : 'a array =
783+
if len <= 0 then
784+
[||]
785+
else
786+
let dst = zeroCreate len
787+
Array.Copy(arr, start, dst, 0, len)
788+
dst
792789

790+
let inline SetArraySub (arr: 'T array) (start:int) (len:int) (src: 'T array) =
791+
if len > 0 then
792+
Array.Copy(src, 0, arr, start, len)
793793

794794
let inline GetArray2D (source: 'T[,]) (index1: int) (index2: int) = (# "ldelem.multi 2 !0" type ('T) source index1 index2 : 'T #)
795795

tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,6 +1962,24 @@ type ArrayModule() =
19621962
Assert.AreEqual(arr.[-4..(-3)], ([||]: int array))
19631963

19641964

1965+
[<Fact>]
1966+
member _.``Slicing creates new non-empty arrays`` () =
1967+
let arr = [|1;2;3;4;5;6|]
1968+
1969+
Assert.False(Object.ReferenceEquals(arr[1..3], arr[1..3]))
1970+
Assert.False(Object.ReferenceEquals(arr[0..], arr[0..]))
1971+
Assert.False(Object.ReferenceEquals(arr[..^1], arr[..^1]))
1972+
1973+
[<Fact>]
1974+
member _.``Empty slices are referentially equivalent`` () =
1975+
let arr = [|1;2;3;4;5;6|]
1976+
1977+
Assert.AreEqual(arr[1..-1].Length, 0)
1978+
Assert.True(Object.ReferenceEquals(arr[1..-1], arr[1..-1]))
1979+
1980+
Assert.True(Object.ReferenceEquals(arr[1..-1], ([||]: int array)))
1981+
1982+
19651983
[<Fact>]
19661984
member _.RandomShuffle() =
19671985
let arr = [| 1..20 |]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module ArraySlicing
2+
3+
open BenchmarkDotNet.Attributes
4+
open System
5+
6+
[<SimpleJob(launchCount = 2, warmupCount = 1, iterationCount = 2)>]
7+
[<GcServer(true)>]
8+
[<MemoryDiagnoser>]
9+
[<MarkdownExporterAttribute.GitHub>]
10+
type ArraySlicingBenchmark() =
11+
let b = [| for _ in 1 .. 100_000 -> byte DateTime.Now.Ticks |]
12+
13+
[<Benchmark>]
14+
member _.x () =
15+
b[ 20 .. 4999 ]

tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/MicroPerf.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<Compile Include="Equality\OptionsAndCo.fs" />
2727
<Compile Include="Equality\ExactEquals.fs" />
2828
<Compile Include="Async.fs" />
29+
<Compile Include="ArraySlicing.fs" />
2930
<Compile Include="Conditions.fs" />
3031
<Compile Include="Collections.fs" />
3132
<Compile Include="Program.fs" />

0 commit comments

Comments
 (0)