#20229 ships FSharp.Core with a net10.0 TFM. A #if NET8_0_OR_GREATER / #if NET block therefore compiles only into the net asset — netstandard2.0/netstandard2.1 stay bit-identical. This tracks the additive, guarded fast paths that unlocks.
Important gate distinction some span APIs are already in netstandard2.1, so FSharp.Core's ns2.1 asset could use them too — those are perf wins but not strictly gated on #20229.
Format per row: F# affected → FSharp.Core file:line → .NET API → guard → win.
1 · Numeric reductions → SIMD TensorPrimitives (net-only)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
Array.sum / Array.average |
array.fs:1628/1722 |
TensorPrimitives.Sum (net8) |
#if NET8_0_OR_GREATER |
2–5× · gated: float reassociation; int drops Checked.(+) overflow |
Array.max / Array.min |
array.fs:1685/1648 |
TensorPrimitives.Max/Min (net8) |
#if NET8_0_OR_GREATER |
3–4× · gated: IEEE vs F# NaN — safe for ints |
Seq.sum/average/min/max (array fast-path) |
seq.fs:1465/1485/1517/1557 |
TensorPrimitives behind :? ('T[]) (net8) |
#if NET8_0_OR_GREATER |
2–4× · inherits above; array-backed only |
2 · Primitive-array equality & comparison (ns2.1+)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
GenericEquality{Byte,Int32,Int64,Char}Array |
prim-types.fs:1481/1496/1558/1543 |
MemoryExtensions.SequenceEqual<'T> |
`#if NETSTANDARD2_1_OR_GREATER |
|
GenericComparisonByteArray |
prim-types.fs:1230 |
MemoryExtensions.SequenceCompareTo<byte> |
`#if NETSTANDARD2_1_OR_GREATER |
|
3 · Array search & reorder (ns2.1+)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
Array.contains (single primitive) |
array.fs:~488 |
MemoryExtensions.Contains / ReadOnlySpan.IndexOf |
`#if NETSTANDARD2_1_OR_GREATER |
|
Array.rev |
array.fs:1386 |
System.Array.Reverse |
any TFM |
small |
4 · Primitive-array hashing (net-only)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
GenericHash{Byte,Int32,Int64}Array |
prim-types.fs:964/975/986 |
HashCode.AddBytes (net6) |
#if NET6_0_OR_GREATER |
15–30% · keep in-process hash stability; 10-node cap |
5 · String building (ns2.1+)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
String.map / String.mapi / String.filter |
string.fs:57/71/89 |
String.Create(len, state, writer) |
`#if NETSTANDARD2_1_OR_GREATER |
|
6 · Formatting / printf (mixed)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
%O/%P/interpoland/user types/decimal/DateTime |
printf.fs:911–929 ObjectToString/InterpolandToString (+ span sink 1073–1083) |
ISpanFormattable.TryFormat (net6) + String.Create |
#if NET6_0_OR_GREATER |
skip 1 temp string per arg; polymorphic (no per-type match). No-width case first — padding is string-based today |
numeric specifiers %d %i %u %x %X %f %e %g %M (no width/prec) |
printf.fs:746–770/852–859 |
concrete Int32/Int64/Double/Single/Decimal.TryFormat into Span<char> |
`#if NETSTANDARD2_1_OR_GREATER |
|
StringBuilder / TextWriter sinks |
printf.fs:1073–1083 |
StringBuilder.Append(ReadOnlySpan<char>) / TextWriter.Write(ReadOnlySpan<char>) |
`#if NETSTANDARD2_1_OR_GREATER |
|
sprintf string sink |
printf.fs:1029–1071 |
pooled char[] (ArrayPool) + String.Create |
`#if NETSTANDARD2_1_OR_GREATER |
|
7 · Generic math (net-only)
| F# affected |
FSharp.Core file |
.NET API |
guard |
win |
GenericZero/OneDynamicImplTable (Half) |
prim-types.fs:2667/2700 |
typeof<System.Half> fast case (net5) |
#if NET5_0_OR_GREATER |
avoid reflection |
No boost (documented negatives)
GenericEquality{Single,Double}Array (prim-types.fs:1511/1527) — NaN-reflexivity (er flag: NaN = NaN) diverges from IEEE SequenceEqual; keep scalar.
DefaultInterpolatedStringHandler as the printf sink — it is a ref struct, cannot be a field of the heap PrintfEnv class; and sprintf's ToStringAndClear() is itself just String.Create over a pool. DISH belongs to a compiler lowering.
#20229 ships FSharp.Core with a
net10.0TFM. A#if NET8_0_OR_GREATER/#if NETblock therefore compiles only into the net asset —netstandard2.0/netstandard2.1stay bit-identical. This tracks the additive, guarded fast paths that unlocks.Important gate distinction some span APIs are already in
netstandard2.1, so FSharp.Core's ns2.1 asset could use them too — those are perf wins but not strictly gated on #20229.Format per row: F# affected → FSharp.Core file:line → .NET API → guard → win.
1 · Numeric reductions → SIMD
TensorPrimitives(net-only)Array.sum/Array.averagearray.fs:1628/1722TensorPrimitives.Sum(net8)#if NET8_0_OR_GREATERChecked.(+)overflowArray.max/Array.minarray.fs:1685/1648TensorPrimitives.Max/Min(net8)#if NET8_0_OR_GREATERSeq.sum/average/min/max(array fast-path)seq.fs:1465/1485/1517/1557TensorPrimitivesbehind:? ('T[])(net8)#if NET8_0_OR_GREATER2 · Primitive-array equality & comparison (ns2.1+)
GenericEquality{Byte,Int32,Int64,Char}Arrayprim-types.fs:1481/1496/1558/1543MemoryExtensions.SequenceEqual<'T>GenericComparisonByteArrayprim-types.fs:1230MemoryExtensions.SequenceCompareTo<byte>3 · Array search & reorder (ns2.1+)
Array.contains(single primitive)array.fs:~488MemoryExtensions.Contains/ReadOnlySpan.IndexOfArray.revarray.fs:1386System.Array.Reverse4 · Primitive-array hashing (net-only)
GenericHash{Byte,Int32,Int64}Arrayprim-types.fs:964/975/986HashCode.AddBytes(net6)#if NET6_0_OR_GREATER5 · String building (ns2.1+)
String.map/String.mapi/String.filterstring.fs:57/71/89String.Create(len, state, writer)6 · Formatting / printf (mixed)
%O/%P/interpoland/user types/decimal/DateTimeprintf.fs:911–929ObjectToString/InterpolandToString(+ span sink1073–1083)ISpanFormattable.TryFormat(net6) +String.Create#if NET6_0_OR_GREATER%d %i %u %x %X %f %e %g %M(no width/prec)printf.fs:746–770/852–859Int32/Int64/Double/Single/Decimal.TryFormatintoSpan<char>StringBuilder/TextWritersinksprintf.fs:1073–1083StringBuilder.Append(ReadOnlySpan<char>)/TextWriter.Write(ReadOnlySpan<char>)sprintfstring sinkprintf.fs:1029–1071char[](ArrayPool) +String.Create7 · Generic math (net-only)
GenericZero/OneDynamicImplTable(Half)prim-types.fs:2667/2700typeof<System.Half>fast case (net5)#if NET5_0_OR_GREATERNo boost (documented negatives)
GenericEquality{Single,Double}Array(prim-types.fs:1511/1527) — NaN-reflexivity (erflag:NaN = NaN) diverges from IEEESequenceEqual; keep scalar.DefaultInterpolatedStringHandleras the printf sink — it is aref struct, cannot be a field of the heapPrintfEnvclass; andsprintf'sToStringAndClear()is itself justString.Createover a pool. DISH belongs to a compiler lowering.