Runtime: JIT fails to inline methods called from a large/complex outer method

Created on 26 Jan 2019  路  22Comments  路  Source: dotnet/runtime

I'm seeing a significant perf regression between 2.1 and 3.0 for this complex hashing function
https://github.com/saucecontrol/Blake2Fast/blob/a140aaba46a8aa3003303c4aef62a472dd8ab4a3/src/Blake2Fast/Blake2bSse4.cs#L186-L503

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
Intel Xeon CPU E3-1505M v6 3.00GHz, 1 CPU, 8 logical and 4 physical cores
Frequency=2929685 Hz, Resolution=341.3336 ns, Timer=TSC
.NET Core SDK=3.0.100-preview-010184
  [Host]        : .NET Core 2.1.7 (CoreCLR 4.6.27129.04, CoreFX 4.6.27129.04), 64bit RyuJIT
  netcoreapp2.1 : .NET Core 2.1.7 (CoreCLR 4.6.27129.04, CoreFX 4.6.27129.04), 64bit RyuJIT
  netcoreapp3.0 : .NET Core 3.0.0-preview-27324-5 (CoreCLR 4.6.27322.0, CoreFX 4.7.19.7311), 64bit RyuJIT

Jit=RyuJit  Toolchain=Default  

| Method | Job | Platform | Mean | Error | StdDev | Allocated |
|------------ |-------------- |--------- |---------:|----------:|----------:|----------:|
| Blake2bFast | netcoreapp2.1 | X64 | 11.79 ms | 0.1075 ms | 0.1005 ms | 0 B |
| Blake2bFast | netcoreapp3.0 | X64 | 52.02 ms | 0.6522 ms | 0.6101 ms | 0 B |
| | | | | | | |
| Blake2bFast | netcoreapp2.1 | X86 | 16.14 ms | 0.1888 ms | 0.1766 ms | 0 B |
| Blake2bFast | netcoreapp3.0 | X86 | 80.83 ms | 2.0971 ms | 6.1834 ms | 0 B |

In looking at the generated assembly, it appears RyuJIT produces good code up until about midway through the third round of mixing, and then switches from inlining the mixing functions (g1 and g2) to calling them instead.

I had to make some changes to the 3.0 version of the code, but those amounted to nothing more than replacing the older StaticCast calls with the new As variants. To confirm the codegen around the API changes wasn't the issue, I commented out rounds 3-12 in the main function, and performance returned to 2.1 levels. So it appears the complexity of the main function is the issue.

I spotted https://github.com/dotnet/coreclr/pull/21893 which mentions a change to the inlining budget allowed by the JIT, but I wasn't able to find where the 'budget' logic was added or whether any part of it was included in 2.1. I can see that in 3.0 Preview 1, this function is about 10x slower than 2.1 and with the nightly builds the delta dropped to 4-5x, so it seems to be related somehow.

Obviously in this case, the performance implications of ignoring the MethodImplOptions.AggressiveInlining hints are quite catastrophic, so I'm wondering if there is a way to truly force inlining or to adjust the budget the JIT allows. The function's complexity can't be reduced, and because of the heavy use of intrinsics, the generated code is a small fraction of what it appears to be in C# or IL.

@AndyAyersMS any ideas?

Most helpful comment

Seems like a case for transforming those vector methods into extension methods.

All 22 comments

With latest bits your example hits the "too many locals" inlining limit. Here "locals" means not only user local variables but also jit-introduced temps.

Jit optimization algorithms may start throttling themselves once we pass this limit.

It may be locals are overcounted in a manner similar to how the budget was overcounted when we partially import methods, ... I'll see if that is the case here, but it seems like a long shot (as jit temps are introduced by importation, so at least temp usage should be fairly accurate).

When I have a chance, I'll compare vs 2.1 to see why things have changed. There could be a number of different reasons for this.

In the short run the only thing you can do is inline fewer methods over all, or less complex methods.

