Skip to content

Commit bea0abb

Browse files
authored
Speed up interface checking and casting (#49257)
* Reduce branches in IsInstanceOfInterface/ChkCastInterface * Drop extra var, lea; additional check for small counts * Feedback * Undo IsInstanceOfClass change * Tidy usings
1 parent 49eed0e commit bea0abb

File tree

1 file changed

+84
-37
lines changed
  • src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices

1 file changed

+84
-37
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs

Lines changed: 84 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using Internal.Runtime.CompilerServices;
65
using System.Runtime.InteropServices;
76
using System.Threading;
8-
using System.Runtime.Intrinsics;
9-
using System.Runtime.Intrinsics.X86;
7+
8+
using Internal.Runtime.CompilerServices;
109

1110
namespace System.Runtime.CompilerServices
1211
{
@@ -207,34 +206,57 @@ private static CastResult TryGet(nuint source, nuint target)
207206
[DebuggerStepThrough]
208207
private static object? IsInstanceOfInterface(void* toTypeHnd, object? obj)
209208
{
209+
const int unrollSize = 4;
210+
210211
if (obj != null)
211212
{
212213
MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
213-
nuint interfaceCount = mt->InterfaceCount;
214+
nint interfaceCount = mt->InterfaceCount;
214215
if (interfaceCount != 0)
215216
{
216217
MethodTable** interfaceMap = mt->InterfaceMap;
217-
for (nuint i = 0; ; i += 4)
218+
if (interfaceCount < unrollSize)
218219
{
219-
if (interfaceMap[i + 0] == toTypeHnd)
220-
goto done;
221-
if (--interfaceCount == 0)
222-
break;
223-
if (interfaceMap[i + 1] == toTypeHnd)
224-
goto done;
225-
if (--interfaceCount == 0)
226-
break;
227-
if (interfaceMap[i + 2] == toTypeHnd)
228-
goto done;
229-
if (--interfaceCount == 0)
230-
break;
231-
if (interfaceMap[i + 3] == toTypeHnd)
220+
// If not enough for unrolled, jmp straight to small loop
221+
// as we already know there is one or more interfaces so don't need to check again.
222+
goto few;
223+
}
224+
225+
do
226+
{
227+
if (interfaceMap[0] == toTypeHnd ||
228+
interfaceMap[1] == toTypeHnd ||
229+
interfaceMap[2] == toTypeHnd ||
230+
interfaceMap[3] == toTypeHnd)
231+
{
232232
goto done;
233-
if (--interfaceCount == 0)
234-
break;
233+
}
234+
235+
interfaceMap += unrollSize;
236+
interfaceCount -= unrollSize;
237+
} while (interfaceCount >= unrollSize);
238+
239+
if (interfaceCount == 0)
240+
{
241+
// If none remaining, skip the short loop
242+
goto extra;
235243
}
244+
245+
few:
246+
do
247+
{
248+
if (interfaceMap[0] == toTypeHnd)
249+
{
250+
goto done;
251+
}
252+
253+
// Assign next offset
254+
interfaceMap++;
255+
interfaceCount--;
256+
} while (interfaceCount > 0);
236257
}
237258

259+
extra:
238260
if (mt->NonTrivialInterfaceCast)
239261
{
240262
goto slowPath;
@@ -374,35 +396,60 @@ private static CastResult TryGet(nuint source, nuint target)
374396
[DebuggerStepThrough]
375397
private static object? ChkCastInterface(void* toTypeHnd, object? obj)
376398
{
399+
const int unrollSize = 4;
400+
377401
if (obj != null)
378402
{
379403
MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
380-
nuint interfaceCount = mt->InterfaceCount;
404+
nint interfaceCount = mt->InterfaceCount;
381405
if (interfaceCount == 0)
382406
{
383407
goto slowPath;
384408
}
385409

386410
MethodTable** interfaceMap = mt->InterfaceMap;
387-
for (nuint i = 0; ; i += 4)
411+
if (interfaceCount < unrollSize)
388412
{
389-
if (interfaceMap[i + 0] == toTypeHnd)
390-
goto done;
391-
if (--interfaceCount == 0)
392-
goto slowPath;
393-
if (interfaceMap[i + 1] == toTypeHnd)
394-
goto done;
395-
if (--interfaceCount == 0)
396-
goto slowPath;
397-
if (interfaceMap[i + 2] == toTypeHnd)
398-
goto done;
399-
if (--interfaceCount == 0)
400-
goto slowPath;
401-
if (interfaceMap[i + 3] == toTypeHnd)
413+
// If not enough for unrolled, jmp straight to small loop
414+
// as we already know there is one or more interfaces so don't need to check again.
415+
goto few;
416+
}
417+
418+
do
419+
{
420+
if (interfaceMap[0] == toTypeHnd ||
421+
interfaceMap[1] == toTypeHnd ||
422+
interfaceMap[2] == toTypeHnd ||
423+
interfaceMap[3] == toTypeHnd)
424+
{
402425
goto done;
403-
if (--interfaceCount == 0)
404-
goto slowPath;
426+
}
427+
428+
// Assign next offset
429+
interfaceMap += unrollSize;
430+
interfaceCount -= unrollSize;
431+
} while (interfaceCount >= unrollSize);
432+
433+
if (interfaceCount == 0)
434+
{
435+
// If none remaining, skip the short loop
436+
goto slowPath;
405437
}
438+
439+
few:
440+
do
441+
{
442+
if (interfaceMap[0] == toTypeHnd)
443+
{
444+
goto done;
445+
}
446+
447+
// Assign next offset
448+
interfaceMap++;
449+
interfaceCount--;
450+
} while (interfaceCount > 0);
451+
452+
goto slowPath;
406453
}
407454

408455
done:

0 commit comments

Comments
 (0)