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

Factors out few GC specific tests #29647

Merged
merged 2 commits into from
May 15, 2018
Merged

Factors out few GC specific tests #29647

merged 2 commits into from
May 15, 2018

Conversation

marek-safar
Copy link
Contributor

No description provided.

@@ -41,6 +41,8 @@ public static partial class PlatformDetection

public static bool IsDomainJoinedMachine => !Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase);

public static bool IsNotGCForVeryLargeObjects => true;
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 definition of the capability that this is controlling?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

@jkotas jkotas May 11, 2018

Choose a reason for hiding this comment

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

On .NET Framework, these tests will pass both with gcAllowVeryLargeObjects=false and gcAllowVeryLargeObjects=true. So it must be something else...

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 wrote like I don't know what blocks coreclr to allocate collections with more than int.MaxValue items, happy to tweak the name to fit that

Copy link
Member

Choose a reason for hiding this comment

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

.NET Framework/Core have documented limits on maximum array sizes. From https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element :

The maximum index in any single dimension is 2,147,483,591 (0x7FFFFFC7) for byte arrays and arrays of single-byte structures, and 2,146,435,071 (0X7FEFFFFF) for other types.

These limits are less than Int32.MaxValue for defense-in-depth security (to reduce changes of integer overflows causing security bugs), and to allow certain types of bounds-check elimination optimizations and loop unrolling (the JIT can assume that array.Length + small_constant won't overflow).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, renamed the property. I didn't know coreclr throws OutOfMemory when indexing into an already allocated array.

Copy link
Member

Choose a reason for hiding this comment

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

I didn't know coreclr throws OutOfMemory when indexing into an already allocated array.

OutOfMemory exception is thrown by new when the array is allocated , not when you index into it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jkotas I have to say this is getting more confusing as the documentation seems to be .NET Framework specific.

var a = new byte[(uint)int.MaxValue + 1]; // This throws System.OverflowException  Array dimensions exceeded supported range

var b = new byte[int.MaxValue - 100];
Array.Resize (ref b, int.MaxValue);   // This throws OutOfMemoryException 

Shouldn't the first one just work, coreclr has LargeObjects enabled by default since dotnet/coreclr#8853 and you said

OutOfMemory exception is thrown by new when the array is allocated , not when you index into it.

The second one is why the tests have OOM expectation but should that throw the exception with Array dimensions exceeded supported range instead?

Copy link
Member

@jkotas jkotas May 11, 2018

Choose a reason for hiding this comment

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

The behavior of new T[len] is:

  • 0 <= len && len <= MAX_LENGTH - Succeeds, assuming there is enough memory
  • MAX_LENGTH < len && len <= Int32.MaxValue - throw OutOfMemoryException.
  • len < 0 || len > Int32.MaxValue - throw OverflowException

On .NET Core, and on .NET Framework with gcAllowVeryLargeObjects=true, MAX_LENGTH is sizeof(T) > 1 ? 0X7FEFFFFF : 0x7FFFFFC7
On .NET Framework with gcAllowVeryLargeObjects=false, MAX_LENGTH is approximately 0x7FFFFFC7 / sizeof(T) .

@@ -73,6 +73,8 @@ private static bool GetIsWindowsSubsystemForLinux()
return false;
}

public static bool IsIntMaxValueArrayIndexNotSupported => true;
Copy link
Member

Choose a reason for hiding this comment

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

It is better to use positive names (XXXSupported instead of XXXNotSupported) for readability.

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 agree but xunit conditionals don't support negating syntax (you have Not everywhere)

Copy link
Member

Choose a reason for hiding this comment

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

ok

Copy link
Member

Choose a reason for hiding this comment

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

Nit, but I believe when we do this we consistently begin with IsNot...

@@ -73,6 +73,8 @@ private static bool GetIsWindowsSubsystemForLinux()
return false;
}

public static bool IsIntMaxValueArrayIndexNotSupported => true;
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to auto-detect the right value for this, similar to how IsNonZeroLowerBoundArraySupported is auto-detected.

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 could add that but what's the coreclr way of checking that for this behaviour? Based on my comment above I could catch System.OverflowException but that seems quite odd to me.

@marek-safar marek-safar force-pushed the oom branch 2 times, most recently from 01a1491 to 73dba83 Compare May 11, 2018 23:26
@jkotas
Copy link
Member

jkotas commented May 12, 2018

@dotnet-bot test NETFX x86 Release Build please

@@ -73,6 +73,20 @@ private static bool GetIsWindowsSubsystemForLinux()
return false;
}

static Lazy<bool> s_largeArrayIsNotSupported = new Lazy<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: I think the corefx convention would be to put { on a new line.

static Lazy<bool> s_largeArrayIsNotSupported = new Lazy<bool>(() => {
try
{
var tmp = new byte[int.MaxValue];
Copy link
Member

Choose a reason for hiding this comment

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

Some codegens may optimize this allocation out. I may be useful to consume the value in a way that is unlikely to be optimized out. E.g. call ToString() on it.

@jkotas
Copy link
Member

jkotas commented May 12, 2018

LGTM otherwise

try
{
var tmp = new byte[int.MaxValue];
return tmp == null;
Copy link
Member

Choose a reason for hiding this comment

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

This should to be a black box operation that the optimizations are not able to eliminate. tmp == null can be optimized out easily.

FWIW, RyuJIT does some of these optimizations today already. For example, if you have:

class Test
{
    static public bool Method()
    {
        Test t = new Test();
        return (t != null);
    }
}

It will get optimized to just return true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would [MethodImpl(MethodImplOptions.NoOptimization)] help?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, NoOptimization is probably the most appropriate solution.

@marek-safar marek-safar force-pushed the oom branch 2 times, most recently from ea55cf0 to b630e9f Compare May 14, 2018 15:16
}
}

public static bool IsNotIntMaxValueArrayIndexSupported => s_largeArrayIsNotSupported.Value;
Copy link
Member

Choose a reason for hiding this comment

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

What you have is fine, but does it need to use lazy? I'm wondering if it can just be:

public static bool IsNotIntMaxValueArrayIndexSupported { get; } = IsLargeArrayIsNotSupported();

or if there's a reason doing that in the static cctor is a bad idea?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could be but allocating 2GB of memory when any of other values are read from the type does not seem to me like a good idea

Copy link
Member

Choose a reason for hiding this comment

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

Ah, this is in the PlatformDetection class; in that case, I agree. I was thinking it was on the test class itself, in which case such a benefit wouldn't exist.

@@ -73,6 +74,24 @@ private static bool GetIsWindowsSubsystemForLinux()
return false;
}

static Lazy<bool> s_largeArrayIsNotSupported = new Lazy<bool>(IsLargeArrayIsNotSupported);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: private

static Lazy<bool> s_largeArrayIsNotSupported = new Lazy<bool>(IsLargeArrayIsNotSupported);

[MethodImpl(MethodImplOptions.NoOptimization)]
static bool IsLargeArrayIsNotSupported()
Copy link
Member

Choose a reason for hiding this comment

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

Nit: private

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Other than my few remaining nits, LGTM.

@stephentoub stephentoub merged commit ae9c360 into dotnet:master May 15, 2018
@karelz karelz added this to the 3.0 milestone Jun 2, 2018
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* Factors out few GC specific tests

* Tweaks to address the review


Commit migrated from dotnet/corefx@ae9c360
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants