Skip to content

Commit ead6c90

Browse files
committed
hello
1 parent baf3ef5 commit ead6c90

File tree

11 files changed

+374
-0
lines changed

11 files changed

+374
-0
lines changed

FastSharpCore.sln

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FastSharpCore", "FastSharpCore\FastSharpCore.fsproj", "{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{66320409-64EC-F7C5-3DEF-65E7510DAAD1}"
9+
EndProject
10+
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "StringReplicate", "benchmarks\StringReplicate\StringReplicate.fsproj", "{7DF4F70A-7DF8-461D-AC50-F1515161B04D}"
11+
EndProject
12+
Global
13+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
14+
Debug|Any CPU = Debug|Any CPU
15+
Debug|x64 = Debug|x64
16+
Debug|x86 = Debug|x86
17+
Release|Any CPU = Release|Any CPU
18+
Release|x64 = Release|x64
19+
Release|x86 = Release|x86
20+
EndGlobalSection
21+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
22+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Debug|x64.ActiveCfg = Debug|Any CPU
25+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Debug|x64.Build.0 = Debug|Any CPU
26+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Debug|x86.ActiveCfg = Debug|Any CPU
27+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Debug|x86.Build.0 = Debug|Any CPU
28+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Release|x64.ActiveCfg = Release|Any CPU
31+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Release|x64.Build.0 = Release|Any CPU
32+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Release|x86.ActiveCfg = Release|Any CPU
33+
{B0E5DB62-9865-452F-9E1F-7132C7CC8A87}.Release|x86.Build.0 = Release|Any CPU
34+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Debug|x64.ActiveCfg = Debug|Any CPU
37+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Debug|x64.Build.0 = Debug|Any CPU
38+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Debug|x86.ActiveCfg = Debug|Any CPU
39+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Debug|x86.Build.0 = Debug|Any CPU
40+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Release|x64.ActiveCfg = Release|Any CPU
43+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Release|x64.Build.0 = Release|Any CPU
44+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Release|x86.ActiveCfg = Release|Any CPU
45+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D}.Release|x86.Build.0 = Release|Any CPU
46+
EndGlobalSection
47+
GlobalSection(SolutionProperties) = preSolution
48+
HideSolutionNode = FALSE
49+
EndGlobalSection
50+
GlobalSection(NestedProjects) = preSolution
51+
{7DF4F70A-7DF8-461D-AC50-F1515161B04D} = {66320409-64EC-F7C5-3DEF-65E7510DAAD1}
52+
EndGlobalSection
53+
EndGlobal

FastSharpCore/FastSharpCore.fsproj

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<Compile Include="prelude.fs" />
10+
<Compile Include="string.fsi" />
11+
<Compile Include="string.fs" />
12+
<Compile Include="array.fsi" />
13+
<Compile Include="array.fs" />
14+
</ItemGroup>
15+
16+
</Project>

