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

API proposal: Array.IsNullOrEmpty #32642

Closed
GrabYourPitchforks opened this issue Feb 21, 2020 · 12 comments
Closed

API proposal: Array.IsNullOrEmpty #32642

GrabYourPitchforks opened this issue Feb 21, 2020 · 12 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime
Milestone

Comments

@GrabYourPitchforks
Copy link
Member

Justification

A helper method along the same lines of string.IsNullOrEmpty. We have several dozen places within System.Private.CoreLib alone which perform array null or empty checks: see GrabYourPitchforks@ef639cd for a prototype of this change and a list of candidate call sites.

Furthermore, we can perform tricks within Array.IsNullOrEmpty similar to what we already do in string.IsNullOrEmpty. These take advantage of patterns the JIT already recognizes in order to optimize the codegen for code later in the method.

For example:

int[] theArray = GetArray();
if (!Array.IsNullOrEmpty(theArray))
{
    int element = theArray[0]; // JIT elides bounds check here
}

This API obviously isn't required for apps to have correct behavior (devs have gone without it for 20+ years). But it is a convenience method that could add succinctness and clarity to call sites, and it could give a minor perf boost to some scenarios.

API Proposal

namespace System
{
    public abstract partial class Array
    {
        public static bool IsNullOrEmpty([NotNullWhen(false)] Array? array) { throw null; }
    }
}

There's no need for this method to be generic on T; taking a standard Array would be sufficient to cover all use cases.

@GrabYourPitchforks GrabYourPitchforks added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime labels Feb 21, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Feb 21, 2020
@stephentoub
Copy link
Member

#16359

@GrabYourPitchforks
Copy link
Member Author

You're dashing my hopes and dreams, man. :)

@stephentoub
Copy link
Member

😄

@AaronRobinsonMSFT
Copy link
Member

Within the link that @stephentoub mention above, there is a comment about ICollection.IsNullOrEmpty(). Perhaps that would have a bit more traction. I am personally convinced by the argument this isn't needed specifically for Array, but would be open to the debate for ICollection.

@GrabYourPitchforks
Copy link
Member Author

GrabYourPitchforks commented Feb 24, 2020

ICollection would definitely be more generalizable. The primary arguments I can think of for a method which takes Array directly are: (a) no virtual dispatch is needed and it keeps the call site very tight, and (b) it allows the JIT to elide accesses to theArray[0] immediately after the check. See for instance GrabYourPitchforks@ef639cd#diff-6e53989dedc88bcbe4187d065ee392f1R31.

(Edit: wrong link above.)

@GrabYourPitchforks
Copy link
Member Author

FWIW I did verify that the JIT does elide the bounds check on the pattern if (!Array.IsNullOrEmpty(someArray)) { /* use someArray[0] */ }. Still not quite sure if it's an important enough optimization for this.

@stephentoub
Copy link
Member

Does it not elide it for:

if (someArray != null && someArray.Length != 0) { /* use someArray[0] */ }

or

if (someArray?.Length != 0) { /* use someArray[0] */ }

?

@GrabYourPitchforks
Copy link
Member Author

Correct, those patterns aren't elided. There's a code comment in string.IsNullOrEmpty explaining it in more detail.

public static bool IsNullOrEmpty([NotNullWhen(false)] string? value)
{
// Using 0u >= (uint)value.Length rather than
// value.Length == 0 as it will elide the bounds check to
// the first char: value[0] if that is performed following the test
// for the same test cost.
// Ternary operator returning true/false prevents redundant asm generation:
// https://github.com/dotnet/runtime/issues/4207
return (value == null || 0u >= (uint)value.Length) ? true : false;
}

@joperezr joperezr removed the untriaged New issue has not been triaged by the area owner label Jul 7, 2020
@joperezr joperezr added this to the Future milestone Jul 7, 2020
@jkotas
Copy link
Member

jkotas commented Jul 17, 2020

Correct, those patterns aren't elided. There's a code comment in string.IsNullOrEmpty explaining it in more detail.

That is a bug to fix in the JIT, not to workaround by adding new APIs.

@GrabYourPitchforks
Copy link
Member Author

Yeah, I only found around 30-ish call sites within System.Private.CoreLib that would be candidates for using this helper method. (See GrabYourPitchforks@ef639cd.) That might not be enough to justify the work. Even string.IsNullOrEmpty has around 5x that many call sites within CoreLib.

@juliusfriedman
Copy link
Contributor

juliusfriedman commented Jul 18, 2020

Justification

A helper method along the same lines of string.IsNullOrEmpty. We have several dozen places within System.Private.CoreLib alone which perform array null or empty checks: see GrabYourPitchforks@ef639cd for a prototype of this change and a list of candidate call sites.

Furthermore, we can perform tricks within Array.IsNullOrEmpty similar to what we already do in string.IsNullOrEmpty. These take advantage of patterns the JIT already recognizes in order to optimize the codegen for code later in the method.

For example:

int[] theArray = GetArray();
if (!Array.IsNullOrEmpty(theArray))
{
    int element = theArray[0]; // JIT elides bounds check here
}

This API obviously isn't required for apps to have correct behavior (devs have gone without it for 20+ years). But it is a convenience method that could add succinctness and clarity to call sites, and it could give a minor perf boost to some scenarios.

API Proposal

namespace System
{
    public abstract partial class Array
    {
        public static bool IsNullOrEmpty([NotNullWhen(false)] Array? array) { throw null; }
    }
}

There's no need for this method to be generic on T; taking a standard Array would be sufficient to cover all use cases.

Having this method not return the length via out is [faux pas] e.g. NOT 聡.

I hate to be a troll but since the JIT can't be fixed so easily why not take the easy road and then have the jit eliminate calls to this method later on... i.e.

You can teach the JIT to see this method and then when the problem is fixed that code can go away because then as you state the asm will be the same.

And btw string needs this also:
#39560

@GrabYourPitchforks
Copy link
Member Author

Previously considered and rejected. Closing.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime
Projects
None yet
Development

No branches or pull requests

7 participants