Skip to content

Commit

Permalink
Faster equality in generic contexts (#16615)
Browse files Browse the repository at this point in the history
  • Loading branch information
psfinaki authored Mar 4, 2024
1 parent b9519e7 commit 9877cfe
Show file tree
Hide file tree
Showing 21 changed files with 836 additions and 76 deletions.
6 changes: 3 additions & 3 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ stages:
condition: always()

# Test trimming on Windows
- job: Build_And_Test_Trimming_Windows
- job: Build_And_Test_AOT_Windows
pool:
name: $(DncEngPublicBuildPool)
demands: ImageOverride -equals $(WindowsMachineQueueName)
Expand Down Expand Up @@ -825,9 +825,9 @@ stages:
env:
NativeToolsOnMachine: true
displayName: Initial build and prepare packages.
- script: $(Build.SourcesDirectory)/tests/AheadOfTime/Trimming/check.cmd
- script: $(Build.SourcesDirectory)/tests/AheadOfTime/check.cmd
displayName: Build, trim, publish and check the state of the trimmed app.
workingDirectory: $(Build.SourcesDirectory)/tests/AheadOfTime/Trimming
workingDirectory: $(Build.SourcesDirectory)/tests/AheadOfTime
- task: PublishPipelineArtifact@1
displayName: Publish Trim Tests Logs
inputs:
Expand Down
2 changes: 2 additions & 0 deletions docs/release-notes/.FSharp.Core/8.0.300.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
### Added

* Minor tweaks to inline specifications to support Visibility PR ([PR #15484](https://github.com/dotnet/fsharp/pull/15484), [#PR 16427](https://github.com/dotnet/fsharp/pull/15484)
* Optimize equality in generic contexts. ([PR #16615](https://github.com/dotnet/fsharp/pull/16615))

### Fixed

* Preserve original stack traces in resumable state machines generated code if available. ([PR #16568](https://github.com/dotnet/fsharp/pull/16568))
2 changes: 1 addition & 1 deletion eng/Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ try {
}

if ($testAOT) {
Push-Location "$RepoRoot\tests\AheadOfTime\Trimming"
Push-Location "$RepoRoot\tests\AheadOfTime"
./check.cmd
Pop-Location
}
Expand Down
252 changes: 190 additions & 62 deletions src/FSharp.Core/prim-types.fs

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions tests/AheadOfTime/Equality/Equality.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<PropertyGroup>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<DisableImplicitLibraryPacksFolder>true</DisableImplicitLibraryPacksFolder>
<PublishTrimmed>true</PublishTrimmed>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

<PropertyGroup>
<DotnetFscCompilerPath>$(MSBuildThisFileDirectory)../../../artifacts/bin/fsc/Release/net8.0/fsc.dll</DotnetFscCompilerPath>
<Fsc_DotNET_DotnetFscCompilerPath>$(MSBuildThisFileDirectory)../../../artifacts/bin/fsc/Release/net8.0/fsc.dll</Fsc_DotNET_DotnetFscCompilerPath>
<FSharpPreferNetFrameworkTools>False</FSharpPreferNetFrameworkTools>
<FSharpPrefer64BitTools>True</FSharpPrefer64BitTools>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>

<Import Project="$(MSBuildThisFileDirectory)../../../eng/Versions.props" />

<ItemGroup>
<PackageReference Include="FSharp.Core" Version="$(FSharpCorePreviewPackageVersionValue)"/>
</ItemGroup>

</Project>
77 changes: 77 additions & 0 deletions tests/AheadOfTime/Equality/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
open System.Collections.Generic
open NonStructuralComparison

let failures = HashSet<string>()

let reportFailure (s: string) =
stderr.Write " NO: "
stderr.WriteLine s
failures.Add s |> ignore

let test testName x y =
let result = HashIdentity.Structural.Equals(x, y)
if result = false
then
stderr.WriteLine($"\n***** {testName}: Expected: 'true' Result: '{result}' = FAIL\n");
reportFailure testName

module BasicTypes =
test "test000" true true
test "test001" 1y 1y
test "test002" 1uy 1uy
test "test003" 1s 1s
test "test004" 1us 1us
test "test005" 1 1
test "test006" 1u 1u
test "test007" 1L 1L
test "test008" 1UL 1UL
test "test009" (nativeint 1) (nativeint 1)
test "test010" (unativeint 1) (unativeint 1)
test "test011" 'a' 'a'
test "test012" "a" "a"
test "test013" 1m 1m
test "test014" 1.0 1.0
test "test015" 1.0f 1.0f

module Arrays =
test "test100" [|1|] [|1|]
test "test101" [|1L|] [|1L|]
test "test102" [|1uy|] [|1uy|]
test "test103" [|box 1|] [|box 1|]

module Structs =
test "test200" struct (1, 1) struct (1, 1)
test "test201" struct (1, 1, 1) struct (1, 1, 1)
test "test202" struct (1, 1, 1, 1) struct (1, 1, 1, 1)
test "test203" struct (1, 1, 1, 1, 1) struct (1, 1, 1, 1, 1)
test "test204" struct (1, 1, 1, 1, 1, 1) struct (1, 1, 1, 1, 1, 1)
test "test205" struct (1, 1, 1, 1, 1, 1, 1) struct (1, 1, 1, 1, 1, 1, 1)
test "test206" struct (1, 1, 1, 1, 1, 1, 1, 1) struct (1, 1, 1, 1, 1, 1, 1, 1)

module OptionsAndCo =
open System

test "test301" (Some 1) (Some 1)
test "test302" (ValueSome 1) (ValueSome 1)
test "test303" (Ok 1) (Ok 1)
test "test304" (Nullable 1) (Nullable 1)

module Enums =
open System

type SomeEnum =
| Case0 = 0
| Case1 = 1

test "test401" (enum<SomeEnum>(1)) (enum<SomeEnum>(1))
test "test402" (enum<DayOfWeek>(1)) (enum<DayOfWeek>(1))

[<EntryPoint>]
let main _ =
match failures with
| set when set.Count = 0 ->
stdout.WriteLine "All tests passed"
exit 0
| _ ->
stdout.WriteLine "Some tests failed"
exit 1
32 changes: 32 additions & 0 deletions tests/AheadOfTime/Equality/check.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Write-Host "Publish and Execute: net8.0 - Equality"

$build_output = dotnet publish -restore -c release -f:net8.0 $(Join-Path $PSScriptRoot Equality.fsproj)

# Checking that the build succeeded
if ($LASTEXITCODE -ne 0)
{
Write-Error "Build failed with exit code ${LASTEXITCODE}"
Write-Error "${build_output}" -ErrorAction Stop
}

$process = Start-Process `
-FilePath $(Join-Path $PSScriptRoot bin\release\net8.0\win-x64\publish\Equality.exe) `
-Wait `
-NoNewWindow `
-PassThru `
-RedirectStandardOutput $(Join-Path $PSScriptRoot output.txt)

$output = Get-Content $(Join-Path $PSScriptRoot output.txt)

# Checking that it is actually running.
if ($LASTEXITCODE -ne 0)
{
Write-Error "Test failed with exit code ${LASTEXITCODE}" -ErrorAction Stop
}

# Checking that the output is as expected.
$expected = "All tests passed"
if ($output -ne $expected)
{
Write-Error "Test failed with unexpected output:`nExpected:`n`t${expected}`nActual`n`t${output}" -ErrorAction Stop
}
2 changes: 0 additions & 2 deletions tests/AheadOfTime/Trimming/check.cmd

This file was deleted.

4 changes: 2 additions & 2 deletions tests/AheadOfTime/Trimming/check.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function CheckTrim($root, $tfm, $outputfile, $expected_len) {
# error NETSDK1124: Trimming assemblies requires .NET Core 3.0 or higher.

# Check net7.0 trimmed assemblies
CheckTrim -root "SelfContained_Trimming_Test" -tfm "net8.0" -outputfile "FSharp.Core.dll" -expected_len 287232
CheckTrim -root "SelfContained_Trimming_Test" -tfm "net8.0" -outputfile "FSharp.Core.dll" -expected_len 284160

# Check net8.0 trimmed assemblies
CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net8.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 8820736
CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net8.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 8817152
3 changes: 3 additions & 0 deletions tests/AheadOfTime/check.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off
powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Trimming\check.ps1""""
powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Equality\check.ps1""""
10 changes: 5 additions & 5 deletions tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/Benchmarks.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
namespace TaskPerf
namespace MicroPerf

open BenchmarkDotNet.Running

module Main =
module Main =

[<EntryPoint>]
let main _ =
let main args =
printfn "Running benchmarks..."
let _ = BenchmarkRunner.Run<Collections.CollectionsBenchmark>()
0
BenchmarkSwitcher.FromAssembly(typeof<Equality.FSharpCoreFunctions>.Assembly).Run(args) |> ignore
0
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Equality

open BenchmarkDotNet.Attributes

type Arrays() =

let numbers = Array.init 1000 id

[<Benchmark>]
member _.Int32() =
numbers |> Array.countBy (fun n -> [| n % 7 |])

[<Benchmark>]
member _.Int64() =
numbers |> Array.countBy (fun n -> [| int64 (n % 7) |])

[<Benchmark>]
member _.Byte() =
numbers |> Array.countBy (fun n -> [| byte (n % 7) |])

[<Benchmark>]
member _.Obj() =
numbers |> Array.countBy (fun n -> [| box (n % 7) |])
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace Equality

open BenchmarkDotNet.Attributes

[<MemoryDiagnoser>]
type BasicTypes() =

let bools = Array.init 1000 (fun n -> n % 2 = 0)
let sbytes = Array.init 1000 sbyte
let bytes = Array.init 1000 byte
let int16s = Array.init 1000 int16
let uint16s = Array.init 1000 uint16
let int32s = Array.init 1000 id
let uint32s = Array.init 1000 uint32
let int64s = Array.init 1000 int64
let uint64s = Array.init 1000 uint64
let intptrs = Array.init 1000 nativeint
let uintptrs = Array.init 1000 unativeint
let chars = Array.init 1000 char
let strings = Array.init 1000 string
let decimals = Array.init 1000 decimal

[<Benchmark>]
member _.Bool() = bools |> Array.distinct

[<Benchmark>]
member _.SByte() = sbytes |> Array.distinct

[<Benchmark>]
member _.Byte() = bytes |> Array.distinct

[<Benchmark>]
member _.Int16() = int16s |> Array.distinct

[<Benchmark>]
member _.UInt16() = uint16s |> Array.distinct

[<Benchmark>]
member _.Int32() = int32s |> Array.distinct

[<Benchmark>]
member _.UInt32() = uint32s |> Array.distinct

[<Benchmark>]
member _.Int64() = int64s |> Array.distinct

[<Benchmark>]
member _.UInt64() = uint64s |> Array.distinct

[<Benchmark>]
member _.IntPtr() = intptrs |> Array.distinct

[<Benchmark>]
member _.UIntPtr() = uintptrs |> Array.distinct

[<Benchmark>]
member _.Char() = chars |> Array.distinct

[<Benchmark>]
member _.String() = strings |> Array.distinct

[<Benchmark>]
member _.Decimal() = decimals |> Array.distinct
Loading

0 comments on commit 9877cfe

Please sign in to comment.