-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-generic StringComparer conversion for dict and hashset #49608
Conversation
Tagging subscribers to this area: @eiriktsarpalis Issue DetailsMove the
|
G_M17833_IG01:
push rdi
push rsi
- push rbp
- push rbx
sub rsp, 40
mov qword ptr [rsp+20H], rcx
mov rsi, rcx
mov rdi, r8 ... G_M17833_IG07:
- mov rbx, qword ptr [rsi]
- mov rcx, rbx
+ mov rcx, qword ptr [rsi]
call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
cmp rax, qword ptr [(reloc)]
- jne G_M17833_IG13
+ jne SHORT G_M17833_IG09
G_M17833_IG08:
- mov rdi, gword ptr [rsi+24]
- test rdi, rdi
- jne SHORT G_M17833_IG10
- mov rcx, rbx
- call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
- mov rdi, rax
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- mov rdx, gword ptr [rax+1838H]
- mov rcx, rdi
- call [CORINFO_HELP_CHKCASTANY]
+ mov rcx, gword ptr [rsi+24]
+ call [NonRandomizedStringEqualityComparer:GetStringComparer(IEqualityComparer`1):IEqualityComparer`1]
lea rcx, bword ptr [rsi+24]
mov rdx, rax
call [CORINFO_HELP_ASSIGN_REF]
- nop
G_M17833_IG09:
- add rsp, 40
- pop rbx
- pop rbp
- pop rsi
- pop rdi
- ret
-G_M17833_IG10:
- mov rbp, rdi
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- cmp gword ptr [rax+856], rbp
- jne SHORT G_M17833_IG12
- mov rcx, rbx
- call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
- mov rdi, rax
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- mov rdx, gword ptr [rax+1840H]
- mov rcx, rdi
- call [CORINFO_HELP_CHKCASTANY]
- lea rcx, bword ptr [rsi+24]
- mov rdx, rax
- call [CORINFO_HELP_ASSIGN_REF]
nop
-G_M17833_IG11:
- add rsp, 40
- pop rbx
- pop rbp
- pop rsi
- pop rdi
- ret
-G_M17833_IG12:
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- cmp gword ptr [rax+864], rdi
- jne SHORT G_M17833_IG13
- mov rcx, rbx
- call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
- mov rdi, rax
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- mov rdx, gword ptr [rax+1848H]
- mov rcx, rdi
- call [CORINFO_HELP_CHKCASTANY]
- lea rcx, bword ptr [rsi+24]
- mov rdx, rax
- call [CORINFO_HELP_ASSIGN_REF]
-G_M17833_IG13:
- nop
-G_M17833_IG14:
+G_M17833_IG10:
add rsp, 40
- pop rbx
- pop rbp
pop rsi
pop rdi
ret |
Lulz. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. @jkotas - thoughts on the call to Unsafe.As<IEqualityComparer<string>>(object)
? I would think this generic instantiation would already exist in every real-world .NET application, but are there situations where we need to avoid it? And if so, would it make sense to make the new GetStringComparer
helper method take IEqualityComparer
instead of IEqualityComparer<string>
?
I wouldn't want to contort the code if this is unnecessary.
The non Unsafe version _comparer = (IEqualityComparer<T>?)NonRandomizedStringEqualityComparer
.GetStringComparer((IEqualityComparer<string>?)_comparer); vs the Unsafe _comparer = Unsafe.As<IEqualityComparer<TKey>?>(
NonRandomizedStringEqualityComparer.GetStringComparer(
Unsafe.As<IEqualityComparer<string>?>(_comparer))); The regular casting includes a double cast even though its passed the G_M17833_IG07:
mov rcx, qword ptr [rsi]
call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
cmp rax, qword ptr [(reloc)]
jne SHORT G_M17833_IG09
;; bbWeight=1 PerfScore 8.00
G_M17833_IG08:
- mov rcx, qword ptr [rsi]
- call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
- mov rdi, rax
mov rcx, gword ptr [rsi+24]
- call [CORINFO_HELP_READYTORUN_CHKCAST]
- mov rcx, rax
call [NonRandomizedStringEqualityComparer:GetStringComparer(IEqualityComparer`1):IEqualityComparer`1]
- mov rdx, rax
- mov rcx, rdi
- call [CORINFO_HELP_CHKCASTANY]
lea rcx, bword ptr [rsi+24]
mov rdx, rax
call [CORINFO_HELP_ASSIGN_REF]
- ;; bbWeight=0.50 PerfScore 10.38
+ ;; bbWeight=0.50 PerfScore 4.38
G_M17833_IG09:
nop
;; bbWeight=1 PerfScore 0.25 |
Is it there for every shared generic? The JIT should be able to see that the So far we have been able to avoid introducing type-unsafe code in collection types. This would be the first instance of type-unsafe code in Dictionary that I think is very unfortunate. I think we should rather look at fixing the JIT to get the code optimized in similar way instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see what it would take to fix the JIT instead.
Raised issue #49614 |
Looks to be; it is however eliminated for |
So I guess that part of the problem is that the check is not eliminated for |
Looks to be eliminated for |
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs
Outdated
Show resolved
Hide resolved
...System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs
Outdated
Show resolved
Hide resolved
Ok, so this part works as expected. It cannot be eliminated for |
So is only one cast in the .ctor which could in theory be eliminated by the Jit as its behind a type guard confirming
|
G_M17833_IG01:
push rdi
push rsi
- push rbp
- push rbx
sub rsp, 40
mov qword ptr [rsp+20H], rcx
mov rsi, rcx
mov rdi, r8 ... G_M17833_IG07:
- mov rbx, qword ptr [rsi]
- mov rcx, rbx
+ mov rcx, qword ptr [rsi]
call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
cmp rax, qword ptr [(reloc)]
- jne G_M17833_IG13
+ jne SHORT G_M17833_IG09
G_M17833_IG08:
- mov rdi, gword ptr [rsi+24]
- test rdi, rdi
- jne SHORT G_M17833_IG10
- mov rcx, rbx
- call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
- mov rdi, rax
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- mov rdx, gword ptr [rax+1838H]
- mov rcx, rdi
- call [CORINFO_HELP_CHKCASTANY]
+ mov rcx, gword ptr [rsi+24]
+ call [NonRandomizedStringEqualityComparer:GetStringComparer(Object):IEqualityComparer`1]
+ mov rdi, rax
+ test rdi, rdi
+ je SHORT G_M17833_IG09
+ mov rcx, qword ptr [rsi]
- lea rcx, bword ptr [rsi+24]
- mov rdx, rax
- call [CORINFO_HELP_ASSIGN_REF]
- nop
-G_M17833_IG09:
- add rsp, 40
- pop rbx
- pop rbp
- pop rsi
- pop rdi
- ret
-G_M17833_IG10:
- mov rbp, rdi
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- cmp gword ptr [rax+856], rbp
- jne SHORT G_M17833_IG12
- mov rcx, rbx
- call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
- mov rdi, rax
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- mov rdx, gword ptr [rax+1840H]
- mov rcx, rdi
- call [CORINFO_HELP_CHKCASTANY]
- lea rcx, bword ptr [rsi+24]
- mov rdx, rax
- call [CORINFO_HELP_ASSIGN_REF]
nop
-G_M17833_IG11:
- add rsp, 40
- pop rbx
- pop rbp
- pop rsi
- pop rdi
- ret
-G_M17833_IG12:
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- cmp gword ptr [rax+864], rdi
- jne SHORT G_M17833_IG13
- mov rcx, rbx
call [CORINFO_HELP_READYTORUN_GENERIC_HANDLE]
+ mov rcx, rax
- mov rdi, rax
- call [CORINFO_HELP_READYTORUN_STATIC_BASE]
- mov rdx, gword ptr [rax+1848H]
mov rcx, rdi
call [CORINFO_HELP_CHKCASTANY]
lea rcx, bword ptr [rsi+24]
mov rdx, rax
call [CORINFO_HELP_ASSIGN_REF]
-G_M17833_IG13:
+G_M17833_IG09:
nop
-G_M17833_IG14:
+G_M17833_IG10:
add rsp, 40
- pop rbx
- pop rbp
pop rsi
pop rdi
ret ... -; Total bytes of code 334, prolog size 13, PerfScore 95.78, instruction count 95, allocated bytes for code 334 (MethodHash=4fbeba56) for method Dictionary`2:.ctor(int,IEqualityComparer`1):this
+; Total bytes of code 169, prolog size 11, PerfScore 52.53, instruction count 48, allocated bytes for code 169 (MethodHash=4fbeba56) for method Dictionary`2:.ctor(int,IEqualityComparer`1):this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thank you!
I think this may make constructing Dictionary<string, ...>
a tiny bit slower. I think it is fine price to pay to eliminate the duplication.
Move the
string
comparer switching out of the generic dictionary (it only applies tostring
) so doesn't need to be there for every shared generic.