Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Conversation

@benaadams
Copy link
Member

@benaadams benaadams commented Dec 9, 2017

Unify OnDeserialization initialization with .ctor Initialize

Split Resize -> Expand and Rehash

Rehashing is only used in string key variant so it doesn't need to be compiled in every Expand
Rehashing doesn't expand the Dictionary so it can reuse the existing arrays
Expand doesn't need the extra param and loop that it always skips
Loop CQ improvements

Shrinks the generic Resize (as Expand) from 497 bytes to 265 bytes

PTAL @jkotas

@benaadams
Copy link
Member Author

Before Resize

; Lcl frame size = 88
...
G_M29783_IG10:
       413BD8               cmp      ebx, r8d
       0F8393000000         jae      G_M29783_IG18
       4863D3               movsxd   rdx, ebx
       488D1452             lea      rdx, [rdx+2*rdx]
       498D4CD510           lea      rcx, bword ptr [r13+8*rdx+16]
       448B4910             mov      r9d, dword ptr [rcx+16]
       4585C9               test     r9d, r9d
       7C1C                 jl       SHORT G_M29783_IG11
       418BC1               mov      eax, r9d
       99                   cdq      
       F7FF                 idiv     edx:eax, edi
       413BD6               cmp      edx, r14d
       7373                 jae      SHORT G_M29783_IG18
       4863C2               movsxd   rax, edx
       8B448510             mov      eax, dword ptr [rbp+4*rax+16]
       894114               mov      dword ptr [rcx+20], eax
       4863C2               movsxd   rax, edx
       895C8510             mov      dword ptr [rbp+4*rax+16], ebx

G_M29783_IG11:
       FFC3                 inc      ebx
       8B44244C             mov      eax, dword ptr [rsp+4CH]
       3BD8                 cmp      ebx, eax
       8944244C             mov      dword ptr [rsp+4CH], eax
       7CB8                 jl       SHORT G_M29783_IG10
...
; Total bytes of code 497, prolog size 29 for method Dictionary`2:Resize(int,bool):this

After Expand

; Lcl frame size = 64
...
G_M13443_IG06:
       443BE1               cmp      r12d, ecx
       735E                 jae      SHORT G_M13443_IG10
       4963C4               movsxd   rax, r12d
       488D0440             lea      rax, [rax+2*rax]
       4D8D44C610           lea      r8, bword ptr [r14+8*rax+16]
       418B4010             mov      eax, dword ptr [r8+16]
       85C0                 test     eax, eax
       7C1A                 jl       SHORT G_M13443_IG07
       99                   cdq      
       F7FF                 idiv     edx:eax, edi
       3BD5                 cmp      edx, ebp
       7343                 jae      SHORT G_M13443_IG10
       4863C2               movsxd   rax, edx
       8B448310             mov      eax, dword ptr [rbx+4*rax+16]
       41894014             mov      dword ptr [r8+20], eax
       4863C2               movsxd   rax, edx
       4489648310           mov      dword ptr [rbx+4*rax+16], r12d

G_M13443_IG07:
       41FFC4               inc      r12d
       453BE7               cmp      r12d, r15d
       7CC5                 jl       SHORT G_M13443_IG06
...
; Total bytes of code 265, prolog size 24 for method Dictionary`2:Expand(int):this

@benaadams
Copy link
Member Author

@dotnet-bot test Windows_NT x64 Checked corefx_baseline
@dotnet-bot test Ubuntu x64 Checked corefx_baseline

@benaadams
Copy link
Member Author

benaadams commented Dec 9, 2017

@mikedn is this better?

int bucket = (int)((uint)hashCode % (uint)prime);

It produces the following asm

