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

Faster equality in generic contexts #16615

Merged
merged 2 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
psfinaki marked this conversation as resolved.
Show resolved Hide resolved
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)"/>
T-Gro marked this conversation as resolved.
Show resolved Hide resolved
</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""""
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
Loading