FastSharpCore/array.fs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
namespace FastSharpCore
2+
3+
open System
4+
open System.Numerics
5+
open System.Runtime.Intrinsics
6+
open System.Runtime.InteropServices
7+
8+
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
9+
[<RequireQualifiedAccess>]
10+
module FArray =
11+
[<CompiledName("Create")>]
12+
let create (count: int) (value: 'T) =
13+
// Negative count check is built into zeroCreate
14+
let array: 'T array = Array.zeroCreate count
15+
16+
// Instead of looping, we use Span to fill the array
17+
array.AsSpan().Fill value
18+
19+
array
20+
21+
22+
let concatArrays (arrs: 'T array array) : 'T array =
23+
let mutable acc = 0
24+
25+
for h in arrs do
26+
acc <- acc + h.Length
27+
28+
let res = Array.zeroCreate acc
29+
30+
let mutable span = res.AsSpan()
31+
32+
for array in arrs do
33+
array.AsSpan().CopyTo span
34+
35+
span <- span.Slice array.Length
36+
37+
res
38+
39+
40+
[<CompiledName("Concat")>]
41+
let concat (arrays: seq<'T array>) =
42+
if isNull arrays then
43+
nameof arrays |> nullArg
44+
45+
match arrays with
46+
| :? ('T array array) as ts -> concatArrays ts // avoid a clone, since we only read the array
47+
| _ -> Seq.toArray arrays |> concatArrays
48+
49+
50+
// [<CompiledName("Sum")>]
51+
// let inline sum (array: ^T array) : ^T =
52+
// if not Vector< ^T>.IsSupported || array.Length <= Vector< ^T>.Count then
53+
54+
// let mutable acc = LanguagePrimitives.GenericZero< ^T>
55+
56+
// for item in array do
57+
// acc <- Checked.(+) acc item
58+
59+
// acc
60+
// elif
61+
// typeof< ^T> = typeof<int16>
62+
// || typeof< ^T> = typeof<int32>
63+
// || typeof< ^T> = typeof<int64>
64+
// then
65+
// let mutable acc: 'T Vector = Vector<'T>.Zero
66+
67+
// let vectorSpan = MemoryMarshal.Cast<'T, Vector<'T>>(array.AsSpan())
68+
69+
// for vec in vectorSpan do
70+
// acc <- Vector.Add(acc, vec)
71+
72+
// let mutable sum = Vector.Sum acc
73+
74+
// // sum the remaining elements
75+
// for i = vectorSpan.Length * Vector<'T>.Count to array.Length - 1 do
76+
// let newSum = Checked.(+) sum array.[i]
77+
78+
// // if needs check for overflow
79+
80+
// sum
81+
82+
// else
83+
// let mutable acc = LanguagePrimitives.GenericZero< ^T>
84+
85+
// for item in array do
86+
// acc <- Checked.(+) acc item
87+
88+
// acc

FastSharpCore/array.fsi

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
namespace FastSharpCore
2+
3+
/// <summary>Faster F# FSharp.Core.Array module functions.</summary>
4+
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
5+
[<RequireQualifiedAccess>]
6+
module FArray =
7+
/// <summary>Creates an array whose elements are all initially the given value.</summary>
8+
///
9+
/// <param name="count">The length of the array to create.</param>
10+
/// <param name="value">The value for the elements.</param>
11+
///
12+
/// <returns>The created array.</returns>
13+
///
14+
/// <exception cref="T:System.ArgumentException">Thrown when count is negative.</exception>
15+
///
16+
/// <example id="create-1">
17+
/// <code lang="fsharp">
18+
/// Array.create 4 "a"
19+
/// </code>
20+
/// Evaluates to a new array containing<c>[| "a"; "a"; "a"; "a" |]</c>.
21+
/// </example>
22+
///
23+
/// <example id="create-2">
24+
/// <code lang="fsharp">
25+
/// let cell = ref "a"
26+
/// let array = Array.create 2 cell
27+
/// cell.Value &lt;- "b"
28+
///
29+
/// </code>
30+
/// Before evaluation of the last line, <c>array</c> contains<c>[| { contents = "a"}; { contents = "a"} |]</c>.
31+
/// After evaluation of the last line <c>array</c> contains<c>[| { contents = "b"}; { contents = "b"} |]</c>.
32+
/// Note each entry in the array is the same mutable cell object.
33+
/// </example>
34+
[<CompiledName("Create")>]
35+
val create: count: int -> value: 'T -> 'T array
36+
37+
38+
/// <summary>Builds a new array that contains the elements of each of the given sequence of arrays.</summary>
39+
///
40+
/// <param name="arrays">The input sequence of arrays.</param>
41+
///
42+
/// <returns>The concatenation of the sequence of input arrays.</returns>
43+
///
44+
/// <exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
45+
///
46+
/// <example id="concat-1">
47+
/// <code lang="fsharp">
48+
/// let inputs = [ [| 1; 2 |]; [| 3 |]; [| 4; 5 |] ]
49+
///
50+
/// inputs |> Array.concat
51+
/// </code>
52+
/// Evaluates to <c>[| 1; 2; 3; 4; 5 |]</c>
53+
/// </example>
54+
[<CompiledName("Concat")>]
55+
val concat: arrays: seq<'T array> -> 'T array
56+
57+
58+
59+
// [<CompiledName("Sum")>]
60+
// val inline sum: array: ^T array -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T)

FastSharpCore/prelude.fs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace FastSharpCore
2+
3+
#nowarn "9"
4+
5+
open System
6+
open FSharp.NativeInterop
7+
8+
[<AutoOpen>]
9+
module internal prelude =
10+
let inline stackalloc<'a when 'a: unmanaged> (length: int) : Span<'a> =
11+
Span<'a>(NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr, length)

FastSharpCore/string.fs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
namespace FastSharpCore
2+
3+
open System
4+
5+
/// <summary>Faster F# FSharp.Core.String module functions.</summary>
6+
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
7+
[<RequireQualifiedAccess>]
8+
module FString =
9+
[<CompiledName("Replicate")>]
10+
let replicate (count: int) (str: string) =
11+
if count < 0 then
12+
invalidArg (nameof count) "count must be non-negative."
13+
14+
// Returns 0 if the string is empty instead of throwing an exception
15+
let len = String.length str
16+
17+
if len = 0 || count = 0 then
18+
String.Empty
19+
20+
elif len = 1 then
21+
new string (str[0], count)
22+
23+
elif count <= 4 then
24+
match count with
25+
| 1 -> str
26+
| 2 -> String.Concat(str, str)
27+
| 3 -> String.Concat(str, str, str)
28+
| _ -> String.Concat(str, str, str, str)
29+
30+
else
31+
String.Create(
32+
count * str.Length,
33+
str,
34+
fun buffer original ->
35+
// This lambda should not take in any additional variables from the surrounding context to avoid heap allocation.
36+
37+
// The overall logic is identical to the original F# implementation
38+
// except that buffer is exactly 1 element larger for a null terminator
39+
original.AsSpan().CopyTo buffer
40+
41+
let mutable i = original.Length
42+
43+
while i * 2 < buffer.Length - 1 do
44+
buffer.Slice(0, i).CopyTo(buffer.Slice i)
45+
i <- i * 2
46+
47+
let finalSize = buffer.Length - 1 - i
48+
49+
buffer.Slice(0, finalSize).CopyTo(buffer.Slice i)
50+
)

FastSharpCore/string.fsi

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace FastSharpCore
2+
3+
/// <summary>Faster F# FSharp.Core.String module functions.</summary>
4+
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
5+
[<RequireQualifiedAccess>]
6+
module FString =
7+
/// <summary> Returns a string by concatenating <c>count</c> instances of <c>str</c>.</summary>
8+
///
9+
/// <param name="count">The number of copies of the input string will be copied.</param>
10+
/// <param name="str">The input string.</param>
11+
///
12+
/// <returns>The concatenated string.</returns>
13+
/// <exception cref="T:System.ArgumentException">Thrown when <c>count</c> is negative.</exception>
14+
///
15+
/// <example id="replicate-1">
16+
/// <code lang="fsharp">
17+
/// "Do it!" |> String.replicate 3
18+
/// </code>
19+
/// Evaluates to <c>"Do it!Do it!Do it!"</c>.
20+
/// </example>
21+
[<CompiledName("Replicate")>]
22+
val replicate: count: int -> str: string -> string

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,24 @@
11
# FastSharpCore
22
FSharp.Core, faster
3+
4+
## Why?
5+
6+
Fsharp.Core, targeting .NET Standard 2.0, without additional dependencies, cannot currently use .NET's Span type, or many vectorization intrinsics to accelerate its core functions. This library aims to provide faster implementations to certain FSharp.Core functions by using these features by targeting a newer version of .NET and dropping support for `netstandard2.0`.
7+
8+
## Implemented:
9+
10+
All functions are implemented to be drop in replacements for the existing FSharp.Core functions.
11+
12+
### `FString.replicate` (replaces [String.replicate](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-stringmodule.html#replicate))
13+
14+
Uses `Span<char>` and a special overload of `String.Create` with a `SpanAction` to fill the string. Removes the need for intermediate allocations and copies.
15+
16+
[Source](./FastSharpCore/string.fs)
17+
[Benchmarks](./benchmarks/StringReplicate/)
18+
19+
### `FArray.create` (replaces [Array.create](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-arraymodule.html#create))
20+
21+
Uses `Span.Fill` instead of looping over the array to fill it.
22+
23+
24+
This is a work in progress, help is appreciated!

benchmarks/StringReplicate/Program.fs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
open BenchmarkDotNet.Attributes
2+
open BenchmarkDotNet.Running
3+
4+
open FastSharpCore
5+
6+
[<MemoryDiagnoser>]
7+
type StringReplicate() =
8+
[<Benchmark>]
9+
member _.Existing() =
10+
String.replicate 1000 "hello"
11+
12+
[<Benchmark>]
13+
member _.StringCreate() =
14+
FString.replicate 1000 "hello"
15+
16+
let _ = BenchmarkRunner.Run<StringReplicate>()

benchmarks/StringReplicate/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## String Replicate Benchmarks
2+
3+
```
4+
5+
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.2894)
6+
AMD Ryzen 7 5800X, 1 CPU, 16 logical and 8 physical cores
7+
.NET SDK 9.0.200
8+
[Host] : .NET 9.0.2 (9.0.225.6610), X64 RyuJIT AVX2 DEBUG
9+
DefaultJob : .NET 9.0.2 (9.0.225.6610), X64 RyuJIT AVX2
10+
11+
12+
```
13+
| Method | Mean | Error | StdDev | Median | Gen0 | Gen1 | Allocated |
14+
|------------- |---------:|---------:|---------:|---------:|-------:|-------:|----------:|
15+
| Existing | 670.8 ns | 13.45 ns | 31.16 ns | 663.5 ns | 1.2026 | 0.0219 | 19.62 KB |
16+
| StringCreate | 333.1 ns | 6.68 ns | 15.87 ns | 326.7 ns | 0.6032 | - | 9.85 KB |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<Compile Include="Program.fs" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\..\FastSharpCore\FastSharpCore.fsproj" />
18+
</ItemGroup>
19+
20+
</Project>

0 commit comments

Comments
 (0)