G_M13443_IG06:
       443BE1               cmp      r12d, ecx
       735F                 jae      SHORT G_M13443_IG10
       4963C4               movsxd   rax, r12d
       488D0440             lea      rax, [rax+2*rax]
       4D8D44C610           lea      r8, bword ptr [r14+8*rax+16]
       418B4010             mov      eax, dword ptr [r8+16]
       85C0                 test     eax, eax
       7C1B                 jl       SHORT G_M13443_IG07
       33D2                 xor      rdx, rdx
       F7F7                 div      edx:eax, edi
       3BD5                 cmp      edx, ebp
       7343                 jae      SHORT G_M13443_IG10
       4863C2               movsxd   rax, edx             ; can these be avoided?
       8B448310             mov      eax, dword ptr [rbx+4*rax+16]
       41894014             mov      dword ptr [r8+20], eax
       4863C2               movsxd   rax, edx             ; can these be avoided?
       4489648310           mov      dword ptr [rbx+4*rax+16], r12d

G_M13443_IG07:
       41FFC4               inc      r12d
       453BE7               cmp      r12d, r15d
       7CC4                 jl       SHORT G_M13443_IG06

@benaadams
Copy link
Member Author

benaadams commented Dec 9, 2017

Error is api change

Miscellaneous build error - see console log for others

D:\j\workspace\x64_checked_w---d7295605\_\fx\Tools\ApiCompat.targets(54,5): error : 
MembersMustExist : Member 'System.Buffers.OwnedMemory.Pin()' 
does not exist in the implementation but it does exist in the contract. 
[D:\j\workspace\x64_checked_w---d7295605\_\fx\src\System.Runtime\src\System.Runtime.csproj]

/cc @ahsonkhan

@benaadams
Copy link
Member Author

for (int i = 0; i < count; i++)
{
    ref Entry entry = ref entries[i];
    int hashCode = entry.hashCode;
    if (hashCode >= 0)
    {
        ref int bucket = ref buckets[((uint)hashCode % (uint)prime)];
        entry.next = bucket;
        bucket = i;
    }
}

Changes it to

G_M13443_IG06:
       443BE1               cmp      r12d, ecx
       735D                 jae      SHORT G_M13443_IG10
       4963C4               movsxd   rax, r12d
       488D0440             lea      rax, [rax+2*rax]
       4D8D44C610           lea      r8, bword ptr [r14+8*rax+16]
       418B4010             mov      eax, dword ptr [r8+16]
       85C0                 test     eax, eax
       7C19                 jl       SHORT G_M13443_IG07
       33D2                 xor      rdx, rdx
       F7F6                 div      edx:eax, esi
       3BD5                 cmp      edx, ebp
       7341                 jae      SHORT G_M13443_IG10
       4863C2               movsxd   rax, edx
       488D448310           lea      rax, bword ptr [rbx+4*rax+16]
       8B10                 mov      edx, dword ptr [rax]
       41895014             mov      dword ptr [r8+20], edx
       448920               mov      dword ptr [rax], r12d

G_M13443_IG07:
       41FFC4               inc      r12d
       453BE7               cmp      r12d, r15d
       7CC6                 jl       SHORT G_M13443_IG06

But again don't know if that's any better

@benaadams
Copy link
Member Author

Can skip the range checks with Unsafe Add; but I assume that wouldn't be palatable?

@jkotas
Copy link
Member

jkotas commented Dec 9, 2017

What is the total size of Dictionary instantiation before/after this change? Or what is the System.Private.CoreLib.dll native image size before/after this change?

@mikedn
Copy link

mikedn commented Dec 10, 2017

int bucket = (int)((uint)hashCode % (uint)prime);

Better how? Unsigned division is just as costly as signed division.

But again don't know if that's any better

Probably it's a wash. Or worse. That lea that appears in your last version takes 3 cycles. And all subsequent mov instructions wait on it. You saved a movsxd but that's only a cycle and if the stars are aligned then the 2 movsxd from the previous version may execute in parallel. You could measure to be sure but the results may vary from CPU to CPU.

@mikedn
Copy link

mikedn commented Dec 10, 2017

Rehashing is only used in string key variant so it doesn't need to be compiled in every Expand

@jkotas This seems like it should work when jitting but does it work when crossgening? I guess not, Rehash will probably be compiled for every instantiation in corelib.

@jkotas
Copy link
Member

jkotas commented Dec 10, 2017

@jkotas This seems like it should work when jitting but does it work when crossgening?

Agree. The trade-off is different for different configs. Also, the implementation is shared with .NET Native / CoreRT and Xamarin / Mono that have full AOT targets.

For changes like this, it is important to understand what we are gaining and losing.

@jkotas
Copy link
Member

jkotas commented Dec 10, 2017

Thinking about this some more: I think it is better tradeoff, considering the different configs, to keep Resize and Expand as a single method. It helps AOT configs much more than it hurts the JIT configs.

@benaadams
Copy link
Member Author

benaadams commented Dec 10, 2017

It helps AOT configs much more than it hurts the JIT configs.

This due to all instance methods getting baked into type on AOT compile; regardless of usage? Would it be better then to externalize Rehash then only for string keys?

@benaadams
Copy link
Member Author

benaadams commented Dec 10, 2017

Externalizing Dictionary Rehash

Total bytes of diff: -7733 (-0.03% of base)
    diff is an improvement.

Total byte diff includes -2590 bytes from reconciling methods
        Base had    2 unique methods,    11957 unique bytes
        Diff had    3 unique methods,     9367 unique bytes

Top file improvements by size (bytes):
       -7733 : System.Private.CoreLib.dasm (-0.23% of base)

1 total files with size differences (1 improved, 0 regressed), 129 unchanged.

Top method regessions by size (bytes):
        7304 : System.Private.CoreLib.dasm - Dictionary`2:Expand(int):this (0/29 methods)
        2057 : System.Private.CoreLib.dasm - DictionaryHelper:Rehash(ref) (0/11 methods)
          70 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,ref,ubyte):bool:this
          16 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,struct,ubyte):bool:this (4 methods)
           6 : System.Private.CoreLib.dasm - DictionaryHelper:.ctor():this (0/1 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.Contains(struct):bool:this (29 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.Remove(struct):bool:this (29 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:GetObjectData(ref,struct):this (29 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:ContainsValue(struct):bool:this (6 methods)

Top method improvements by size (bytes):
      -11918 : System.Private.CoreLib.dasm - Dictionary`2:Resize(int,bool):this (29/0 methods)
       -3142 : System.Private.CoreLib.dasm - Dictionary`2:OnDeserialization(ref):this (29 methods)
        -332 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ref,ubyte):bool:this (3 methods)
        -261 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ref,ubyte):bool:this (3 methods)
        -225 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,struct,ubyte):bool:this (2 methods)
        -203 : System.Private.CoreLib.dasm - Dictionary`2:Initialize(int):this (29 methods)
        -174 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,ref,ubyte):bool:this (2 methods)
        -102 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,int,ubyte):bool:this (3 methods)
         -94 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,int,ubyte):bool:this
         -94 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ubyte,ubyte):bool:this
         -88 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(char,ref,ubyte):bool:this
         -88 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(short,long,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,short,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ubyte,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,int,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,bool,ubyte):bool:this
         -39 : System.Private.CoreLib.dasm - Dictionary`2:Resize():this (1/0 methods)
         -34 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,bool,ubyte):bool:this
         -34 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,long,ubyte):bool:this
         -34 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,char,ubyte):bool:this

29 total methods with size differences (20 improved, 9 regressed), 130066 unchanged.

@benaadams
Copy link
Member Author

static class DictionaryHelper

Total bytes of diff: -7739 (-0.23% of base)
    diff is an improvement.

Total byte diff includes -2596 bytes from reconciling methods
        Base had    2 unique methods,    11957 unique bytes
        Diff had    2 unique methods,     9361 unique bytes

Top file improvements by size (bytes):
       -7739 : System.Private.CoreLib.dasm (-0.23% of base)

1 total files with size differences (1 improved, 0 regressed), 0 unchanged.

Top method regessions by size (bytes):
        7304 : System.Private.CoreLib.dasm - Dictionary`2:Expand(int):this (0/29 methods)
        2057 : System.Private.CoreLib.dasm - DictionaryHelper:Rehash(ref) (0/11 methods)
          70 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,ref,ubyte):bool:this
          16 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,struct,ubyte):bool:this (4 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.Contains(struct):bool:this (29 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.Remove(struct):bool:this (29 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:GetObjectData(ref,struct):this (29 methods)
           3 : System.Private.CoreLib.dasm - Dictionary`2:ContainsValue(struct):bool:this (6 methods)

Top method improvements by size (bytes):
      -11918 : System.Private.CoreLib.dasm - Dictionary`2:Resize(int,bool):this (29/0 methods)
       -3142 : System.Private.CoreLib.dasm - Dictionary`2:OnDeserialization(ref):this (29 methods)
        -332 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ref,ubyte):bool:this (3 methods)
        -261 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ref,ubyte):bool:this (3 methods)
        -225 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,struct,ubyte):bool:this (2 methods)
        -203 : System.Private.CoreLib.dasm - Dictionary`2:Initialize(int):this (29 methods)
        -174 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,ref,ubyte):bool:this (2 methods)
        -102 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,int,ubyte):bool:this (3 methods)
         -94 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,int,ubyte):bool:this
         -94 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ubyte,ubyte):bool:this
         -88 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(char,ref,ubyte):bool:this
         -88 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(short,long,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,short,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ubyte,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,int,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,bool,ubyte):bool:this
         -39 : System.Private.CoreLib.dasm - Dictionary`2:Resize():this (1/0 methods)
         -34 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,bool,ubyte):bool:this
         -34 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,long,ubyte):bool:this
         -34 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,char,ubyte):bool:this

28 total methods with size differences (20 improved, 8 regressed), 16715 unchanged.

@benaadams
Copy link
Member Author

@dotnet-bot test Windows_NT x64 Checked corefx_baseline
@dotnet-bot test Ubuntu x64 Checked corefx_baseline

@jkotas
Copy link
Member

jkotas commented Dec 10, 2017

The spliting the methods into two achieves:

  • Avoids the for-loop that recomputes the hashcodes
  • Avoids extra bool argument. The benefit of this is very small.

Can we achieve most of the benefit by just skipping the for-loop that recomputes the hashcodes for valuetypes in the unified method?

@benaadams
Copy link
Member Author

benaadams commented Dec 10, 2017

It either recomputes the hashcodes or expands; but never both; so its a function with two exclusive modes. However, currently it always reallocates the arrays no matter which its doing and loops twice for rehashing.

The efficiency of rehashing probably isn't greatly important as rehashing will only ever happen once per dictionary; however resizing will happen lots.

So its avoiding the for-loop that recomputes the hashcodes; which is a code path that is only ever run 0 - 1 times for a string key Dictionary; and run 0 times for any other key type for N resizes.

From a code size point of view, skipping it for value types gets most of the way there:

https://github.com/dotnet/coreclr/compare/master...benaadams:rehash-skip-structs?expand=1

Total bytes of diff: -6590 (-0.19% of base)
    diff is an improvement.

Total byte diff includes 0 bytes from reconciling methods
        Base had    0 unique methods,        0 unique bytes
        Diff had    0 unique methods,        0 unique bytes

Top file improvements by size (bytes):
       -6590 : System.Private.CoreLib.dasm (-0.19% of base)

1 total files with size differences (1 improved, 0 regressed), 0 unchanged.

Top method improvements by size (bytes):
       -3142 : System.Private.CoreLib.dasm - Dictionary`2:OnDeserialization(ref):this (29 methods)
       -1549 : System.Private.CoreLib.dasm - Dictionary`2:Resize(int,bool):this (29 methods)
        -329 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ref,ubyte):bool:this (3 methods)
        -258 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ref,ubyte):bool:this (3 methods)
        -221 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,struct,ubyte):bool:this (2 methods)
        -203 : System.Private.CoreLib.dasm - Dictionary`2:Initialize(int):this (29 methods)
        -172 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,ref,ubyte):bool:this (2 methods)
         -96 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ubyte,ubyte):bool:this
         -96 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,int,ubyte):bool:this
         -93 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,int,ubyte):bool:this
         -93 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ubyte,ubyte):bool:this
         -86 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(char,ref,ubyte):bool:this
         -86 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(short,long,ubyte):bool:this
         -83 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,short,ubyte):bool:this
         -83 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,bool,ubyte):bool:this

