|
7 | 7 | using System.Diagnostics; |
8 | 8 | using System.Diagnostics.Contracts; |
9 | 9 | using System.Runtime.CompilerServices; |
| 10 | +#if NETCORE_RUNTIME |
| 11 | +using System.Runtime.InteropServices; |
| 12 | +#endif |
10 | 13 | using Microsoft.Toolkit.HighPerformance.Buffers.Views; |
11 | 14 | using Microsoft.Toolkit.HighPerformance.Extensions; |
12 | 15 |
|
@@ -180,7 +183,22 @@ public Span<T> Span |
180 | 183 | ThrowObjectDisposedException(); |
181 | 184 | } |
182 | 185 |
|
| 186 | +#if NETCORE_RUNTIME |
| 187 | + ref T r0 = ref array!.DangerousGetReferenceAt(this.start); |
| 188 | + |
| 189 | + // On .NET Core runtimes, we can manually create a span from the starting reference to |
| 190 | + // skip the argument validations, which include an explicit null check, covariance check |
| 191 | + // for the array and the actual validation for the starting offset and target length. We |
| 192 | + // only do this on .NET Core as we can leverage the runtime-specific array layout to get |
| 193 | + // a fast access to the initial element, which makes this trick worth it. Otherwise, on |
| 194 | + // runtimes where we would need to at least access a static field to retrieve the base |
| 195 | + // byte offset within an SZ array object, we can get better performance by just using the |
| 196 | + // default Span<T> constructor and paying the cost of the extra conditional branches, |
| 197 | + // especially if T is a value type, in which case the covariance check is JIT removed. |
| 198 | + return MemoryMarshal.CreateSpan(ref r0, this.length); |
| 199 | +#else |
183 | 200 | return new Span<T>(array!, this.start, this.length); |
| 201 | +#endif |
184 | 202 | } |
185 | 203 | } |
186 | 204 |
|
|
0 commit comments