Inlines into 06000024 Blake2bContext:mixSse41(long,long)
  [1 IL=0362 TR=000242 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [2 IL=0129 TR=003790 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [3 IL=0148 TR=003801 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [4 IL=0270 TR=003859 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [5 IL=0296 TR=003874 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [6 IL=0411 TR=000291 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [7 IL=0136 TR=004027 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [8 IL=0162 TR=004042 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [9 IL=0277 TR=004098 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [10 IL=0296 TR=004109 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [11 IL=0434 TR=000322 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [12 IL=0538 TR=000417 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [13 IL=0129 TR=004355 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [14 IL=0148 TR=004366 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [15 IL=0270 TR=004424 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [16 IL=0296 TR=004439 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [17 IL=0587 TR=000466 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [18 IL=0136 TR=004592 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [19 IL=0162 TR=004607 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [20 IL=0277 TR=004663 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [21 IL=0296 TR=004674 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [22 IL=0610 TR=000497 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [23 IL=0659 TR=000544 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [24 IL=0129 TR=004920 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [25 IL=0148 TR=004931 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [26 IL=0270 TR=004989 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [27 IL=0296 TR=005004 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [28 IL=0680 TR=000567 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [29 IL=0709 TR=000603 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [30 IL=0136 TR=005178 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [31 IL=0162 TR=005193 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [32 IL=0277 TR=005249 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [33 IL=0296 TR=005260 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [34 IL=0732 TR=000634 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [35 IL=0741 TR=000647 0600001F] [aggressive inline attribute] Blake2bContext:shuffle_ulong(struct,ubyte):struct
  [36 IL=0781 TR=000687 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [37 IL=0129 TR=005526 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [38 IL=0148 TR=005537 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [39 IL=0270 TR=005595 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [40 IL=0296 TR=005610 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [41 IL=0830 TR=000736 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [42 IL=0136 TR=005763 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [43 IL=0162 TR=005778 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [44 IL=0277 TR=005834 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [45 IL=0296 TR=005845 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [46 IL=0853 TR=000767 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [47 IL=0863 TR=000781 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [48 IL=0903 TR=000824 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [49 IL=0129 TR=006112 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [50 IL=0148 TR=006123 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [51 IL=0270 TR=006181 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [52 IL=0296 TR=006196 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [53 IL=0928 TR=000847 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [54 IL=0957 TR=000883 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [55 IL=0136 TR=006378 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [56 IL=0162 TR=006393 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [57 IL=0277 TR=006449 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [58 IL=0296 TR=006460 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [59 IL=0980 TR=000914 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [60 IL=0994 TR=000928 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [61 IL=1034 TR=000971 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [62 IL=0129 TR=006735 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [63 IL=0148 TR=006746 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [64 IL=0270 TR=006804 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [65 IL=0296 TR=006819 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [66 IL=1055 TR=000994 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [67 IL=1084 TR=001030 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [68 IL=0136 TR=006993 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [69 IL=0162 TR=007008 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [70 IL=0277 TR=007064 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [71 IL=0296 TR=007075 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [72 IL=1107 TR=001061 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [73 IL=1156 TR=001108 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [74 IL=0129 TR=007321 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [75 IL=0148 TR=007332 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [76 IL=0270 TR=007390 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [77 IL=0296 TR=007405 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [78 IL=1205 TR=001157 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [79 IL=0136 TR=007558 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [80 IL=0162 TR=007573 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [81 IL=0277 TR=007629 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [82 IL=0296 TR=007640 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [83 IL=1228 TR=001188 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [84 IL=1242 TR=001202 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [85 IL=1258 TR=001219 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [86 IL=1287 TR=001255 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [87 IL=0129 TR=007944 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [88 IL=0148 TR=007955 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [89 IL=0270 TR=008013 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [90 IL=0296 TR=008028 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [91 IL=1336 TR=001304 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [92 IL=0136 TR=008181 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [93 IL=0162 TR=008196 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [94 IL=0277 TR=008252 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [95 IL=0296 TR=008263 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [96 IL=1359 TR=001335 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [97 IL=1408 TR=001382 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [98 IL=0129 TR=008509 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [99 IL=0148 TR=008520 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [100 IL=0270 TR=008578 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [101 IL=0296 TR=008593 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [102 IL=1422 TR=001398 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [103 IL=1438 TR=001415 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [104 IL=1467 TR=001451 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [105 IL=0136 TR=008804 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [106 IL=0162 TR=008819 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [107 IL=0277 TR=008875 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [108 IL=0296 TR=008886 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [109 IL=1490 TR=001482 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [110 IL=1504 TR=001496 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [111 IL=1520 TR=001513 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [112 IL=1549 TR=001549 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [113 IL=0129 TR=009190 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [114 IL=0148 TR=009201 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [115 IL=0270 TR=009259 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [116 IL=0296 TR=009274 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [117 IL=1559 TR=001565 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [118 IL=1575 TR=001582 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [119 IL=1604 TR=001618 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [120 IL=0136 TR=009477 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [121 IL=0162 TR=009492 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [122 IL=0277 TR=009548 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [123 IL=0296 TR=009559 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [124 IL=1627 TR=001649 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [125 IL=1676 TR=001696 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [126 IL=0129 TR=009805 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [127 IL=0148 TR=009816 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [128 IL=0270 TR=009874 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [129 IL=0296 TR=009889 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [130 IL=1725 TR=001745 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [131 IL=0136 TR=010042 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [132 IL=0162 TR=010057 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [133 IL=0277 TR=010113 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [134 IL=0296 TR=010124 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [135 IL=1748 TR=001776 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [136 IL=1762 TR=001790 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [137 IL=1802 TR=001833 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [138 IL=0129 TR=010399 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [139 IL=0148 TR=010410 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [140 IL=0270 TR=010468 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [141 IL=0296 TR=010483 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [142 IL=1827 TR=001856 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [143 IL=1856 TR=001892 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [144 IL=0136 TR=010665 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [145 IL=0162 TR=010680 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [146 IL=0277 TR=010736 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [147 IL=0296 TR=010747 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [148 IL=1879 TR=001923 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [149 IL=1893 TR=001937 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [150 IL=1933 TR=001980 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [151 IL=0129 TR=011022 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [152 IL=0148 TR=011033 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [153 IL=0270 TR=011091 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [154 IL=0296 TR=011106 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [155 IL=1954 TR=002003 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [156 IL=1983 TR=002039 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [157 IL=0136 TR=011280 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [158 IL=0162 TR=011295 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [159 IL=0277 TR=011351 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [160 IL=0296 TR=011362 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [161 IL=2006 TR=002070 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [162 IL=2026 TR=002090 0600001F] [aggressive inline attribute] Blake2bContext:shuffle_ulong(struct,ubyte):struct
  [163 IL=2055 TR=002123 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [164 IL=0129 TR=011628 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [165 IL=0148 TR=011639 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [166 IL=0270 TR=011697 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [167 IL=0296 TR=011712 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [168 IL=2080 TR=002146 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [169 IL=2109 TR=002182 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [170 IL=0136 TR=011894 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [171 IL=0162 TR=011909 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [172 IL=0277 TR=011965 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [173 IL=0296 TR=011976 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [174 IL=2132 TR=002213 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [175 IL=2157 TR=002234 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [176 IL=2186 TR=002270 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [177 IL=0129 TR=012251 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [178 IL=0148 TR=012262 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [179 IL=0270 TR=012320 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [180 IL=0296 TR=012335 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [181 IL=2196 TR=002286 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [182 IL=2236 TR=002329 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [183 IL=0136 TR=012509 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [184 IL=0162 TR=012524 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [185 IL=0277 TR=012580 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [186 IL=0296 TR=012591 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [187 IL=2259 TR=002360 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [188 IL=2308 TR=002407 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [189 IL=0129 TR=012837 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [190 IL=0148 TR=012848 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [191 IL=0270 TR=012906 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [192 IL=0296 TR=012921 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [193 IL=2357 TR=002456 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [194 IL=0136 TR=013074 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [195 IL=0162 TR=013089 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [196 IL=0277 TR=013145 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [197 IL=0296 TR=013156 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [198 IL=2380 TR=002487 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [199 IL=2401 TR=002508 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [200 IL=2430 TR=002544 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [201 IL=0129 TR=013423 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [202 IL=0148 TR=013434 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [203 IL=0270 TR=013492 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [204 IL=0296 TR=013507 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [205 IL=2451 TR=002567 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [206 IL=2480 TR=002603 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [207 IL=0136 TR=013681 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [208 IL=0162 TR=013696 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [209 IL=0277 TR=013752 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [210 IL=0296 TR=013763 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [211 IL=2503 TR=002634 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [212 IL=2517 TR=002653 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [213 IL=2546 TR=002689 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [214 IL=0129 TR=014030 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [215 IL=0148 TR=014041 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [216 IL=0270 TR=014099 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [217 IL=0296 TR=014114 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [218 IL=2560 TR=002705 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [219 IL=2593 TR=002746 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [220 IL=0136 TR=014296 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [221 IL=0162 TR=014311 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [222 IL=0277 TR=014367 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [223 IL=0296 TR=014378 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [224 IL=2616 TR=002777 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [225 IL=2665 TR=002824 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [226 IL=0129 TR=014624 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [227 IL=0148 TR=014635 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [228 IL=0270 TR=014693 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [229 IL=0296 TR=014708 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [230 IL=2690 TR=002847 0600001D] [aggressive inline attribute] Blake2bContext:blend_ulong(struct,struct,ubyte):struct
  [231 IL=2719 TR=002883 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [232 IL=0136 TR=014890 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [233 IL=0162 TR=014905 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [234 IL=0277 TR=014961 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [235 IL=0296 TR=014972 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [236 IL=2742 TR=002914 06000020] [aggressive inline attribute] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [237 IL=2791 TR=002961 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [238 IL=0129 TR=015218 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [239 IL=0148 TR=015229 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [240 IL=0270 TR=015287 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [241 IL=0296 TR=015302 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [242 IL=2801 TR=002977 0600001E] [aggressive inline attribute] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [243 IL=2841 TR=003020 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [244 IL=0136 TR=015476 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [245 IL=0162 TR=015491 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [246 IL=0277 TR=015547 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [247 IL=0296 TR=015558 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
  [248 IL=2864 TR=003051 06000021] [aggressive inline attribute] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [249 IL=2913 TR=003098 06000022] [aggressive inline attribute] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [250 IL=0129 TR=015804 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [251 IL=0148 TR=015815 0600001B] [aggressive inline attribute] Blake2bContext:ror64_32(struct):struct
    [252 IL=0270 TR=015873 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [253 IL=0296 TR=015888 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
  [254 IL=2962 TR=003147 06000023] [aggressive inline attribute] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
    [255 IL=0136 TR=016041 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [256 IL=0162 TR=016056 0600001C] [aggressive inline attribute] Blake2bContext:ror64_shuffle(struct,struct):struct
    [257 IL=0277 TR=016112 0600001A] [aggressive inline attribute] Blake2bContext:ror64_63(struct):struct
    [0 IL=0296 TR=016123 0600001A] [FAILED: too many locals] Blake2bContext:ror64_63(struct):struct
  [0 IL=2985 TR=003178 06000020] [FAILED: too many locals] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3034 TR=003225 06000022] [FAILED: too many locals] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3083 TR=003274 06000023] [FAILED: too many locals] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3106 TR=003305 06000021] [FAILED: too many locals] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3155 TR=003352 06000022] [FAILED: too many locals] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3176 TR=003375 0600001E] [FAILED: too many locals] Blake2bContext:alignr_ulong(struct,struct,ubyte):struct
  [0 IL=3205 TR=003411 06000023] [FAILED: too many locals] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3228 TR=003442 06000020] [FAILED: too many locals] Blake2bContext:diagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3237 TR=003455 0600001F] [FAILED: too many locals] Blake2bContext:shuffle_ulong(struct,ubyte):struct
  [0 IL=3277 TR=003495 06000022] [FAILED: too many locals] Blake2bContext:g1(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3326 TR=003544 06000023] [FAILED: too many locals] Blake2bContext:g2(byref,byref,byref,byref,byref,byref,byref,byref,byref,byref,byref)
  [0 IL=3349 TR=003575 06000021] [FAILED: too many locals] Blake2bContext:undiagonalize(byref,byref,byref,byref,byref,byref,byref,byref,byref)

In current master the JIT create 510 temps before it stops inlining (the limit is 512)

  • 21 jit temps for root method locals and args and outgoing arg space
  • 184 jit temps for inlinee locals
  • 305 jit temps for inlinee arguments

So I don't see anything there obviously out of line. Inlinee local temps are created only when we actually import a reference to the local, so we shouldn't create any for locals only referred to in un-imported bits of code (I'm pretty sure of this, will verify).

The methods you are inlining take lots of arguments and that is the main pressure on temp allocation.

@AndyAyersMS Thanks for looking into this so quickly!

I had initially planned to eliminate a lot of those smaller one-line 3-argument methods, but due to the changes in the Intrinsics APIs between 2.1 and 3.0, I decided to keep them as what I assumed was a no cost abstraction. The code is generated from a T4 template, so I can get rid of them easily enough.

If you think this is enough of an edge case that it's unlikely to affect anyone else, I'm happy to rearrange my code to make the JIT happy. I'll do some experimenting and see how little it takes to flip back to inlining as expected.

Edit: I wonder if this might end up being more common with algorithms implemented using the new SRI APIs since quite a lot of the API is made up of small managed helpers that are meant to be inlined like mine here.

I tried manually inlining the nested ROR helper methods used in g1 and g2, and that change alone made up most of the difference.

| Method | Job | Platform | Mean | Error | StdDev | Allocated |
|------------ |-------------- |--------- |---------:|----------:|----------:|----------:|
| Blake2bFast | netcoreapp2.1 | X64 | 11.76 ms | 0.0422 ms | 0.0374 ms | 0 B |
| Blake2bFast | netcoreapp3.0 | X64 | 15.01 ms | 0.0913 ms | 0.0854 ms | 0 B |
| | | | | | | |
| Blake2bFast | netcoreapp2.1 | X86 | 15.99 ms | 0.1335 ms | 0.1249 ms | 0 B |
| Blake2bFast | netcoreapp3.0 | X86 | 16.50 ms | 0.2039 ms | 0.1907 ms | 0 B |

Unfortunately, eliminating the rest of the one-liner helpers didn't improve beyond that. I'll keep looking to see if I can find where the remainder of the x64 regression comes from.

I have similar concerns about people hitting various internal jit limits with heavy intrinsics usage.

There's no easy way for the jit to provide feedback to the user when this happens. And because these internal limits are measured against various jit artifacts, even if we could provide feedback, it would be very general ("method is too complex to optimize") and might not be actionable.

The limits are there to prevent jit algorithms from taking up too much memory, too much time, or both. Perhaps we could tie increasing the limits into AggressiveOptimization so we have a better idea that the performance of a method is deemed critical and so optimizing it is worth the extra jit time and memory.

On further investigation, it looks like the failure to inline was merely a symptom and not the real problem here. Something is different in the way those temp variables are created/counted for inlined methods. It seems the variable counts have gone way up in 3.0, and that's causing the inliner to hit its threshold where it didn't in 2.1.

The extra variable counts seem to have a negative impact on the register allocator as well. Once I got past the inliner issue, the 3.0 performance still lagged 2.1, and in looking at the assembly it appears the difference is mostly due to additional stack shuffling.

I modified my code in a few stages, manually inlining more and more of it until I had one big method, and I grabbed the JitDisasm output and perf numbers for 2.1 and 3.0 at those various stages. Here's what I found:

| Stage | Variable Count 2.1 | Variable Count 3.0 | Scaled Time 2.1 | Scaled Time 3.0 | JitDisasm |
|-----------|---------:|----------:|----------:|----------:|-----|
| Original | 415 | 516 | 1.0 | 4.4 |2.1 3.0|
| Removed Nested Helpers (ROR) | 127 | 218 | 1.0 | 1.3 |2.1 3.0|
| Removed All Helpers | 70 | 144 | 1.0 | 1.3 |2.1 3.0|
| Full Manual Inline | 28 | 31 | 1.0 | 0.9 |2.1 3.0|

So basically, 2.1 had consistent codegen and perf numbers regardless of how I structured my code, but 3.0 behaves quite differently. The good news is that once I had manually inlined everything, I can see the other codegen improvements in 3.0 and a resulting perf increase.

the difference is mostly due to additional stack shuffling.

Do you have some ASM example? This PR may help:

https://github.com/dotnet/coreclr/pull/19429

I linked the asm in the far right column of the table above. It's not the kind of ping-pong shuffling you describe in the PR comment (I've seen a few of those before too), but there are more memory reads in the 3.0 asm.

I think the shuffling here is more of a symptom as well, with the real issue being the change in the way inlined variables/args are imported.

Interesting. Do all the 2.1 variants run at the same speed? Or did you scale each row separately?

It's possible that some change in the jit is leading to more temps, but we haven't made many changes in that part of the jit recently -- if anything 3.0 should require fewer overall temps (see eg dotnet/coreclr#20640) for the same input IL.

So my guess is that there is something in the input IL that is driving the jit to create more temps. I can drill sometime in the next few days.

It's also a little surprising that once we get the number of 3.0 temps down below the magic 512 level that we don't recover 2.1 level perf. Maybe that same IL change is leading to conflicting live ranges for those extra temps, or ...? If you can point us at repros for those cases perhaps @CarolEidt can look at what's going on in the allocator as it looks like everything should really end up in registers.

Interesting. Do all the 2.1 variants run at the same speed? Or did you scale each row separately?

I scaled everything using the original code running on 2.1 as a baseline, so yeah, 2.1 was the same perf no matter how the code was structured on my side. I arrived at that original code through experimentation, making sure that any changes I made to improve readability/maintainability didn't hurt performance, so that makes sense.

If you can point us at repros for those cases...

What's the best way for me to get those to you? I was thinking I'd clean up my benchmark app to remove the 3rd party library tests from it, and then I can make a separate branch for each stage of my manual inlining effort. Is there another option that makes it easier for you?

So my guess is that there is something in the input IL that is driving the jit to create more temps. I can drill sometime in the next few days.

IL for the inlinees is different enough to be interesting. It's worth noting that the argument list for all inlinees is the same in both versions of the code, but the locals are set up differently since the 2.1 API used static methods for StaticCast while the 3.0 API uses instance methods for As..., resulting in the need for an extra local temp in some cases.

Here's a representative sample:

2.1 Version

.method private hidebysig static valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<uint64> 
        blend_ulong(valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<uint64> x,
                    valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<uint64> y,
                    uint8 m) cil managed aggressiveinlining
{
  // Code size       24 (0x18)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<!!1> [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.X86.Sse::StaticCast<uint64,uint16>(valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<!!0>)
  IL_0006:  ldarg.1
  IL_0007:  call       valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<!!1> [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.X86.Sse::StaticCast<uint64,uint16>(valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<!!0>)
  IL_000c:  ldarg.2
  IL_000d:  call       valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<uint16> [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.X86.Sse41::Blend(valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<uint16>,
                                                                                                                                                                                                          valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<uint16>,
                                                                                                                                                                                                          uint8)
  IL_0012:  call       valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<!!1> [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.X86.Sse::StaticCast<uint16,uint64>(valuetype [System.Runtime.Intrinsics.Experimental]System.Runtime.Intrinsics.Vector128`1<!!0>)
  IL_0017:  ret
} // end of method Blake2bContext::blend_ulong

3.0 Version

.method private hidebysig static valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint64> 
        blend_ulong(valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint64> x,
                    valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint64> y,
                    uint8 m) cil managed aggressiveinlining
{
  // Code size       29 (0x1d)
  .maxstack  3
  .locals init ([0] valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16> V_0)
  IL_0000:  ldarga.s   x
  IL_0002:  call       instance valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16> valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint64>::AsUInt16()
  IL_0007:  ldarga.s   y
  IL_0009:  call       instance valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16> valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint64>::AsUInt16()
  IL_000e:  ldarg.2
  IL_000f:  call       valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16> [System.Runtime.Intrinsics]System.Runtime.Intrinsics.X86.Sse41::Blend(valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16>,
                                                                                                                                                                                valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16>,
                                                                                                                                                                                uint8)
  IL_0014:  stloc.0
  IL_0015:  ldloca.s   V_0
  IL_0017:  call       instance valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint64> valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector128`1<uint16>::AsUInt64()
  IL_001c:  ret
} // end of method Blake2bContext::blend_ulong

Just as a note: As, in particular, is actually zero-cost when it's intrinsic implementation is used since we actually fold it away entirely in the importer. Maybe there is something else we could do to assist here?

For repros, just point us at some commit in your repo.

It's not the expansion of As that causes the temp inflation -- it's the fact that in the IL the caller of As must pass the address of the value type this, and so the expression feeding the call must be something whose address can be taken, and so requires the expression be an arg or local. Which means CSC will create locals as needed.

The "equivalent" static version doesn't need these locals.

Is there an equivalent static I can use here? I was looking through the API and can't seem to find it if so.

No. We removed the static methods.

@AndyAyersMS, is this something we could help the JIT to understand/special-case? That is, that the As intrinsic method doesn't actually need to be address taken here?

I've counted up the number times AsUInt64() is called on the return value of another Intrinsic, and I'm getting 95, which comes awfully close to the max difference in variable counts between runtime versions. It's looking increasingly likely that's the casue of this issue. It seems I may have just gotten lucky that the nested AggressiveInlining methods in my original code didn't hurt me in the 2.1 version.

Since there's no way to get truly equivalent code with the Intrinsics API changes, I'll dummy something up that keeps the number of calls and arguments the same by using only methods that haven't changed, and then re-run the numbers.

It would be tricky for the jit to realize this is a special case. The thing that triggers creation of the temp is the stloc two instructions before the call to As. So we'd need some kind of lookahead. There might be ways leverage the IL prescan the jit does for inlinees... maybe.

Seems like a case for transforming those vector methods into extension methods.

When the JIT exceeds limits during inlining, maybe a simplification pass could be run. Then the inliner could restart if the simplifications have helped to meet the limits.

I've logged https://github.com/dotnet/corefx/issues/34902 to track an API suggestion to change the methods to be extension methods.

@tannergooding thanks for logging that issue. I wasn't sure if it was too late in the 3.0 cycle to make that kind of change, but I agree it would be best to replace all those instance methods.

I can confirm that swapping out the calls that include the As methods for other stand-in statics eliminates the perf regression I was seeing.

I'll go ahead and close this issue since it's a bit of a red herring and the other two issues created address my concerns.

@AndyAyersMS thanks again for your help tracking this down.

@saucecontrol I suggest reopening this issue until we fix it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

EgorBo picture EgorBo  路  3Comments

matty-hall picture matty-hall  路  3Comments

v0l picture v0l  路  3Comments

jzabroski picture jzabroski  路  3Comments

nalywa picture nalywa  路  3Comments