15 total methods with size differences (15 improved, 0 regressed), 16730 unchanged.

Would you prefer that as a PR instead?

@benaadams
Copy link
Member Author

Or... could split it to two methods and if out the body for structs?

@benaadams
Copy link
Member Author

Changed to if out resize body for structs

Total bytes of diff: -6962 (-0.20% of base)
    diff is an improvement.

Total byte diff includes -1757 bytes from reconciling methods
        Base had    2 unique methods,    11957 unique bytes
        Diff had    2 unique methods,    10200 unique bytes

Top file improvements by size (bytes):
       -6962 : System.Private.CoreLib.dasm (-0.20% of base)

1 total files with size differences (1 improved, 0 regressed), 0 unchanged.

Top method regessions by size (bytes):
        7304 : System.Private.CoreLib.dasm - Dictionary`2:Expand(int):this (0/29 methods)
        2896 : System.Private.CoreLib.dasm - Dictionary`2:Rehash():this (0/11 methods)

Top method improvements by size (bytes):
      -11918 : System.Private.CoreLib.dasm - Dictionary`2:Resize(int,bool):this (29/0 methods)
       -3142 : System.Private.CoreLib.dasm - Dictionary`2:OnDeserialization(ref):this (29 methods)
        -332 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ref,ubyte):bool:this (3 methods)
        -261 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ref,ubyte):bool:this (3 methods)
        -225 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,struct,ubyte):bool:this (2 methods)
        -203 : System.Private.CoreLib.dasm - Dictionary`2:Initialize(int):this (29 methods)
        -174 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,ref,ubyte):bool:this (2 methods)
         -94 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,int,ubyte):bool:this
         -94 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(struct,ubyte,ubyte):bool:this
         -88 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(char,ref,ubyte):bool:this
         -88 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(short,long,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,short,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,ubyte,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(int,int,ubyte):bool:this
         -84 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(long,bool,ubyte):bool:this
         -63 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,struct,ubyte):bool:this (4 methods)
         -45 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,int,ubyte):bool:this (3 methods)
         -39 : System.Private.CoreLib.dasm - Dictionary`2:Resize():this (1/0 methods)
         -15 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,ref,ubyte):bool:this
         -15 : System.Private.CoreLib.dasm - Dictionary`2:TryInsert(ref,bool,ubyte):bool:this

24 total methods with size differences (22 improved, 2 regressed), 16719 unchanged.

@jkotas
Copy link
Member

jkotas commented Dec 11, 2017

efficiency of rehashing probably isn't greatly important as rehashing will only ever happen once per dictionary

Right - the efficiency of rehashing is not that important. It will only happen once, and only when the system is under likely DoS attack - close to never in practice. The most important thing is that it works reliably and does make things worse by exposing new bugs. It may be one of the reasons why why the implementation was done as a small modification of Resize. I would prefer to keep it that way. Minimizing rehasing-specific code reduces chances that there is rehasing-specific bug.

@benaadams
Copy link
Member Author

Makes sense

@benaadams benaadams closed this Dec 11, 2017
@jkotas
Copy link
Member

jkotas commented Dec 11, 2017

This PR had a couple of other good changes (caching fields in locals) that are pure improvements. It would be great if you can submit those - I will be happy to take them.

@benaadams
Copy link
Member Author

This PR had a couple of other good changes (caching fields in locals) that are pure improvements. It would be great if you can submit those - I will be happy to take them.

Added #15462 and #15461

@benaadams benaadams deleted the Rehash+expand branch December 11, 2017 06:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants