Skip to content
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

Optimize unboxing in System.Data.Common.ObjectStorage:Set #91945

Closed
wants to merge 3 commits into from

Conversation

xtqqczze
Copy link
Contributor

@xtqqczze xtqqczze commented Sep 12, 2023

Inline value.GetType() instead of using a local, to enable a JIT optimization.

Diffs: https://www.diffchecker.com/QgpKJ9UD/

Top method improvements (bytes):
        -456 (-32.50 % of base) : System.Data.Common.dasm - System.Data.Common.ObjectStorage:Set(int,System.Object):this (FullOpts)

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Sep 12, 2023
@ghost
Copy link

ghost commented Sep 12, 2023

Tagging subscribers to this area: @roji, @ajcvickers
See info in area-owners.md if you want to be subscribed.

Issue Details

Emitting isinst + unbox.any enables a JIT optimization.

Author: xtqqczze
Assignees: -
Labels:

area-System.Data, community-contribution

Milestone: -

{
_values[recordNo] = new Guid((string)value);
}
else if (_dataType == typeof(byte[]))
{
if (valType == typeof(bool))
if (value is bool)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can use a switch expression

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may hit the performance issue logged in #47920. I actually have a branch to improve this, but it's not expandable-proof so I haven't push it.

It's also interesting to see if a following unbox.any changes the codegen of isinst.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of a switch expression, the JIT is not able to eliminate the calls to CORINFO_HELP_UNBOX.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that CORINFO_HELP_UNBOX is ever evolved after #87241.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that CORINFO_HELP_UNBOX is ever evolved after #87241.

The isinst + unbox.any optimization appears in .NET 7.

@EgorBo
Copy link
Member

EgorBo commented Sep 12, 2023

there should be no perf difference between valType == typeof(..) and val is PrimitiveType I believe, at least for JIT.

@xtqqczze
Copy link
Contributor Author

xtqqczze commented Sep 13, 2023

there should be no perf difference between valType == typeof(..) and val is PrimitiveType I believe, at least for JIT.

This is part of the current codegen, from the build MihuBot/runtime-utils#177, showing a call to CORINFO_HELP_UNBOX that could be eliminated.

G_M32004_IG28:
       mov      rsi, 0xD1FFAB1E      ; 'System.UInt64'

       cmp      rax, rsi
       jne      SHORT G_M32004_IG31
       mov      r15, gword ptr [r15+0x40]
       mov      rsi, 0xD1FFAB1E      ; System.UInt64
       cmp      qword ptr [rbx], rsi
       je       SHORT G_M32004_IG30
						;; size=34 bbWeight=0.50 PerfScore 3.88
G_M32004_IG29:
       mov      rsi, rbx
       mov      rdi, 0xD1FFAB1E      ; System.UInt64
       mov      rax, 0xD1FFAB1E      ; code for CORINFO_HELP_UNBOX
       call     [rax]CORINFO_HELP_UNBOX
						;; size=25 bbWeight=0.25 PerfScore 0.94
G_M32004_IG30:
       mov      rdi, qword ptr [rbx+0x08]
       mov      rax, 0xD1FFAB1E      ; code for System.BitConverter:GetBytes(ulong):ubyte[]
       call     [rax]System.BitConverter:GetBytes(ulong):ubyte[]
       mov      rdx, rax
       movsxd   rsi, r14d
       mov      rdi, r15
       call     CORINFO_HELP_ARRADDR_ST
       jmp      G_M32004_IG37
						;; size=35 bbWeight=0.50 PerfScore 4.50

The issue may be that the JIT is unable to prove value is not null.

@xtqqczze
Copy link
Contributor Author

@EgorBo This change is also a little more efficient in terms of IL.

  isinst [System.Runtime]System.Single

vs:

  callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Object::GetType()
  ldtoken [System.Runtime]System.Single
  call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
  call bool [System.Runtime]System.Type::op_Equality(class [System.Runtime]System.Type, class [System.Runtime]System.Type)

@xtqqczze
Copy link
Contributor Author

@MihuBot

@xtqqczze
Copy link
Contributor Author

@MihuBot

@huoyaoyuan
Copy link
Member

I think we would be able to use switch when #47920 is fully resolved.

`System.ArgumentException : Type of value has a mismatch with column typeCouldn't store <System.Int32[]> in Column1 Column.  Expected type is Array.`
@xtqqczze
Copy link
Contributor Author

@MihuBot

if (_nullValue == value)
{
_values[recordNo] = null;
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the aim behind replacing else blocks with many duplicated returns?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To use a single throw at the end of the method whilst keeping the control flow simple. The JIT will create a single exit.

If this is less readable I can revert to else blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MihaZupan What is your view on this?

@xtqqczze xtqqczze changed the title Use isinst in System.Data.Common.ObjectStorage:Set Optimize unboxing in System.Data.Common.ObjectStorage:Set Oct 13, 2023
@xtqqczze
Copy link
Contributor Author

xtqqczze commented Oct 13, 2023

there should be no perf difference between valType == typeof(..) and val is PrimitiveType I believe, at least for JIT.

@EgorBo This is correct, as long as valType isn't stored to a local.

I've refactored to use value.GetType() inline.

@xtqqczze xtqqczze marked this pull request as ready for review October 13, 2023 10:56
@xtqqczze xtqqczze marked this pull request as draft January 21, 2024 19:12
@ghost ghost closed this Feb 20, 2024
@ghost
Copy link

ghost commented Feb 20, 2024

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@github-actions github-actions bot locked and limited conversation to collaborators Mar 22, 2024
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Data community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants